事务的四大特性
事务是一个操作序列,不可分割的工作单位
A:原子性:事务操作逻辑上是不可分割的操作单元,事务的所有操作要么全部提交成功,要么全部提交失败,进行事务的回滚操作, 事务回滚使用回滚日志实现,反向执行日志中的操作
C 一致性:事务中的执行必须使数据库保持一致性状态,在一致性状态下,所有事务对一个数据读取的结果都是相同的。
I 隔离性,一个事务所在的修改在最终提交前,对其他事务来说是不可见的(并发执行的事务之间不能相互影响)
D 持久性,一旦事务提交成功,对数据的影响是永久的
事务的并发一致性问题
丢失修改:一个事务对数据进行了修改后,在事务提交前,另一个事务对同一个数据进行了修改,覆盖了之前的修改。
脏读:一个事务读去了另一个事务修改、但未提交的数据,造成了两个数据得到的数据不一致
幻读:多次查询中,由于其他事务在数据范围中执行了插入操作,导致每次返回的结果不一样
不可重复读:在同一个事务中,某查询操作在一个时间段中读取某一行数据和之后一个时间读取改行数据,发现数据已经呗修改
数据库的四种隔离级别
读未提交: 在一个事务提交之前,他的执行结果对其他事务也是可见的,会导致脏读,幻读,不可重复度
读已提交: 事务只能读取已经提交的事务的操作,可以避免脏读
可重复读:
可重复度保证了不会不可重读读和脏读,但是并不能保证幻读。幻读对数据库的影响也并不大,
通过行锁定,在读取的数据不允许其他数据修改
序列化
通过表锁定,彻底禁止读取期间其他进程的修改
乐观锁和悲观锁
悲观锁认为数据随时都会被修改,因此每次读数据的时候都会加上锁,防止其他事务读取或修改数据;
应用于数据更新比较频繁的场景
乐观锁:在操作数据的时候不会进行上锁,但是在更新数据的时候会判断同一时刻有没有别的事务在更新这个事务,通过CAS操作来进行更新。
加入版本号或者时间戳
常见的封锁类型
意向锁是InnoDB自动加的,不需要用户来进行噶虐,对于UPDATE,inSERT,delete语句,InnoDB会自动给涉及到的数据添加排它锁
排它锁/X锁,事务对数据加上X锁后,只允许此事务进行修改和读取操作,其实事务不能对改数据加任何锁。
共享锁/S锁:加了共享锁后,事务对数据只能进行读取而不能进行修改,并且其他数据只能加共享锁,不能添加排它锁
意向锁
MVVC
版本控制协议,MVVC在每行记录后面都保存两个隐藏的咧,用来存储创建版本号和删除版本号
数据库的三大范式
1。 每一列都是不可再分的:例如电话,也应该分为家庭电话和个人电话
- 每个非主属性完全依赖与主属性
COPY
编辑点评:隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed,它能够避免脏读取,而且具有较好的并发性能。
一、数据并发带来的各种情况
①脏读:事务A读到事务B尚未提交的数据,并基于这个数据进行后续操作
②不可重复读:事务A读取数据后,被事务B修改或删除,事务A再次读取时前后两次读取的数据不一致
③幻像读:事务A读取数据后,事务B新增了数据,事务A再次读取是前后两次读取的数据不一致
不可重复读和幻想读的区别:
幻象读和不可重复读是两个容易混淆的概念,前者是指读到了其它已经提交事务的新增数据,而后者是指读到了已经提交事务的更改数据(更改或删除),为了避免这两种情况,采取的对策是不同的,防止读取到更改数据,只需要对操作的数据添加行级锁,阻止操作中的数据发生变化,而防止读取到新增数据,则往往需要添加表级锁——将整个表锁定,防止新增数据(Oracle使用多版本数据的方式实现)。
④第一类更新丢失:事务A和事务B同时访问同一个数据,事务B先提交修改,事务A回滚操作。导致事务B的修改丢失
⑤第二类更新丢失:事务A和事务B同时访问同一个数据,事务B先提交修改,事务A再提交。导致事务B的修改被覆盖
第一类更新丢失是很严重的操作,如果控制不当,可能导致在一个长时间的大型事务中,所有的操作都被回滚。所以所有的数据库都不支持这种并发情况。
二、数据库的锁机制
从锁的作用范围来分,可以分为:行级锁和表级锁
从锁的排他性来分,可以分为:共享锁和独占锁(排他锁),其中共享锁允许共享,但阻止独占锁。独占锁不但不允许共享锁,且不允许其它独占锁
于是组合起来就有:
①行共享锁:允许多个会话共享锁定的行数据,但不允许对这些行的独占锁。例如select …for update
②行独占锁:对行进行独占,不允许其它的共享锁(表,行)、独占锁(表,行)和表共享行独占锁。例如insert,update
③表共享锁:允许多个会话共享表数据,但不允许其它的独占锁(表,行)、表共享行独占锁。可以实现表级事务一致性
④表独占锁:对表进行独占,不允许其它的共享锁(表,行)、读占锁(表,行)和表共享行独占锁。达到序列化操作级别
⑤表共享行独占锁:允许多个会话共享表数据,但同一时刻只能有一个行独占锁。可以达到数据共享同时防止脏读、不可重复读、幻像读
表共享锁定可以让会话具有对表事务级一致性访问,因为其它会话在你提交或者回溯该事务并释放对该表的锁定之前不能更改这个被锁定的表(因为要修改表的记录,就必须获得行独占锁,但是共享锁会阻止独占锁的获取,这样原来其它正在读取表记录的事务就不会出现脏读、不可重复读、幻像读的情况了);
表共享行独占锁与其不同则是多了一个行独占,这样效率更高。
三、事务隔离级别
尽管数据库为用户提供了锁的DML操作方式,但直接使用锁管理是非常麻烦的,因此数据库为用户提供了自动锁机制。只要用户指定会话的事务隔离级别,数据库就会分析事务中的SQL语句,然后自动为事务操作的数据资源添加上适合的锁。此外数据库还会维护这些锁,当一个资源上的锁数目太多时,自动进行锁升级以提高系统的运行性能,而这一过程对用户来说完全是透明的
下面是ANSI ISO92定义的4个事务隔离级别以及对应的对数据并发的处理
READ COMMITITED:不允许读取未提交的数据,但可以读取已提交的数据。所以可能出现不可重复读、和幻像读(读的过程依然可以被修改、增加、删除)
REPEATABLE READ:通过行锁定,在读的数据不允许其它进程修改。确保已读取的数据不被修改、删除(不可重复读)但无法阻止其它进程写入新数据,所以不能确保读取到新的数据(幻像读)
SERIALIZABLE:通过表锁定,彻底禁止读取期间其它进程的修改、删除(屏蔽不可重复读)和增加(屏蔽幻像读)
但是不管是那种隔离级别,对第一类丢失更新都是不能接收的
解决方案:为了避免上面出现的几种情况,在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同。
● 未授权读取,也称为读未提交(Read Uncommitted):允许脏读取,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个数据则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。
● 授权读取,也称为读提交(Read Committed):允许不可重复读取,但不允许脏读取。这可以通过“瞬间共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。
● 可重复读取(Repeatable Read):禁止不可重复读取和脏读取,但是有时可能出现幻影数据。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。
● 序列化(Serializable):提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed,它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、虚读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。