概念简介:
数据库是支持多用户访问,因此需要一种机制来保证在多个用户同事读取和更新数据的时候,数据不会被破坏或者失效,在MySQL中,使用锁来保证并发连接的情况下的数据准确性。锁,顾名思义,就是在用户进行操作的时候,将操作对象锁起来,其他的用户想要修改这条记录,首先得拿到这些数据的锁,只有在锁的持有期间,才能对相应的记录进行修改。在MySQL中,最常用的两种存储引擎Innodb和MyIsam分别使用了行锁和表锁。
锁的类型:
InnoDB存储引擎实现了两种标准的行级锁,一种是共享锁也叫S锁,另外一种是排他锁也叫X锁。接下来对这两种锁做下介绍:
共享锁,顾名思义,可以共享的锁,当一个事务A获得了第m行数据的共享锁,事务B可以立即获得第m行的共享锁,二者兼容,互不冲突。
排他锁,从这个命名上面不难看出,排他锁不接受其他事务在他的持有期间重新加锁,同样的,他也不会强行叠加在其他锁上,如果在上述情况中,事务C想要获取第m行数据的排他锁,那么需要等待事务A和事务B释放了第m行的共享锁之后,才能获取这行数据的排他锁。
从另一个维度来区分,行级锁可以被分为记录锁record lock、间隙锁gap lock以及next-key锁,这里我们简单介绍下:
记录锁:即索引的记录锁
他建立在索引记录上,很多时候,扫描一个表,如果没有索引,往往会导致整个表被锁住,简历合适的索引可以防止扫描整个表。
间隙锁:这是施加于索引记录间隙上的锁
next-key锁:记录锁+间隙锁的组合,有时候在开发的过程中,我们会发现在insert操作的时候,会锁定相邻的键,这就是一个next-key锁技术,MySQL用这个技术来避免“幻读”。幻读是并发事务带来的几个问题之一,其他的问题还包含更新丢失、脏读、不可重复读等等,这些问题我们之后会单独分析。
innodb行级锁:
如果我们想要查看当前锁请求的信息,可以使用show engine innodb status的命令来查看:
mysql> show engine innodb status\G
*************************** 1. row ***************************
Type: InnoDB
Name:
Status:
=====================================
2018-12-25 23:45:04 0x924 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 7 seconds
------------
TRANSACTIONS
------------
Trx id counter 8478
Purge done for trx's n:o < 8474 undo n:o < 0 state: running but idle
History list length 9
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 8477, ACTIVE 10 sec
2 lock struct(s), heap size 1080, 6 row lock(s)
MySQL thread id 3, OS thread handle 2340, query id 47 localhost ::1 root startin
show engine innodb status
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2, 3 merges
merged operations:
insert 0, delete mark 0, delete 0
discarded operations:
insert 0, delete mark 0, delete 0
0.00 hash searches/s, 0.00 non-hash searches/s
---
LOG
---
Log sequence number 2707739
Log flushed up to 2707739
Pages flushed up to 2707739
Last checkpoint at 2707730
0 pending log flushes, 0 pending chkp writes
47 log i/o's done, 0.00 log i/o's/second
--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
0 read views open inside InnoDB
Process ID=1344, Main thread ID=2160, state: sleeping
Number of rows inserted 6, updated 0, deleted 0, read 46
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================
1 row in set (0.00 sec)
在innodb1.0开始之后,用户可以在information_schama架构下添加了表innodb_trx、innodb_locks、innodb_lock_waits,通过这三张表,我们可以监控当前事务并分析可能存在的锁问题。
除此之外,如果我们想查看行锁的争夺情况,可以使用下面的方法:
mysql> show status like 'innodb_row_lock%';
+-------------------------------+-------+
| Variable_name | Value |
+-------------------------------+-------+
| Innodb_row_lock_current_waits | 0 |
| Innodb_row_lock_time | 0 |
| Innodb_row_lock_time_avg | 0 |
| Innodb_row_lock_time_max | 0 |
| Innodb_row_lock_waits | 0 |
+-------------------------------+-------+
5 rows in set (0.28 sec)
一般情况下,我们使用行级锁就能满足绝大部分业务场景,行级锁的优点有:
1.在很多线程中访问不同的行时只存在少量锁定冲突;
2.回滚时只有少量的更改;
3.可以长时间锁定单一的行;
需要注意的是,innodb中的锁定技术往往是基于索引来实现的,如果我们的SQL语句中没有利用到索引,那么innodb将会执行一个全表扫描,锁定所有的行。而锁定过多的行往往又会导致问题,就是锁之间的竞争激烈,降低了并发率,所以建立合适的索引是很重要的,innodb需要索引来过滤那些不需要访问的行。
关于锁的知识点还有很多,牵扯到事务、隔离级别、并发事务带来的问题分析、表锁行锁区别、死锁原理与避免方法等,后续将会追加这部分内容。时间原因,今天先写这么多。