MySQL global locks and table-level locks

Preface

When using MySQL in a real enterprise development environment, MySQL is definitely not only used by me, but a team explicitly uses MySQL, or business implicitly uses MySQL, so when multiple users or clients connect and use it , We should consider a question: how to ensure the consistency of concurrent access to data ? In this article, I will talk about MySQL locks, not involving MySQL's transaction isolation level.

Global lock

MySQL's global lock will close all open tables and make all tables in a read-only state. Their commands are:

# 全局锁,简称FTWRL
FLUSH TABLES WITH READ LOCK;

# 解锁命令
UNLOCK TABLES;

Experiment with FTWRL: (All the following experiments are done in MySQL8.0.22)

session1 session2
FLUSH TABLES WITH READ LOCK;
select * from test limit 1;
(return results normally)
select * from test limit 1;
(return results normally)
insert into test(a,b,c) values(6,6,6);
(error report)
insert into test(a,b,c) values(8,8,8);# sql1
(blocking)
UNLOCK TABLES;
insert into test(a,b,c) values(8,8,8);# sql1
(After session1 is unlocked, sql1 executes successfully immediately)

From the above experiment, it can be concluded that when FTWRL is executed, all tables become read-only, and other update operations will be blocked.

The main function of the global lock is to make a logical backup of the entire database, that is, to select each table in the database and save it as a text.

During the backup process, the entire database is in a read-only state, and the risk is extremely high. If it is backed up in the main library, it will cause all the business tables to be unable to modify the data; if it is backed up in the slave library, the slave library cannot execute the binlog passed from the main library at this time, which will cause the master-slave delay.

Fortunately, the InnoDB storage engine supports transactions. mysqldump has a single-transaction parameter, which can create a consistent snapshot in the transaction and then back up all tables. With this parameter, data can be modified during backup, so it is recommended to use the InnoDB storage engine in normal development.

Table-level lock

There are two types of table-level locks, one is table locks and the other is metadata locks.

Table lock

Table locks are divided into table read locks and table write locks. The commands in MySQL are:

# 表读锁
lock tables test read;

# 表写锁
lock tables test write;

Next, through experiments, see what is the difference between the table read lock and the table write lock.

Table read lock

session1 session2
lock tables test read;
select * from test limit1;
(return results normally)
select * from test limit 1;
(return results normally)
insert into test(a,b,c) values(6,6,6);
(error report)
insert into test(a,b,c) values(8,8,8); # sql1
(blocking)
unlock tables;
insert into test(a,b,c) values(8,8,8); # sql1
(after session1 is unlocked, sql1 is written successfully immediately)

A table read lock is added to the session1 session. At this time, both session1 and session2 can read data normally, but session1 will report an error when writing data, and session2 will be blocked. After session1 is unlocked, session2's write data can be executed successfully.

From this experiment, it can be concluded that after the table read lock is added to the table, this thread and other threads can read data, this thread will report an error when writing data, and other threads will block when writing data.

Table write lock

session1 session2
lock tables test write;
select * from test limi1;
(return results normally)
select * from test limit 1; # sql1
(blocking)
unlock tables;
select * from test limit; # sql1
(After session1 is unlocked, sql1 returns the result immediately)
lock tables test write;
insert into test(a,b,c) values(6,6,6);
(insert successfully)
insert into test(a,b,c) values(8,8,8);# sql 2
(blocking)
unlock tables;
insert into test(a,b,c) values(8,8,8);# sql2
(After session1 is unlocked, sql2 is executed immediately)

From the above experiment, it can be concluded that after the table write lock is added to the table, this thread can perform read and write operations, and the read and write operations of other threads will be blocked.

Metadata Locking (Metadata Locking, referred to as: MDL lock)

In MySQL, the DDL of the database does not belong to the transaction category. If you select a row of data in session1, at this time session2 adds a new column xxx to this table. At this time, there may be bugs such as corrupted transaction characteristics and disordered binlog sequence ( Similar bugs have been published on the MySQL official website, you can find out if you are interested).

为了解决以上的问题,从MySQL5.5.3引入了元数据锁,MDL锁不需要显式使用,MySQL会默认加上,它的作用就是保证数据库读写正确性。以下全部用MDL表示元数据锁。

当你对一张表进行增删查改的时候会默认加上MDL读锁;当你对一张表进行表结构更改的时候会默认加上MDL写锁。

session1 session2 session3 session4
begin;
select * from test lmi1;
(正常返回结果)
select * from test limit 1;
(正常返回结果)
alter table test add d int;
(阻塞)
select * from test limit 1;
(阻塞)

一开始session1会话查询test的时候,获取到了MDL读锁,可以正常查询到数据。然后session2会话查询数据也会获取MDL读锁,不冲突,也可以正常查询到数据返回。

但是到了session3会话的时候,需要获取MDL写锁,这个时候因为session1的MDL读锁没有释放,所以会阻塞。后面session4也需要MDL读锁,但是因为session3被阻塞了,所以session4也会被阻塞。

假如这是一张线上业务表,这种场景将会使后面的任何操作都失效,表现出来就是这张表变得无法写和读。如果客户端配置了MySQL重试机制的话,会在超时的时候重新建立一个session会话重新请求,然后MySQL就会因为线程不停新增而崩溃。

从上面的例子可以知道MDL锁是在语句执行的时候默认加上的,但是语句执行完是不会释放的,只有等整个事务提交了才会释放MDL锁。

所以对于我们开发者来说,在工作中应该尽量避免满查询、尽量保证事务及时提交、避免大事务等,对于DBA来说,也应该尽量避免在业务高峰期执行DDL操作。

总结

  • 全局锁会让所有的表变成只读状态,所有更新操作都会被阻塞
  • 表读锁是本线程和其他线程都可以读,本线程写会报错,其他线程写会阻塞
  • 表写锁是本线程可以读写,其他线程读写都会阻塞
  • 引入MDL锁解决事务和DDL同时执行引发的bug

参考资料

  • 《深入浅出MySQL》第二版:20.3.8 什么时候使用表锁
  • 《MySQL实战45讲》林晓斌

Guess you like

Origin blog.csdn.net/weixin_37686415/article/details/114184773