Skip to content

Commit bd79707

Browse files
committed
mysql transaction
1 parent 8036632 commit bd79707

File tree

5 files changed

+198
-0
lines changed

5 files changed

+198
-0
lines changed
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
---
2+
title: Mysql事务
3+
category: db
4+
tags: MySQL
5+
---
6+
7+
极客时间"Mysql实战45讲"讲的真是很精彩,看到事务的文章更是感到茅塞顿开,下边写下整理,以备后面思考!
8+
9+
## 隔离性和隔离级别
10+
11+
事务绕不开的ACID:
12+
* Atomicity(原子性)
13+
* Consistency(一致性)
14+
* Isolation(隔离性)
15+
* Durability(持久性)
16+
17+
当数据库上有多个事务同时执行的时候,就可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题,为了解决这些问题,就有了“隔离级别”的概念。
18+
19+
事务标准的隔离级别:
20+
* 读未提交(read uncommited):可以读到其它事务未提交的数据
21+
* 读提交(read committed):事务提交的数据可以读到
22+
* 可重复读(repeatable read):事务执行中读到的数据和启动事务时的数据一致
23+
* 串行化(serializable):对同一行数据采用"读锁"和"写锁"保证在读写冲突的时候后访问的事务必须要等前一个事务执行完成,才能继续执行.
24+
25+
当然隔离级别越高,效率越差!
26+
27+
> 注意:
28+
> 不同数据库的行为是不同的.Oracle数据库的默认隔离级别是"读提交",这一点在Oracle迁移到Mysql的应用需要考虑是否需要设置MySQL的隔离级别设置为"读提交".
29+
> 设置启动参数`transaction-isolation`**READ-COMMITTED**
30+
> `mysql> show variable like 'transactin_isolation'`
31+
32+
<!-- more -->
33+
34+
35+
## 事务隔离级别的实现
36+
37+
更新操作的同时数据库会记录一条回滚操作.记录的最新值可以通过回滚操作得到前面的状态的值.
38+
39+
下图是一个值从 1被顺序改成 2,3,4时,在回滚日志中的记录示意图
40+
41+
![回滚日志示意图]({{ site.img_server }}/db/mysql_undo_log.png)
42+
43+
不同时刻启动的事务会有不同的read-view.同一条记录在系统中可以存在多个版本,即使数据库的多版本并发控制(MVCC).
44+
45+
> 回滚日志什么时间删除呢?
46+
> 答案: 不需要的时候.
47+
>
48+
> 如何判断什么时间是不需要的时候?
49+
> 答案: 系统没有比这个回滚日志更早的read-view的时候.
50+
51+
基于上面的回滚日志的删除时机, 建议尽量不使用长事务.
52+
53+
## 事务的启动方式
54+
55+
1. 显示启动事务.begin 或 start transaction,配套使用commit,rollback可以结束事务.
56+
2. `set autocommit=0`,这个设置会将自动提交关闭,这意味着即使执行一个select语句,这个事务并没有自动提交,这个事务会持续存在,知道主动执行 commit或rollback语句或者断开连接.
57+
58+
> Tips:
59+
>
60+
> 建议总是使用 `set autocommit=1` , 通过显式语句的方式来启动事务.
61+
> 但是有的开发同学会纠结“多一次交互”的问题。对于一个需要频繁使用事务的业务,第二种方式每个事务在开始时都不需要主动执行一次 “begin”,减少了语句的交互次数。如果你也有这个顾虑,建议你使用 commit work and chain 语法。
62+
> 可以在 `information_schema` 库的 `innodb_trx` 这个表中查询长事务,比如下面这个语句,用于查找持续时间超过 60s 的事务。
63+
64+
```sql
65+
select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60
66+
```
67+
68+
## 事务执行的例子
69+
70+
事务A |事务B |事务C
71+
--------|-------|--------
72+
start transaction with consistent snapshot; | |
73+
|start transaction with consistent snapshot;|
74+
| |update t set k=k+1 where id = 1;
75+
|update t set k=k+1 where id = 1;|
76+
|select k from t where id = 1;|
77+
select k from t where id = 1;||
78+
commit; | |
79+
|commit;|
80+
81+
** 例子中没有特别说明都是autocommit=1 **
82+
83+
## 事务的启动时机
84+
begin/start transaction 并不是事务的起点,在之习惯到他们之后的地一个操作的InnoDB表的语句,事务才真正的启动.想要马上启动事务,可以使用`start transaction with consistent snapshot`
85+
86+
> 第一种启动方式,一致性试图实在执行地一个快照读语句是创建的;
87+
> 第二种启动方式,一致性试图是在执行`start transaction with consistent snapshot`时创建的.
88+
89+
所以上面的例子事务A和事务B实在创建就开始了,事务C本身执行update语句就是一个事务,语句完成自动提交.
90+
91+
> 例子查询的执行结果:
92+
> 事务B查到的k的值是3,事务A查到的k的值是1
93+
94+
(如果和自己分析的不一致,请带着问题往下看)
95+
96+
在 MySQL 里,有两个“视图”的概念:
97+
98+
* 一个是 view。它是一个用查询语句定义的虚拟表,在调用的时候执行查询语句并生成结果。创建视图的语法是 create view … ,而它的查询方法与表一样。
99+
* 另一个是 InnoDB 在实现 MVCC 时用到的一致性读视图,即 consistent read view,用于支持 RC(Read Committed,读提交)和 RR(Repeatable Read,可重复读)隔离级别的实现。
100+
101+
## "快照"在MVCC是如何工作的
102+
103+
> 每一事务在启动时都会生成一个唯一的事务id(transaction id),在数据库的事务系统中是按顺序严格递增的
104+
105+
这里的"快照"并不是完全拷贝的整库,而是在每个事务更新数据会生成一个新的数据版本,并且把transaction id 赋值给这个数据版本的事务id,记为row trx_id.同时保留就的数据版本记录.
106+
也就是说,数据表中的一行记录,其实是有多个版本,每个版本有自己的row trx_id.
107+
108+
![数据的多个版本]({{ site.img_server }}/db/row_trx_id.png)
109+
110+
图中的虚线箭头就是undo log(回滚日志),而V1,V2,V3并不是物理存在的而通过当前版本和undo log 计算出来的.
111+
112+
一个事务在启动时就相当于声明了"以我启动的时刻为准,如果一个数据版本是在我启动之前生成的,就认;如果是我启动以后才生成的,我就不认,我必须要找到它的上一个版本"
113+
114+
当然,如果“上一个版本”也不可见,那就得继续往前找。还有,如果是这个事务自己更新的数据,它自己还是要认的。
115+
116+
假设事务A,事务B,事务C的id分别为100,101,102, 并且在这之前最后一个变更该数据版本的事务id为99,并且执行完成之后的值为1.
117+
118+
根据事务的隔离规则我们可能得出的结论是:事务A的查询结果是1, 事务B的查询结果是2, (但是细想一下这不是丢失了事务C的更新结果?不符合逻辑呀)
119+
整个的执行过程:
120+
1. 事务C执行过后k的当前版本的值是2
121+
2. 事务B执行更新操作需要在当前版本的基础上进行更新,重点说明:确实,如果在update之前查询k的值得到的应该一直是1,但是更新数据都是先读后写,而这个读只能读取当前的值,成为"当前读"(current read),因此实在k=2的基础之上进行更新,得到k的值为3
122+
3. 事务B执行查询操作,得到之前在本事务中更新的k的值3
123+
4. 事务A因为一直存在于自己的一致性视图中所以查询到的值是1
124+
125+
## 例子执行的变体
126+
127+
事务A |事务B |事务C
128+
--------|-------|--------
129+
start transaction with consistent snapshot; | |
130+
|start transaction with consistent snapshot;|
131+
| |start transaction with consistent snapshot;
132+
| |update t set k=k+1 where id = 1;
133+
|update t set k=k+1 where id = 1;|
134+
|select k from t where id = 1;|
135+
| |commit;
136+
select k from t where id = 1;||
137+
commit; | |
138+
|commit;|
139+
140+
提示:需要考虑事务在该记录上添加的排它锁(两阶段锁协议).
141+
142+
## 总结
143+
144+
现在回答,事务的可重复读的能力是怎么实现的?
145+
146+
> 可重复读的核心就是一致性读(consistent read);而事务更新数据的时候,只能用当前读。如果当前的记录的行锁被其他事务占用的话,就需要进入锁等待。
147+
148+
读提交的可重复读的主要区别:
149+
150+
* 在可重复读隔离级别下,只需要在事务开始的时候创建一致性视图,之后事务里的其他查询都共用这个一致性视图;
151+
* 在读提交隔离级别下,每一个语句执行前都会重新算出一个新的视图。

