一、事务的基本要素
- 原子性(Atomicity):事务开始后的所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,就当所有的操作没发生过一样。这是一个整体,不可分割。
- 一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏。比如A转账给B,A的钱已经扣了,B不可能没收到钱。
- 隔离性(Isolation):同一时间只允许一个事务请求统一数据,不同事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束之前,B无法向这张银行卡转账。
- 持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,且不能回滚。
二、事务的并发问题
- 脏读:事务A读取了事务B更新的数据,但是事务B回滚了,那么事务A则读取到的数据是脏数据。
- 不可重复读:事务A多次读取同一数据,事务B在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。
- 幻读:事务A将数据库中所有的数据从数字改为字母,在修改的过程中,事务B插入了一条数字的数据,导致在事务A修改完后发现有一条没有改过来,如幻觉一样,所以叫幻读。
PS:
1).不可重复读侧重数据的修改,幻读是侧重数据的新增或删除。
2).解决不可重复读的问题只需锁住满足条件的行,解决幻读的问题只需锁表。
三、事务隔离级别
可重复读和幻读
在可重复读中,该sql第一次读取到数据后,就将这些数据加锁(悲观锁),其它事务无法修改这些数据,就可以实现可重复读了。但这种方法却无法锁住insert的数据,所以当事务A先前读取了数据,或者修改了全部数据,事务B还是可以insert数据提交,这时事务A就会发现莫名其妙多了一条之前没有的数据,这就是幻读,不能通过行锁来避免。
数据库默认隔离级别: mysql —repeatable-read,oracle,sql server —read commited
为什么MySQL不和Oracle一样,因为在MySQL5.0之前,binlog只支持statement这种格式,而这种格式会导致MySQL在read-committed级别下主从复制会产生逻辑bug。比如在多事务下,有顺序先后的不同的数据更新操作,记录到binlog的顺序会与之相反,从库同步根据binlog日志的记录顺序依次执行,就会产生不一样的结果,导致主从不一致。
解决方案:
- 现在版本已经有了多种格式的binlog日志,可以改为row格式,此时基于行的复制,好处是记录详细,每次数据更新都会记录原值,所以不会导致主从复制不一致的问题,但是日志文件常比较大。
- 隔离级别设置为repeatable-read,但是该级别会有间隙锁(为解决幻读)。在一个事务范围查找,并请求共享或者排他锁的时候,InnoDB会给符合条件的已有数据记录的索引项加锁,InnoDB对于键值在查询条件范围内但不存在的记录也会加锁,叫做“间隙锁”。在并发时,多个事务同时对一个区间加上了间隙锁,就会导致死锁。
四、对比不可重复读(RC)和可重复读(RR)
项目上一般只有这两种隔离级别,读未提交和串行化一般不用。
why?:
- 读未提交会产生脏读,这是不允许的。
- 串行化,每次读操作都会加锁快照读失效,一般MySQL使用自带的分布式事务功能时才使用该隔离级别。总的来说,强一致性事务,性能不佳。
1)、RR隔离级别下,由于间隙锁的存在,在并发情况下死锁几率大。
2)、RR隔离级别下,条件列未命中索引会锁表,RC只会锁行。
在RC隔离级别下,其先走聚簇索引,扫描全表,然后MySQL会根据过滤条件,将不满足的用unlock_row方法,把不满足条件的行记录释放锁。而RR是走聚簇索引,进行全部扫描,最后会将整个表锁上。
3)、RC隔离级别下,半一致性读(semi-consistent)特性增加了update操作的并发性。
在5.1.15的时候,innodb引入了一个概念叫做“semi-consistent”,减少了更新同一行记录时的冲突,减少锁等待。
所谓半一致性读就是,一个update语句,如果读到一行已经加锁的记录,此时InnoDB返回记录最近提交的版本,由MySQL上层判断此版本是否满足update的where条件。若满足(需要更新),则MySQL会重新发起一次读操作,此时会读取行的最新版本(并加锁)!
总结: 所以,虽然MySQL默认隔离级别为RR,然而由于现如今高并发的现实问题,大部分互联网项目,MySQL采用RC的为主。
五、两个疑问
Q1: 在RC级别下,不可重复读问题需要解决么?
A1: 不用解决,这个问题是可以接受的!毕竟你数据都已经提交了,读出来本身就没有太大问题!Oracle的默认隔离级别就是RC,你们改过Oracle的默认隔离级别么?
Q2: 在RC级别下,主从复制用什么binlog格式?
A2: OK,在该隔离级别下,用的binlog为row格式,是基于行的复制!Innodb的创始人也是建议binlog使用该格式!