Mysql专题八:Mysql 锁

一、读锁与写锁

• 读锁:共享锁、Shared Locks、S锁。
• 写锁:排他锁、Exclusive Locks、X锁。
• select :不加锁
在这里插入图片描述

(一)读操作

对于普通 SELECT 语句,InnoDB 不会加任何锁
select … lock in share mode

将查找到的数据加上一个S锁,允许其他事务继续获取这些记录的S锁,不能获取这些记录的X锁(会阻塞)

使用场景:读出数据后,其他事务不能修改,但是自己也不一定能修改,因为其他事务也可以使用select … lock in share mode 继续加读锁。
select … for update

将查找到的数据加上一个X锁,不允许其他事务获取这些记录的S锁和X锁。
使用场景:读出数据后,其他事务即不能写,也不能加读锁,那么就导致只有自己可以修改数据。

(二)写操作

• DELETE:删除一条数据时,先对记录加X锁,再执行删除操作。
• INSERT:插入一条记录时,会先加隐式锁来保护这条新插入的记录在本事务提交前不被别的事务访问到。
• UPDATE
• 如果被更新的列,修改前后没有导致存储空间变化,那么会先给记录加X锁,再直接对记录进行修改。
• 如果被更新的列,修改前后导致存储空间发生了变化,那么会先给记录加X锁,然后将记录删掉,再Insert一条新记录。

隐式锁:一个事务插入一条记录后,还未提交,这条记录会保存本次事务id,而其他事务如果想来对这个记录加锁时会发现事务id不对应,这时会产生X锁,所以相当于在插入一条记录时,隐式的给这条记录加了一把隐式X锁。

二、行锁与表锁

1、查看锁情况的SQL:

SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX; -- 记录当前运行的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS; -- 记录当前出现的锁
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS; -- 记录锁等待的对应关系

2、INNODB_TRX表字段:
• trx_id:InnoDB存储引擎内部唯一的事物ID
• trx_status:当前事务的状态, RUNNING, LOCK WAIT, ROLLING BACK or COMMITTING.
• trx_status:事务的开始时间
• trx_requested_lock_id:事务等待的锁的ID(如果事务状态不是LOCK WAIT,这个字段是NULL),详细的锁的信息可以连查INNODB_LOCKS表
• trx_wait_started:事务等待的开始时间
• trx_weight:事务的权重,反应一个事务修改和锁定的行数,当发现死锁需要回滚时,权重越小的值被回滚
• trx_mysql_thread_id:MySQL中的进程ID,与show processlist中的ID值相对应
• trx_query:事务运行的SQL语句
• trx_operation_state:事务当操作的类型 如updating or deleting,starting index read等
• trx_tables_in_use:查询用到的表的数量
• trx_tables_locked:查询加行锁的表的数量
• trx_rows_locked:事务锁住的行数(不是准确数字)
• trx_rows_modified:事务插入或者修改的行数

3、INNODB_LOCKS表:
• lock_id:锁ID
• lock_trx_id:拥有锁的事务 ID。可以和 INNODB_TRX 表 JOIN 得到事务的详细信息。
• lock_mode:锁的模式。
• lock_type:锁的类型。RECORD 代表行级锁,TABLE 代表表级锁。
• lock_table:被锁定的或者包含锁定记录的表的名称。
• lock_index:当LOCK_TYPE=’RECORD’ 时,表示索引的名称;否则为 NULL。
• lock_space:当LOCK_TYPE=’RECORD’ 时,表示锁定行的表空间 ID;否则为 NULL。
• lock_page:当 LOCK_TYPE=’RECORD’ 时,表示锁定行的页号;否则为 NULL。
• lock_rec:当 LOCK_TYPE=’RECORD’ 时,表示一堆页面中锁定行的数量,亦即被锁定的记录号;否则为 NULL。
• lock_data:当 LOCK_TYPE=’RECORD’ 时,表示锁定行的主键;否则为NULL。

4、INNODB_LOCK_WAITS表:
• requesting_trx_id:申请锁资源的事务ID
• requesting_lock_id:申请的锁的ID
• blocking_trx_id:租塞的事务ID
• blocking_lock_id:租塞的锁的ID

(一)行锁

• LOCK_REC_NOT_GAP:单个行记录上的锁。
• LOCK_GAP:间隙锁,锁定一个范围,但不包括记录本身。GAP锁的目的,是为了防止同一事务的两次当前读,出现幻读的情况。
• LOCK_ORDINARY:锁定一个范围,并且锁定记录本身。对于行的查询,都是采用该方法,主要目的是解决幻读的问题。

间隙锁(LOCK_GAP、GAP锁)

READ COMMITTED级别下

1、查询使用的主键
session1:

总结:查询使用的是主键时,只需要在主键值对应的那一个条数据加锁即可。

2、查询使用的唯一索引
在这里插入图片描述

总结:查询使用的是唯一索引时,只需要对查询值所对应的唯一索引记录项和对应的聚集索引上的项加锁即可。