assets/img/blog/db/mysql_undo_log.png

98.2 KB
Loading

assets/img/blog/db/row_trx_id.png

175 KB
Loading

npm-debug.log

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
0 info it worked if it ends with ok
2+
1 verbose cli [ '/usr/bin/node', '/usr/bin/npm', 'start' ]
3+
2 info using npm@3.5.2
4+
3 info using node@v8.10.0
5+
4 verbose run-script [ 'prestart', 'start', 'poststart' ]
6+
5 info lifecycle codingted@1.0.0~prestart: codingted@1.0.0
7+
6 silly lifecycle codingted@1.0.0~prestart: no script for prestart, continuing
8+
7 info lifecycle codingted@1.0.0~start: codingted@1.0.0
9+
8 verbose lifecycle codingted@1.0.0~start: unsafe-perm in lifecycle true
10+
9 verbose lifecycle codingted@1.0.0~start: PATH: /usr/share/npm/bin/node-gyp-bin:/home/zl/Github/codingted.github.io/node_modules/.bin:/home/zl/.rvm/gems/ruby-2.6.3/bin:/home/zl/.rvm/gems/ruby-2.6.3@global/bin:/home/zl/.rvm/rubies/ruby-2.6.3/bin:/home/zl/.rvm/bin:/home/zl/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/zl/Tools/JDK/jdk1.8.0_221/bin:/home/zl/Tools/JDK/jdk1.8.0_221/bin
11+
10 verbose lifecycle codingted@1.0.0~start: CWD: /home/zl/Github/codingted.github.io
12+
11 silly lifecycle codingted@1.0.0~start: Args: [ '-c',
13+
11 silly lifecycle 'bundle exec jekyll serve -w --drafts --host 0.0.0.0 --port 80 ' ]
14+
12 silly lifecycle codingted@1.0.0~start: Returned: code: 1 signal: null
15+
13 info lifecycle codingted@1.0.0~start: Failed to exec start script
16+
14 verbose stack Error: codingted@1.0.0 start: `bundle exec jekyll serve -w --drafts --host 0.0.0.0 --port 80 `
17+
14 verbose stack Exit status 1
18+
14 verbose stack at EventEmitter.<anonymous> (/usr/share/npm/lib/utils/lifecycle.js:232:16)
19+
14 verbose stack at emitTwo (events.js:126:13)
20+
14 verbose stack at EventEmitter.emit (events.js:214:7)
21+
14 verbose stack at ChildProcess.<anonymous> (/usr/share/npm/lib/utils/spawn.js:24:14)
22+
14 verbose stack at emitTwo (events.js:126:13)
23+
14 verbose stack at ChildProcess.emit (events.js:214:7)
24+
14 verbose stack at maybeClose (internal/child_process.js:925:16)
25+
14 verbose stack at Process.ChildProcess._handle.onexit (internal/child_process.js:209:5)
26+
15 verbose pkgid codingted@1.0.0
27+
16 verbose cwd /home/zl/Github/codingted.github.io
28+
17 error Linux 5.0.0-25-generic
29+
18 error argv "/usr/bin/node" "/usr/bin/npm" "start"
30+
19 error node v8.10.0
31+
20 error npm v3.5.2
32+
21 error code ELIFECYCLE
33+
22 error codingted@1.0.0 start: `bundle exec jekyll serve -w --drafts --host 0.0.0.0 --port 80 `
34+
22 error Exit status 1
35+
23 error Failed at the codingted@1.0.0 start script 'bundle exec jekyll serve -w --drafts --host 0.0.0.0 --port 80 '.
36+
23 error Make sure you have the latest version of node.js and npm installed.
37+
23 error If you do, this is most likely a problem with the codingted package,
38+
23 error not with npm itself.
39+
23 error Tell the author that this fails on your system:
40+
23 error bundle exec jekyll serve -w --drafts --host 0.0.0.0 --port 80
41+
23 error You can get information on how to open an issue for this project with:
42+
23 error npm bugs codingted
43+
23 error Or if that isn't available, you can get their info via:
44+
23 error npm owner ls codingted
45+
23 error There is likely additional logging output above.
46+
24 verbose exit [ 1, true ]

tools.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,4 @@ chrome
3636
* `xx-net`
3737
* `trash`
3838
* `rvm` ruby版本管理工具[https://rvm.io/](https://rvm.io/)
39+
* `nmcli` 管理无线网络 `nmcli dev wifi connect <myssid> password <mypassword>`

0 commit comments

Comments
 (0)