那一天,人们终于回想起了被BUG支配的恐惧
Toggle navigation
Home
AboutMe
Links
Archives
Tags
MySQL事务和隔离级别
2017-05-16 19:20:11
574
0
0
weibo-007
# MySQL事务和隔离级别 ## 前言 想象一个转账的场景,A要向B转账200元,首先会检查A的账户够不够200元,然后执行A的账户减去200元,最后执行B的账户增加200元。这几条语句可以表示成 ``` 1. select money from account where name=A; 2. update account set money = money - 200 where name = A; 3. update account set money = money + 200 where name = B; ``` 可以想象一下,如果前2条语句执行成功,第3条语句执行失败,那么这200元就“不翼而飞”。这个时候我们需要引入事务的概念,这三天语句要么都执行成功,只要有一条语句执行失败,则全部回滚。这样才能保证A的钱财不会白白丢失 ## 事务的ACID 事务必须满足ACID特性,这四个特性分别是原子性,一致性,隔离性和持久性 ### 原子性 一个事务必须被视为不可分割的最小工作单元,整个事务中所有操作要么全部提交成功,要么全部失败回滚。对于一个事务来说,不可能只执行其中的一部分,这就是事务的原子性 ### 一致性 事务总是从一个一致性的状态,转移到另一个一致性的状态。在前面的例子中,即使在执行第3条语句的时候系统奔溃了,A也不会白白损失200元,因为事务最终没有提交。所以事务中所做的修改也不会保存到数据库中。 ### 隔离性 通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的。针对上面的例子,试想一下,假如A只有500元,如果SQL执行到第2句,A账户减去了200元。但是,这个时候,另一个进程也需要A转账500元,这个时候就需要考虑这个进程看到的是300元还是500元的问题。这一点比较复杂,会引入隔离级别的概念 ### 持久性 一旦事务提交,则其所做的修改会永久保存到数据库中。此时即使系统奔溃,修改的数据也不会丢失。这一点也比较复杂,会引入持久化的真正含义 ## 隔离级别 在MySQL的事务特性中,隔离一直是比较复杂而且难以理解的话题。MySQL一共给我们提供了4中不同的隔离级别。每一种隔离级别都规定了一个事务中所做的修改,哪些在事务内和事务间是可见,哪些是不可见的。通常,隔离级别越高,支持的并发就越低 ### 未提交读 在未提交读的隔离级别里面,事务的修改,即使没有提交,对其他事务也都是可见的。这种隔离级别的可以用下图表示 ![image](http://note.youdao.com/yws/api/personal/file/WEB800550524c70c9456a15dfb1b4ec73ea?method=download&shareKey=0686eef7d32957568ab7441dc3e49f38) 可以看到,在事务A将money从200变成500的一个事务时间内,有的线程读取到的money是200,有的线程读取到的money是500。事务可以读取未提交的数据,我们称为**脏读**。 我们可以用一个例子说明脏读的问题,假设事务A表示用户A进程账户充值操作,表示用户A向账户A充值300元,使得账户余额变成500元。线程B表示需要每月自动扣除的服务费,扣费逻辑为 ``` 1. 如果用户账户余额大于或等于500,直接扣除 2. 如果用户账户余额少于500,短信提示余额不足 ``` ![image](http://note.youdao.com/yws/api/personal/file/WEBb832a598927817495d6454d28dc6e00e?method=download&shareKey=ebb171b0680779d7a49d67634130f1eb) 可以看到,线程B第一次进行select操作的时候,看到A账户的金额是500,然后线程B进行扣费操作。关键是这段时间内事务A最终落地的操作是回滚,所以出现问题。在线上很少用这种隔离级别,会导致很多问题 ### 提交读 既然未提交读存在问题,所以MySQL提供了提升隔离级别的方式,就是提交读,在这种隔离级别下,一个事务从开始直到提交事务之前,所做的任何修改对其他事务都是不可见对的。这种隔离级可以用下图表示 ![image](http://note.youdao.com/yws/api/personal/file/WEBa88fec8342575901818fb22b66e300b2?method=download&shareKey=4c3d029de7948883fa977e4e944048b5) 可以看到,在整个事务A期间,读取到的money都是200,只要事务A不提交,其他事务读取的结果永远不变。很显然,这种隔离级别消除了**脏读**问题。但是,这种隔离级别还是有问题,会到来**不可重复读**问题。什么是不可重复读,假如我们有一个事务是这样的,这个事务包含2条一样的SQL查询语句。但是再一个事务内,两条语句的执行结果不一致,如下图表示**不可重复读**问题 ![image](http://note.youdao.com/yws/api/personal/file/WEBa13cc5571d466a144a88b1d4ee02c9e1?method=download&shareKey=c0531e4548325a03bdba3c24c33176d3) ### 可重复读 这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行,在这种隔离级别下,一个事务内多次读取同样记录的结果是一致的。这种隔离级别可以用下图展示 ![image](http://note.youdao.com/yws/api/personal/file/WEBfa54658b6959a2a2dbff5c464d697796?method=download&shareKey=f66789555597fc8220d801cd1eb75003) 注意,MySQL的隔离级别是一级比一级高的,也就是说在可重复读隔离级别下面不可能出现**脏读**和**可重复读**。但是,这种隔离级别下面还会出现一个问题,就是**幻读**,幻读可以下面一张图表示 ![image](http://note.youdao.com/yws/api/personal/file/WEBcb5ef119da743630f7fff4fb5d9f5d79?method=download&shareKey=fe021b66ed2e05fdf7ec0218ad32dcea) 事务B表示很冤枉,我明明开始查询了一下id=1的记录为空,然后插入的时候告诉我主键冲突。 ### 可串行化 可串行化是MySQL中最高隔离级别。他通过强制事务串行执行,也就是说每个事务一个排一个组成一个队列执行,不存在并发。线上很少用,除非我们非常需要确保数据的一致性,并且接受没有并发的情况,才考虑该级别。 ![image](http://note.youdao.com/yws/api/personal/file/WEBf418b1375c9835c6e6ed88cda0682a66?method=download&shareKey=c217d17b47611186048ee85671604de6) 这种隔离级别可以避免上面提到的任何一种读问题,但是牺牲的代价非常惨重。 ### 总结 最后通过一张表总结每一种隔离级别存在的问题 |隔离级别|脏读可能性|不可重复读可能性|幻读可能性| |--|--|--|--| |未提交读|yes|yes|yes| |提交读|no|yes|yes| |可重读读|no|no|yes| |可序列化|no|no|no|
Pre:
C语言编写服务端入门
Next:
TCP连接的建立和终止
0
likes
574
Weibo
Wechat
Tencent Weibo
QQ Zone
RenRen
Submit
Sign in
to leave a comment.
No Leanote account?
Sign up now.
0
comments
More...
Table of content
No Leanote account? Sign up now.