小白知识:事务+ACID+更新丢失+脏读+幻读+隔离级别+锁
Posted 长安紫薯
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了小白知识:事务+ACID+更新丢失+脏读+幻读+隔离级别+锁相关的知识,希望对你有一定的参考价值。
为什么需要事务?
代码运行中什么情况都会发生,你有没相关代码执行一半死机了,网络断了,程序崩溃了,怎么办?
如果有多个数据操作呢,如果订单,这边下订单,那边扣减库存。订单下完了,宕机了或网络断了,库存扣减无法执行了。
造成数据的不一致。
如果日常直接写的代码,那必然会发生这类事情,那如何怎么解决呢?
数据库 Dababase 最早解决了这个问题,它提供了事务 Transaction 机制。
事务通过一整套要求,并且数据库产品都必须实现这些要求。
有哪些具体要求呢?
就是ACID,A原子性,C一致性,I隔离性,D持久性,我们也称它们为事务的四大特性。
事务相关的概念:四大特性和隔离级别
事务的四大特性:ACID
原子性(Atomic):事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行。通常,与某个事务关联的操作具有共同的目标,并且是相互依赖的。如果系统只执行这些操作的一个子集,则可能会破坏事务的总体目标。原子性消除了系统处理操作子集的可能性。
一致性(Consistency):事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。这种特性称为事务的一致性。假如数据库的状态满足所有的完整性约束,就说该数据库是一致的。
隔离性(Isolation):由并发事务所作的修改必须与任何其它并发事务所作的修改隔离。事务查看数据时数据所处的状态,到底是另一个事务执行之前的状态还是中间某个状态,相互之间存在什么影响,是可以通过隔离级别的设置来控制的。
持久性(Durability):事务结束后,事务处理的结果必须能够得到固化,即写入数据库文件中即使机器宕机数据也不会丢失,它对于系统的影响是永久性的。
程序实现这四大特征,就不怕断电、宕机、程序执行一半异常了。
事务 trasaction
事务自动提交 autocommit
事务提交 commit
事务回滚 rollback
事务并发引起的新问题
有了事务,并不是就高枕无忧了,要是遇上了高并发,又当怎么办呢?
那接着我们先来看常见的五大问题,然后再看事务中怎么解决这些问题。
1、第一类丢失更新(Update Lost)
此种更新丢失是因为回滚的原因,所以也叫回滚丢失。此时两个事务同时更新count,两个事务都读取到100,事务一更新成功并提交,count=100+1=101,事务二出于某种原因更新失败了,然后回滚,事务二就把count还原为它一开始读到的100,此时事务一的更新就这样丢失了。
2、脏读(Dirty Read)
此种异常时因为一个事务读取了另一个事务修改了但是未提交的数据。举个例子,事务一更新了count=101,但是没有提交,事务二此时读取count,值为101而不是100,然后事务一出于某种原因回滚了,然后第二个事务读取的这个值就是噩梦的开始。
3、不可重复读(Not Repeatable Read)
此种异常是一个事务对同一行数据执行了两次或更多次查询,但是却得到了不同的结果,也就是在一个事务里面你不能重复(即多次)读取一行数据,如果你这么做了,不能保证每次读取的结果是一样的,有可能一样有可能不一样。造成这个结果是在两次查询之间有别的事务对该行数据做了更新操作。举个例子,事务一先查询了count,值为100,此时事务二更新了count=101,事务一再次读取count,值就会变成101,两次读取结果不一样。
幻读(Phantom Read)
幻读和不可重复读有点像,只是针对的不是数据的值而是数据的数量。此种异常是一个事务在两次查询的过程中数据的数量不同,让人以为发生幻觉,幻读大概就是这么得来的吧。举个例子,事务一查询order表有多少条记录,事务二新增了一条记录,然后事务一查了一下order表有多少记录,发现和第一次不一样,这就是幻读。
第二类丢失更新(Second Update Lost)
此种更新丢失是因为更新被其他事务给覆盖了,也可以叫覆盖丢失。举个例子,两个事务同时更新count,都读取100这个初始值,事务一先更新成功并提交,count=100+1=101,事务二后更新成功并提交,count=100+1=101,由于事务二count还是从100开始增加,事务一的更新就这样丢失了。
隔离级别
我们列举了常见的五种高并发情况下会出现的问题,在事务中就通过事务的隔离级别来解决。
read uncommited 读未提交
read commited 读提交
repeatable read 重复读
serializable 序列化(串行)
隔离级别越低速度越快,风险越大;级别越高速度越慢,风险越小。
大部分数据库 Oracle 、SQLServer 采用 read committed 读提交 隔离级别,mysql采用 repeatable read 重复读隔离级别
隔离级别的控制作用
小结
解决上述数据操作不一致的问题,数据库通过锁机制来解决。按锁对象来分分为行级锁和表级锁;根据并发事务锁定的关系上看:分为共享锁和独占锁。为了更改数据,数据库必须在进行更改的行上施加行独占锁定,一般的insert update delete都会隐式采用必要的行锁定。基于锁机制,数据库面提供了4级事务隔离级别,用户只需要设置事务的隔离级别,就会分析事务的sql语句并自动选择合适的锁。值得注意的是,隔离级别越高,数据库的并发性能越差,即事务的隔离级别与数据库的并发性能成负相关关系。
共享锁和独占锁
独占锁:独占锁也叫排他锁,是指该锁一次只能被一个线程所持有。如果线程T对数据A加上排他锁后,则其他线程不能再对A加任何类型的锁。获得排它锁的线程即能读数据又能修改数据。
共享锁:共享锁是指该锁可被多个线程所持有。如果线程T对数据A加上共享锁后,则其他线程只能对A再加共享锁,不能加排它锁。获得共享锁的线程只能读数据,不能修改数据。
行级锁、表级锁、页级锁
在MySQL数据库中,可以按照锁的粒度把数据库锁分为行级锁(INNODB引擎)、表级锁(MYISAM引擎)和页级锁(BDB引擎 )。
行级锁是Mysql中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,但加锁的开销也最大。行级锁分为共享锁 和 排他锁。
表级锁是MySQL中锁定粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分MySQL引擎支持。最常使用的MYISAM与INNODB都支持表级锁定。
页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。BDB支持页级锁
以上是关于小白知识:事务+ACID+更新丢失+脏读+幻读+隔离级别+锁的主要内容,如果未能解决你的问题,请参考以下文章