Skip to content

Commit 096573e

Browse files
committed
mysql
1 parent 980121e commit 096573e

File tree

4 files changed

+146
-85
lines changed

4 files changed

+146
-85
lines changed

02数据存取/1mysql/10锁.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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+
1. 记录锁(Record Locks) : select * from t where id=1 for update; 在记录为1上加锁
55+
2. 间隙锁(Gap Locks): select * from t where id between 8 and 15 for update; 区间加锁
56+
3. 临键锁: 记录锁和间隙锁的组合
57+
58+
###### 共享锁和排它锁区别
59+
60+
```
61+
共同点:都属于悲观锁
62+
区别:
63+
共享锁:事务1加锁后,事务2也可以加锁,事务1执行update操作,由于事务2也加锁了,所以事务1等待,如果事务2也执行update,会出现死锁退出,然后事务1执行成功。
64+
排它锁:事务1加锁后,事务2可以查询,如果事务2也加锁,则会等待,直到事务1释放锁,因为排它锁同一时刻只能有一个加锁成功
65+
共享锁:相当于go sync包读写锁
66+
排它锁:相当于go sync包的互斥锁
67+
68+
```
69+
> 注意:在不通过索引条件查询的时候,InnoDB确实使用的是表锁,而不是行锁
70+
71+
72+
73+
74+
75+
76+
77+
#### 死锁
78+
79+
```
80+
死锁的发生与否,并不在于事务中有多少条SQL语句,死锁的关键在于:两个(或以上)的Session加锁的顺序不一致。而使用本文上面提到的,分析MySQL每条SQL语句的加锁规则,分析出每条语句的加锁顺序,然后检查多个并发SQL间是否存在以相反的顺序加锁的情况,就可以分析出各种潜在的死锁情况,也可以分析出线上死锁发生的原因
81+
82+
```
83+
84+
```
85+
MyISAM总是一次获得所需的全部锁,要么全部满足,要么等待,因此不会出现死锁。但在InnoDB中,除单个SQL组成的事务外,锁是逐步获得的,这就决定了在InnoDB中发生死锁是可能的
86+
87+
```
88+

02数据存取/1mysql/1事务隔离级别.md

Lines changed: 12 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,3 @@
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-
851
### 事务
862

873

@@ -202,6 +118,18 @@ set global transaction isolation level read committed; #设置系统当前隔离
202118
|可重复读||||
203119
|可串行化||||
204120

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+
205133

206134

207135

02数据存取/1mysql/4日志.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
## 日志
22

33
[参考](https://blog.csdn.net/kobejayandy/article/details/50885693)
4+
[参考2](https://mp.weixin.qq.com/s/R3yuitWpHHGWxsUcE0qIRQ)
45

56
### 为什么要有缓存
67

@@ -11,10 +12,15 @@
1112
### undo redo 检查点(checkpoint)
1213

1314
undo: 记录事务开始前的状态,用于事务失败回滚
14-
redo: 记录事务执行后的状态,用来恢复未写入data file的已成功事务更新的数据
15+
redo: 记录事务执行后的状态,用来恢复未写入data file的已成功事务更新的数据,**随机写优化为顺序写,思路更重要**
1516
checkpoint: 为了定期将db buffer 的内容刷新到data file,当内存不足,db buffer已满的情况,需要将db buffer的内容转存到data file中,
1617
会记录checkpoint发生的时刻,在故障回复时候,只需要redo/undo最近的一次checkpoint之后的操作
1718

19+
#### 一句话说明
20+
21+
redo日志用于保障,已提交事务的ACID特性
22+
undo日志用于保障,未提交事务不会对数据库的ACID特性产生影响,事务提交,回滚段里的undo日志可以删除
23+
1824
### 事务执行的阶段
1925

2026
1. 写undo日志到log buffer
@@ -26,3 +32,12 @@ checkpoint: 为了定期将db buffer 的内容刷新到data file,当内存不足
2632

2733
如果在log file中某个事务没有事务开始(start T)标记,那么需要对这个事务做undo操作,如果有事务结束标志(end commit T),做redo操作
2834

35+
### 总结
36+
37+
1. 常见并发控制保证数据一致性的方法有锁,数据多版本;
38+
2. 普通锁串行,读写锁读读并行,数据多版本读写并行;
39+
3. redo日志保证已提交事务的ACID特性,设计思路是,通过顺序写替代随机写,提高并发;
40+
4. undo日志用来回滚未提交的事务,它存储在回滚段里;
41+
5. InnoDB是基于MVCC的存储引擎,它利用了存储在回滚段里的undo日志,即数据的旧版本,提高并发;
42+
6. InnoDB之所以并发高,快照读不加锁;
43+
7. InnoDB所有普通select都是快照读;

02数据存取/1mysql/9索引.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
## 索引
2+
3+
[介绍](https://mp.weixin.qq.com/s/YMbRJwyjutGMD1KpI_fS0A)
4+
5+
### 聚集索引
6+
7+
叶子节点存储行记录(row),所以,InnoDB索引和记录是存储在一起的,而MyISAM的索引和记录是分开存储的
8+
9+
InnoDB的每一个表都会有聚集索引:
10+
1. 如果表定义了PK,则PK就是聚集索引;
11+
2. 如果表没有定义PK,则第一个非空unique列是聚集索引;
12+
3. 否则,InnoDB会创建一个隐藏的row-id作为聚集索引;
13+
索引的结构是B+树
14+
15+
### 普通索引
16+
叶子节点存储了PK的值,所以,InnoDB的普通索引,实际上会扫描两遍,第一遍,由普通索引找到PK;第二遍,由PK找到行记录;
17+
18+
### 什么是局部性原理?
19+
20+
1. 内存读写快,磁盘读写慢,而且慢很多
21+
2. 磁盘预读:磁盘读写并不是按需读取,而是按页预读,一次会读一页的数据(4k),每次加载更多的数据,如果未来要读取的数据就在这一页中,可以避免未来的磁盘IO,提高效率
22+
3. 局部性原理:软件设计要尽量遵循“数据读取集中”与“使用到一个数据,大概率会使用其附近的数据”,这样磁盘预读能充分提高磁盘IO
23+
24+
### 数据库索引为什么使用B+树? 为什么不是Hash,B树
25+
26+
1. Hash(O(1))比树(O(log(n))),增删改查都快,但是不适合SQL语句,(InnoDB并不支持哈希索引)
27+
2. 二叉搜索树: 1)当数据量大的时候,树的高度会比较高,数据量大的时候,查询会比较慢,2)每个节点只存储一个记录,可能导致一次查询有很多次磁盘IO
28+
3. B树(m叉搜索): 叶子节点,非叶子节点,都存储数据;中序遍历,获取所有节点
29+
4. B+树: 1)非叶子节点不再存储数据,数据只存储在同一层的叶子节点上 2)**叶子之间,增加了链表**,获取所有节点,不再需要中序遍历 3)适合范围查找
30+

0 commit comments

Comments
 (0)