锁是一种与表相关联的机制,用于限制对表中数据的未授权访问。MySQL 允许客户端会话显式获取表锁以与其他会话合作访问表的数据。MySQL 还允许表锁定,以防止它在特定时间段内对同一表进行未经授权的修改。
MySQL 中的会话只能为自己获取或释放表上的锁。因此,一个会话不能为其他会话获取或释放表锁。需要注意的是,我们必须有 TABLE LOCK 和 SELECT 权限才能进行表锁定。
MySQL 中的表锁定主要用于解决并发问题。它将在运行事务时使用,即首先从表(数据库)读取值,然后将其写入表(数据库)。
MySQL为表提供了两种类型的锁,它们是:
READ LOCK:此锁允许用户仅从表中读取数据。
WRITE LOCK:此锁允许用户对表进行读取和写入操作。
需要注意的是,MySQL 中使用的默认存储引擎是 InnoDB。InnoDB 存储引擎不需要手动锁定表,因为 MySQL 自动对 InnoDB 表使用行级锁定。因此,我们可以在同一张表上同时做多个事务来进行读写操作,而无需相互等待。所有其他存储引擎在 MySQL 中使用表锁定。
在了解表锁定概念之前,首先,我们将使用如下语句创建一个名为“ info_table ”的新表:
CREATE TABLE info_table (
Id INT NOT NULL AUTO_INCREMENT,
Name VARCHAR(50) NOT NULL,
Message VARCHAR(80) NOT NULL,
PRIMARY KEY (Id)
);
MySQL LOCK TABLES 语句
以下是允许我们显式获取表锁的语法:
LOCK TABLES table_name [READ | WRITE];
在上面的语法中,我们在LOCK TABLES关键字之后指定了我们想要获取锁的表名。我们可以指定锁定类型,READ 或 WRITE。
我们还可以通过使用带有锁定类型的逗号分隔表名称列表来锁定 MySQL 中的多个表。请参阅以下语法:
LOCK TABLES tab_name1 [READ | WRITE],
tab_name2 [READ | WRITE],...... ;
MySQL UNLOCK TABLES 语句
以下是允许我们为MySQL 中的表释放锁的语法:
mysql> UNLOCK TABLES;
锁类型
让我们详细了解锁类型。
读锁
以下是READ锁的特点:
- 同时,MySQL 允许多个会话获取一个表的 READ 锁。并且所有其他会话都可以在不获取锁的情况下读取该表。
- 如果会话持有表上的 READ 锁,则它们无法对其执行写操作。这是因为 READ 锁只能从表中读取数据。未获得 READ 锁的所有其他会话无法在不释放 READ 锁的情况下将数据写入表中。写操作进入等待状态,直到我们还没有释放 READ 锁。
- 当会话正常或异常终止时,MySQL 会隐式释放对表的所有类型的锁。此功能也与 WRITE 锁相关。
让我们举一个例子来看看在给定场景下 READ 锁是如何在 MySQL 中工作的。我们将首先连接到数据库并使用CONNECTION_ID()函数,该函数在第一个会话中给出当前连接 ID,如下所示:
mysql> SELECT CONNECTION_ID();
请参阅以下输出:
接下来,我们将使用以下语句在info_table 中插入几行:
mysql> INSERT INTO info_table (name, message)
VALUES('Peter', 'Hi'),
('Joseph', 'Hello'),
('Mark', 'Welcome');
现在,使用以下语句验证表中的数据:
mysql> SELECT * FROM info_table;
我们应该看到如下输出:
现在,我们将执行 LOCK TABLE 语句来获取对表的锁:
mysql> LOCK TABLE info_table READ;
之后,我们将尝试向 info_table 中插入一条新记录,如下所示:
mysql> INSERT INTO info_table (name, message)
VALUES ('Suzi', 'Hi');
我们将得到以下输出,其中 MySQL 发出以下消息“Table 'info_table' was locked with a READ lock and can't be updated”。
因此,我们可以看到,一旦获得了对表的 READ 锁,我们就不能在同一个会话中向表中写入数据。
现在,我们将检查 READ 锁如何在不同的会话中工作。首先,我们将连接到数据库并查看连接 id:
接下来,我们将从返回输出的 info_table 中查询数据,如下所示:
然后,将一些行插入到该表中,如下所示:
mysql> INSERT INTO info_table (name, message)
VALUES ('Stephen', 'Hello');
我们应该看到如下输出:
在上面的输出中,我们可以看到来自第二个会话的插入操作处于等待状态。这是由于 READ 锁,该锁已被第一个会话在表上获取并且尚未释放。
我们可以在第一个会话中使用SHOW PROCESSLIST语句查看有关它们的详细信息。请参阅以下输出:
最后,我们需要在第一个会话中使用UNLOCK TABLES语句来释放锁。现在,我们可以在第二个会话中执行 INSERT 操作。
写锁
以下是 WRITE 锁的特点:
- 会话持有表的锁并且可以从表中读取和写入数据。
- 它是唯一通过持有锁访问表的会话。并且在释放 WRITE 锁之前,所有其他会话都无法访问该表的数据。
让我们举一个例子来看看 WRITE 锁是如何在给定场景下的 MySQL 中工作的。在第一个会话中,我们将使用以下语句获取 WRITE 锁:
mysql> LOCK TABLE info_table WRITE;
然后,我们将在 info_table 中插入一条新记录,如下所示:
mysql> INSERT INTO info_table (name, message)
VALUES ('Stephen', 'How R U'
上面的说法奏效了。现在,我们可以使用 SELECT 语句验证输出:
同样,我们将尝试从第二个会话访问(读/写)表:
INSERT INTO info_table (name, message)
VALUES ('George', 'Welcome');
SELECT * FROM info_table;
我们可以看到这些操作都进入了等待状态。使用 SHOW PROCESSLIST 语句查看有关它们的详细信息:
最后,我们将从第一个会话中释放锁。现在,我们可以执行挂起的操作。
读与写锁
- 读锁类似于“共享”锁,因为多个线程可以同时获取它。
- 写锁是一种“排他”锁,因为另一个线程无法读取它。
- 我们不能同时在表上提供读写锁。
- 读锁的优先级低于写锁,可确保尽快进行更新。