欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

MySql锁机制

程序员文章站 2022-05-05 10:00:13
...

锁的分类
按操作分:读锁/写锁
按粒度分:表锁/行锁


表锁

偏向MyISAM存储引擎,开销小,加锁快,无死锁,锁定粒度大,发生冲突概率高,并发度低

读锁

//建表
mysql> create table mylock(
    -> id int not null primary key auto_increment,
    -> name varchar(20))engine myisam;
mysql> create table book(
    -> id int primary key auto_increment,
    -> name varchar(20))engine myisam;
//插入数据
mysql> insert into mylock(name) values('a');
mysql> insert into mylock(name) values('b');
mysql> insert into mylock(name) values('c');
mysql> insert into mylock(name) values('d');
mysql> insert into mylock(name) values('e');

//查看锁的情况
mysql> show open tables;

//给mylock加读锁,book加写锁
mysql> lock table mylock read, book write;

//查看锁情况
+--------------------+------------------------------------------------------+--------+-------------+
| Database           | Table                                                | In_use | Name_locked |
+--------------------+------------------------------------------------------+--------+-------------+
| mylock             | mylock                                               |      1 |           0 |
| mylock             | book                                                 |      1 |           0 |

//解锁
mysql> unlock tables;

//查看锁
+--------------------+------------------------------------------------------+--------+-------------+
| Database           | Table                                                | In_use | Name_locked |
+--------------------+------------------------------------------------------+--------+-------------+
| mylock             | mylock                                               |      0 |           0 |
| mylock             | book                                                 |      0 |           0 |

//给mylock加读锁
mysql> lock table mylock read;
//用加锁的连接尝试读取
mysql> select * from mylock;
+----+------+
| id | name |
+----+------+
|  1 | a2   |
|  2 | b    |
|  3 | c    |
|  4 | d    |
|  5 | e    |
+----+------+
//用加锁的连接尝试修改
mysql> update mylock set name = 'a2' where id = 1;
//报错了
ERROR 1099 (HY000): Table 'mylock' was locked with a READ lock and can't be updated
//用加锁的连接尝试读别的表,同样报错
mysql> select * from book;
ERROR 1100 (HY000): Table 'book' was not locked with LOCK TABLES

//另一条连接
//读mylock
mysql> select * from mylock;
+----+------+
| id | name |
+----+------+
|  1 | a2   |
|  2 | b    |
|  3 | c    |
|  4 | d    |
|  5 | e    |
+----+------+

//更新mylock,此时这条连接被阻塞,直到加锁的那条连接解锁
mysql> update mylock set name = 'a2' where id = 1;

写锁

//加写锁
mysql> lock table mylock write;
//自己读 ok
mysql> select * from mylock;
+----+------+
| id | name |
+----+------+
|  1 | a2   |
|  2 | b    |
|  3 | c    |
|  4 | d    |
|  5 | e    |
+----+------+
//自己写 ok
mysql> update mylock set name = 'a3' where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
//自己读其他表,报错
mysql> select * from book;
ERROR 1100 (HY000): Table 'book' was not locked with LOCK TABLES

//另一条连接
//读取别的表
mysql> select * from book;
+----+------+
| id | name |
+----+------+
|  1 | 1    |
|  2 | 2    |
|  3 | 3    |
|  4 | 4    |
|  5 | 5    |
+----+------+
//读取,阻塞,直到锁释放
mysql> select * from mylock;

MySql锁机制
读锁会阻塞写,写锁会同时阻塞读和写

分析表锁

mysql> show status like "table%";
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Table_locks_immediate      | 318   |
| Table_locks_waited         | 0     |

//Table_locks_immediate表示产生表级锁定的次数
//Table_locks_waited表示出现表级锁定竞争而发生等待的次数

myisam的读写锁调度是写优先,这也是myisam不适合做写为主表的引擎,因为写锁后,其他线程不能做任何操作,大量的更新使查询很难得到锁,从而造成永远阻塞


行锁

偏向innodb存储引擎,开销大,加锁慢,会出现死锁,锁定粒度小,发生锁冲突的概率低,并发度高

//建表
mysql> create table test_innodb_lock(
    -> a int(11),
    -> b varchar(16))engine=innodb;
//建索引
mysql> create index test_innodb_a_ind on test_innodb_lock(a);
mysql> create index test_innodb_lock_b_ind on test_innodb_lock(b);

//下面2条连接,成为A和B

//A,B连接关闭自动提交
mysql> set autocommit = 0;
//A连接进行修改
mysql>  update test_innodb_lock set b = 1111 where a = 3;
//A连接查看数据
+------+----------+
| a    | b        |
+------+----------+
|    3 | 1111     |
//B连接连接查看数据,发现数据并未修改,
+------+----------+
| a    | b        |
+------+----------+
|    3 | a4     |
//A连接提交
mysql> commit;
//B连接还是看不到数据,因为B连接还在事务里,没有提交,保证可重复读
//B连接提交
mysql> commit;
//B连接查看数据,发现已经被修改
+------+----------+
| a    | b        |
+------+----------+
|    3 | 1111     |
//A连接进行修改
mysql>  update test_innodb_lock set b = 520 where a = 3;
//B连接进行修改,被阻塞(注意,是对同一行进行修改,若不是同一行,不会阻塞)
mysql> update test_innodb_lock set b = 521 where a = 3;
//A连接commit后,B成功执行
+------+----------+
| a    | b        |
+------+----------+
|    3 | 521     |

索引失效,行锁变表锁

//A连接进行更新操作,但是由于where后条件是varchar但是没加引号,导致索引失效,造成锁表
mysql> update test_innodb_lock set a = 8888 where b = 22;
//B连接也进行更新,不和A更新同一行,但是也被阻塞了,因为此时是表锁
mysql> update test_innodb_lock set a = 6666 where b = 33;

间隙锁

当我们使用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做间隙(GAP)

InnoDB也会对这个间隙加锁,这就是间隙锁

//查询记录,可以看到,a为5的这一项是没有的
mysql>  select * from test_innodb_lock;

+---+------+
| a | b    |
+---+------+
| 1 | 11   |
| 2 | 22   |
| 3 | 33   |
| 4 | 44   |
| 6 | 66   |
| 7 | 77   |
| 8 | 88   |
+---+------+

//A连接现在进行范围操作,从3到6
mysql> delete from test_innodb_lock where a > 3 and a < 6;
//B连接进行插入操作,插入a为5的一行,发现被阻塞了,说明5虽然没有,但是也被加锁了
mysql> insert into test_innodb_lock values(5,'55');

如何给一行加锁

//在事务中,后面加上for update即可锁住改行直到commit
mysql> select * from test_innodb_lock where a = 1 for update;

查看行锁数据

mysql> show status like 'innodb_row_lock%';

// Innodb_row_lock_waits 为等待锁次数


+-------------------------------+--------+
| Variable_name                 | Value  |
+-------------------------------+--------+
| Innodb_row_lock_current_waits | 0      |
| Innodb_row_lock_time          | 208725 |
| Innodb_row_lock_time_avg      | 34787  |
| Innodb_row_lock_time_max      | 51025  |
| Innodb_row_lock_waits         | 6      |
+-------------------------------+--------+

MySql锁机制

相关标签: mysql lock