3、查询使用的普通索引
在这里插入图片描述
出现幻读:
在这里插入图片描述
总结:查询使用的是普通索引时,会对满足条件的索引记录都加上锁,同时对这些索引记录对应的聚集索引上的项也加锁。

4、查询使用没有用到索引
在这里插入图片描述
总结:查询的时候没有走索引,也只会对满足条件的记录加锁。

REPEATABLE READ级别下

1、查询使用的主键和RC隔离级别一样。
2、查询使用的唯一索引和RC隔离级别一样。
3、查询使用的普通索引
在这里插入图片描述
解决幻读问题:
在这里插入图片描述
总结:REPEATABLE READ级别可以解决幻读,解决的方式就是加了GAP锁

4、查询使用没有用到索引
在这里插入图片描述
总结:查询的时候没有走索引,会对表中所有的记录以及间隙加锁。

(二)表锁

1、表级别的S锁、X锁
在对某个表执行SELECT、INSERT、DELETE、UPDATE语句时,InnoDB存储引擎是不会为这个表添加表级别的S锁或者X锁的。

在对某个表执行ALTER TABLE、DROP TABLE这些DDL语句时,其他事务对这个表执行SELECT、INSERT、DELETE、UPDATE的语句会发生阻塞,或者,某个事务对某个表执行SELECT、INSERT、DELETE、UPDATE语句时,其他事务对这个表执行DDL语句也会发生阻塞。这个过程是通过使用的元数据锁(英文名:Metadata Locks,简称MDL)来实现的,并不是使用的表级别的S锁和X锁。

• LOCK TABLES t1 READ:对表t1加表级别的S锁。
• LOCK TABLES t1 WRITE:对表t1加表级别的X锁。

尽量不用这两种方式去加锁,因为InnoDB的优点就是行锁,所以尽量使用行锁,性能更高

2、IS锁、IX锁

• IS锁:意向共享锁、Intention Shared Lock。当事务准备在某条记录上加S锁时,需要先在表级别加一个IS锁。

• IX锁,意向排他锁、Intention Exclusive Lock。当事务准备在某条记录上加X锁时,需要先在表级别加一个IX锁。

IS、IX锁是表级锁,它们的提出仅仅为了在之后加表级别的S锁和X锁时可以快速判断表中的记录是否被上锁,以避免用遍历的方式来查看表中有没有上锁的记录。

AUTO-INC锁
• 在执行插入语句时就在表级别加一个AUTO-INC锁,然后为每条待插入记录的AUTO_INCREMENT修饰的列分配递增的值,在该语句执行结束后,再把AUTO-INC锁释放掉。这样一个事务在持有AUTO-INC锁的过程中,其他事务的插入语句都要被阻塞,可以保证一个语句中分配的递增值是连续的。
• 采用一个轻量级的锁,在为插入语句生成AUTO_INCREMENT修饰的列的值时获取一下这个轻量级锁,然后生成本次插入语句需要用到的AUTO_INCREMENT列的值之后,就把该轻量级锁释放掉,并不需要等到整个插入语句执行完才释放锁。
系统变量innodb_autoinc_lock_mode:
• innodb_autoinc_lock_mode值为0:采用AUTO-INC锁。
• innodb_autoinc_lock_mode值为2:采用轻量级锁。
• 当innodb_autoinc_lock_mode值为1:当插入记录数不确定是采用AUTO-INC锁,当插入记录数确定时采用轻量级锁。

(三)悲观锁和乐观锁

悲观锁
悲观锁用的就是数据库的行锁,认为数据库会发生并发冲突,直接上来就把数据锁住,其他事务不能修改,直至提交了当前事务。
乐观锁
乐观锁其实是一种思想,认为不会锁定的情况下去更新数据,如果发现不对劲,才不更新(回滚)。在数据库中往往添加一个version字段来实现。

(四)死锁

在这里插入图片描述

1、死锁检测
• 系统变量innodb_deadlock_detect:控制是否打开死锁检测,默认打开。
• 系统变量innodb_lock_wait_timeout:等待锁的超时时间,默认50s。
• 系统变量innodb_print_all_deadlocks:将所有的死锁日志写入到mysql的错误日志中,默认是关闭的。检测到死锁时,InnoDB会在导致死锁的事务中选择一个权重比较小的事务来回滚,这个权重值可能是由该事务影响的行数(增加、删除、修改)决定的。
SHOW ENGINE INNODB STATUS;看看最近死锁的日志

2、避免死锁
• 以固定的顺序访问表和行
• 大事务拆小,大事务更容易产生死锁
• 在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁概率
• 降低隔离级别
• 为表添加合理的索引

以上均为鲁班学院学习资料,欢迎大家报班学习,真心推荐

发布了143 篇原创文章 · 获赞 49 · 访问量 25万+

猜你喜欢

转载自blog.csdn.net/weixin_36586564/article/details/104021293