3.1 锁的定义
锁是计算机协调多个进程或线程并发访问某一资源的机制
表级锁、页面锁、行级锁;
开销、加锁速度、死锁、粒度、并发性能衡量锁的性能 。除了表级锁都会出现死锁情况。
表级锁 锁定粒度最大 发生锁冲突的概率最高
3.2 常见锁
InnoDB存储引擎既支持行级锁(row-level locking),也支持表级锁,但默认情况下是采用行级锁。
3.2.1 获取InnoDB行锁争用情况
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.00 sec)
如果InnoDB_row_lock_waits和InnoDB_row_lock_time_avg的值比较高,表示锁争用情况比较严重。
3.2.2 InnoDB的行锁模式以及加锁方法
InnoDB实现了一下两种类型的行锁:
共享锁(S):允许一个事务去多一行,阻止其它事务获得相同数据集的排他锁。
排他锁(X): 允许获得排他锁的事务更新数据,阻止其它事务获得相同数据集的共享锁和排他写锁
为了允许行锁和表锁共存InnoDB还有两种内部使用的意向锁 都是表锁
意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。
意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。
请求锁模式与当前锁模式
事务的请求锁模式与当前锁模式兼容时才能执行。
使用共享锁模式下,查询完数据后不要进行更新操作,不然又可能会造成死锁;要更新数据,应该使用排他锁模式。
3.2.3 InnoDB行锁实现方式
InnoDB行锁是通过给索引上的索引项加锁来实现的
只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁
访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的
3.2.4 间隙锁
查询范围大于已有记录,innodb会对不存在的记录加锁,导致后面需要插入的数据阻塞,尽量使用相等条件来访问更新数据。
3.3 死锁
3.3.1 死锁定义
两个事务需要获取同一个锁资源。
InnoDB一般都能自动检测到,并使一个事务释放锁并回退,另一个事务获得锁,继续完成事务
外部锁通过设置锁等待超时参数 innodb_lock_wait_timeout来解决.
SHOW GLOBAL VARIABLES LIKE ‘innodb_lock_wait_timeout’;//查看锁等待超时时间
SET GLOBAL innodb_lock_wait_timeout=50;//设置锁等待超时时间
3.3.2 避免死锁的方法
1 在事务中约定以相同的顺序来访问表;
2 并发处理时每个线程按固定的顺序来处理记录;
3 写操作直接申请排他锁;
4 降低隔离级别 从可重复读降低到读已提交 SELECT…FOR UPDATE加排他锁
5 可以直接做插入操作,然后再捕获主键重异常,或者在遇到主键重错误时,总是执行ROLLBACK释放获得的排他锁
当第1个线程提交后,第2个线程会因主键重出错,但虽然这个线程出错了,却会获得一个排他锁!
这时如果有第3个线程又来申请排他锁,也会出现死锁