数据库事务
数据库引擎
Mysql提供了多种的数据库引擎,以适应不同的应用需求,大概有8种,但是常用的有以下。
- MyISAM
这是MySQL的默认存储引擎,特别适合于读密集型的应用,如Web应用。它支持表级锁定,但不支持事务和外键约束。MyISAM引擎强调快速读取操作,主要用于高负载的select查询。 - InnoDB:这是MySQL中最常用的存储引擎之一,从MySQL 5.5版本开始成为默认的存储引擎。InnoDB支持事务和外键约束,采用行级锁定,适合于高并发的读写操作,如在线事务处理(OLTP)应用。
事务
数据库事务是访问操作数据项的一个数据库操作序列,这些操作要么全部执行成功,要么全部执行失败,是一个不可分割的工作单位。
四个特性(ACID)
- 原子性(Atomicity):事务中的全部操作在数据库中是不可分割的,要么全部完成,要么全部不执行。
- 一致性(Consistency) 几个并行的事务,其执行结果必须与按某一顺序 串行执行的结果相一致。
- 隔离性(Isolation):事务与事务之间互不干扰(同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰)。
- 持久性(Durability): 对于任意已提交事务,系统必须保证该事物对数据库的改变不被丢失,即使数据库出现故障。
事务的ACID特性是由关系数据库(DBMS)来实现的,DBMS采用日志来保存事务的原子性、一致性、持久性。
事务并发带来的问题
数据库在并发环境下会出现脏读、重复读、和幻读问题。
脏读
事务A读取了事务B更新的数据,然后事务B回滚,那么A读取到的就是脏数据。
不可重复读
事务A多次读取同一条数据,事务B在事务A的执行过程中,对数据进行了更新并提交,导致事务A多次读取同一条数据时,结果不一致。
幻读
事务A对某一范围内的一批数据进行更新操作,执行完后,事务B又对这一范围内的数据进行了新增或删除操作,这时候发现还有一条记录没有被更新,这就是幻读。
不可重复读和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或者删除。 解决不可重复读的问题只需要锁住满足条件的行,而幻读需要锁住表。
隔离级别
Q1:事务的隔离级别和事务有什么关系呢?
事务本身之间是隔离的、不受其他事务影响的,但是如果操作了同一数据,则会对数据多次改变,导致数据的不统一问题,这种不统一问题,逻辑上是正常的,但是业务上并不一定正常,所以可以设置事务的隔离级别来解决这种不正常问题。事务的隔离级别解决的问题本质上是不同事务之间对同一数据的影响程度。
1.读未提交(Read uncommitted)
简述:事务读取到了其他事务未提交的数据。
这个级别允许事务读取到其他事务未提交的数据,可能会导致脏读问题。
这是并发最高,一致性最差的隔离级别。
2.读提交(read committed)
简述:事务读取不到其他事务为提交的数据,但是当多次读取同一数据时,可能读取到的结果不一致。
一个事务过程中只能读取到其他事务对数据的提交后修改。即事务的修改阶段加了排它锁,直到事务结束才释放;执行读命令那一刻加了共享锁,读完即释放,以此维护事务修改阶段对其他事务的不可见。
3.可重复读(repeatable read)
一个事务过程中不允许其他事务对数据进行修改。即事务的读取过程中加了共享锁,事务的修改过程加了排它锁,并一直维持锁定状态直到事务结束。
因为事务的读取或修改都需要位置整个阶段的锁定状态,所以避免了脏读和不可重复读现象。但是因为只对现有的记录进行了锁定,并未维持间隙锁、范围锁,导致某些数据记录的插入并未受阻,即存在幻读现象。
4. 序列化(serializable)
是最高的事物隔离级别,同时性能很低,一般很少使用。在该级别下,事物顺序执行,不仅可以避免脏读、不可重复读、还避免了幻读。
Mysql默认的隔离级别为:Repeatable read ,
查看方式:show variables like ‘tx_isolation’;
实现原理
隔离级别的实现原理
Mysql的事务实现逻辑是位于引擎层的,下面以InnoDb为准。
- 读未提交
事务对以后被读取的数据不加锁(正是因为这个导致了脏读)。
事务在更新数据的霎时,必须先对其增加行级共享锁,直到事务结束才释放。 - 读已提交
事务对以后被读取的数据加行级共享锁(当读到时才加锁),一旦读完该行,立刻释放该行级共享锁。(虽然加了共享锁,但是读完立马释放了,所以多次读取可能会读到不一样的结果)
事务在更新某数据的霎时(就是产生更新的霎时),必须先对其加行级排它锁,直到事务完结时才释放。 - 重复读
Innodb引擎通过mvcc(多版本并发控制)机制解决了不可重复读问题,在可重复读隔离级别下,innnodb为每行数据创建一个版本号,事务在读取数据时,会读取到版本号小于或等于当前事务版本号的数据。即使其他事务在读取后修改了数据并且提交,事务仍然能看到修改前的数据。 - 序列化读
事务在读取数据时,必须先对其加表级共享锁,直到事务完结才释放。
事务在更新数据时,必须现对其加表级排它锁,直到事务结束才释放。