|
1 | | -#### 锁 |
2 | | - |
3 | | -[参考:MySQL-锁研究](https://blog.csdn.net/puhaiyang/article/details/72284702) |
4 | | - |
5 | | -[参考:MySQL-锁深入](https://blog.csdn.net/bigtree_3721/article/details/77417518) |
6 | | - |
7 | | - |
8 | | -##### 乐观锁 |
9 | | - |
10 | | -``` |
11 | | -乐观锁的特点先进行业务操作,不到万不得已不去拿锁。即“乐观”的认为拿锁多半是会成功的,因此在进行完业务操作需要实际更新数据的最后一步再去拿一下锁就好 |
12 | | -
|
13 | | -通常实现:给表加版本号,更新的时候查看刚才取出的版本号是否有变化,如果有,数据回滚,没有,提交 |
14 | | -
|
15 | | -``` |
16 | | - |
17 | | - |
18 | | -##### 悲观锁 |
19 | | - |
20 | | -悲观锁的特点是先获取锁,再进行业务操作,即“悲观”的认为获取锁是非常有可能失败的,因此要先确保获取锁成功再进行业务操作 |
21 | | - |
22 | | -*mysql还有个问题是select for update语句执行中所有扫描过的行都会被锁上,这一点很容易造成问题。因此如果在mysql中用悲观锁务必要确定走了索引,而不是全表扫描。* |
23 | | - |
24 | | -###### 共享锁(lock in share mode) |
25 | | - |
26 | | -select操作加,其他select可以访问,update,insert,delete无法访问 |
27 | | - |
28 | | -``` |
29 | | -终端1: |
30 | | -start transaction; #开启事务 |
31 | | -select * from user limit 1 lock in share mode; #加共享锁 |
32 | | -终端2: |
33 | | -start transaction; #开启事务 |
34 | | -select * from user limit 1 lock in share mode; #加共享锁,可以成功 |
35 | | -update user set username="hh" where id = 1; #执行更新同一条数据 |
36 | | -ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction #返回超时错误 |
37 | | -update user set username="hh" where id = 2; #因为终端1中的条件使用了主键索引,没锁全表,终端2可以更新别的行 |
38 | | -
|
39 | | -``` |
40 | | - |
41 | | -###### 排它锁(for update) |
42 | | - |
43 | | -``` |
44 | | -终端1: |
45 | | -start transaction; #开启事务 |
46 | | -select * from user where id = 1 for update; #加排他锁 |
47 | | -终端2: |
48 | | -start transaction; #开启事务 |
49 | | -select * from user where id = 1 for update; #加排他锁,失败 |
50 | | -ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction #返回超时错误 |
51 | | -
|
52 | | -``` |
53 | | - |
54 | | -###### 共享锁和排它锁区别 |
55 | | - |
56 | | -``` |
57 | | -共同点:都属于悲观锁 |
58 | | -区别: |
59 | | -共享锁:事务1加锁后,事务2也可以加锁,事务1执行update操作,由于事务2也加锁了,所以事务1等待,如果事务2也执行update,会出现死锁退出,然后事务1执行成功。 |
60 | | -排它锁:事务1加锁后,事务2可以查询,如果事务2也加锁,则会等待,直到事务1释放锁,因为排它锁同一时刻只能有一个加锁成功 |
61 | | -共享锁:相当于go sync包读写锁 |
62 | | -排它锁:相当于go sync包的互斥锁 |
63 | | -
|
64 | | -``` |
65 | | -> 注意:在不通过索引条件查询的时候,InnoDB确实使用的是表锁,而不是行锁 |
66 | | -
|
67 | | - |
68 | | - |
69 | | - |
70 | | - |
71 | | - |
72 | | - |
73 | | -#### 死锁 |
74 | | - |
75 | | -``` |
76 | | -死锁的发生与否,并不在于事务中有多少条SQL语句,死锁的关键在于:两个(或以上)的Session加锁的顺序不一致。而使用本文上面提到的,分析MySQL每条SQL语句的加锁规则,分析出每条语句的加锁顺序,然后检查多个并发SQL间是否存在以相反的顺序加锁的情况,就可以分析出各种潜在的死锁情况,也可以分析出线上死锁发生的原因 |
77 | | -
|
78 | | -``` |
79 | | - |
80 | | -``` |
81 | | -MyISAM总是一次获得所需的全部锁,要么全部满足,要么等待,因此不会出现死锁。但在InnoDB中,除单个SQL组成的事务外,锁是逐步获得的,这就决定了在InnoDB中发生死锁是可能的 |
82 | | -
|
83 | | -``` |
84 | | - |
85 | 1 | ### 事务 |
86 | 2 |
|
87 | 3 |
|
@@ -202,6 +118,18 @@ set global transaction isolation level read committed; #设置系统当前隔离 |
202 | 118 | |可重复读|否|否|是| |
203 | 119 | |可串行化|否|否|否| |
204 | 120 |
|
| 121 | +快照读(Snapshot Read),这种不加锁的读,是InnoDB高并发的核心原因之一 |
| 122 | + |
| 123 | +### InnoDB如何实现事务的隔离级别 |
| 124 | + |
| 125 | +使用不同的锁策略(Locking Strategy)来实现不同的隔离级别 |
| 126 | + |
| 127 | +1. RU: select语句不加锁 |
| 128 | +2. Serializable: 所有select语句都会被隐式的转化为select ... in share mode |
| 129 | +3. RR: 普通select快照读,锁select /update /delete 根据查询条件情况,会选择记录锁,或者间隙锁/临键锁,以防止读取到幻影记录 |
| 130 | +4. RC: 普通select快照读,锁select /update /delete 会使用记录锁,可能出现不可重复读; |
| 131 | + |
| 132 | + |
205 | 133 |
|
206 | 134 |
|
207 | 135 |
|
|
0 commit comments