当前位置: 首页 > 科技观察

事务、隔离级别、并发一致性问题

时间:2023-03-17 19:44:31 科技观察

数据库事务一直是数据库的核心基础知识。熟悉事务知识是深入研究数据库的前提;同时,数据库事务也是互联网面试中问得最多的知识之一。在本文中,我们将从以下几个角度进行深入分析:事务的四大特点并发数据库环境下的一致性问题数据库隔离级别解决的一致性问题。话不多说,朋友们,上车吧!商业什么是商业?事务是数据库系统中一个非常重要的术语。可以用一行简单的SQL来实现,也可以用一组复杂的SQL来实现。对于MySQL来说,事务的实现方式有两种,一种是显式事务,一种是隐式事务。显式事务需要我们手动使用begin或starttransaction来启动事务,执行完中间SQL语句后使用commit来提交事务,即手动提交。InnoDB默认是一个隐式事务,即自动提交,每行insert、update、deleteSQL语句的操作默认是一个独立的事务。如果想关闭autocommit,可以设置autocommit=0来实现。从本质上讲,事务实际上是一系列逻辑操作。为了保证这一系列的逻辑操作能够准确、统一、安全地执行,就需要通过规范和技术来实现交易,让交易具有特点。交易的特点是什么?1.原子性事务被看作是一个不可分割的最小单元,事务的所有操作要么提交成功,要么失败回滚。当你为一组行为开启事务时,这组行为的所有操作要么同时成功,要么同时失败。不存在行为的一个步骤成功执行而另一个步骤失败的情况。一旦某个行为操作失败,这组行为中所有成功和失败的行为都会被回滚,就好像什么都没发生过一样。2.隔离(Isolation)一个事务所做的修改在最终提交之前对其他事务是不可见的。3.一致性数据库中的数据在事务执行前后保持一致的状态。在一致的状态下,所有事务读取相同的数据并获得相同的结果。不能出现同一个事务前后两次读取某组数据内容不一致的场景。4.持久性(Durability)一旦一个事务被提交,它的修改将永久保存在数据库中。即使系统崩溃,交易执行的结果也不能丢失。对于一个事务,如果要实现它,就必须遵循ACID的上述四个特性,也就是必须满足这四个特性,才能称得上是一个完整的数据库“事务”。那么InnoDB是如何实现这四个特性的呢?不同的特性其实有不同的实现方式:并发一致性什么是数据库的并发一致性?并发一致性问题是指在并发环境下,由于事务的隔离性难以保证,会出现很多并发问题。数据库中存在三种并发和一致性问题,分别是:1.脏读是指数据已经被事务B修改过,但是事务A还没有提交。假设A和B分别读取了一段数据在同时,事务A读取这个数据的值为10,然后修改它的值为20。按道理事务A对这个数据的修改在事务提交之前不会被其他事务改变。事务可以看到,但是因为数据库的隔离性不能保证,此时事务B也读取了这个数据的值,它会直接读取事务A修改后的值20,那么如果事务Aperforms数据回滚完成,不提交。然后事务B最后读到一个过期值20,这种情况称为脏读。2、不可重复读(NnrepeatableRead)是指在一个事务中两次读取的内容是不同的。事务B读取某条数据S,值为10,然后事务A将S的值修改为20,如果B再次读取这条数据,则读取的值变为20,此时读取的结果与读取的结果不同第一次,这是不可重复读。按理说事务B还没有提交,读取的数据应该是其他事务不可见的,换句话说应该是安全的。但是,由于无法满足并发环境下事务的隔离类型,多个事务同时修改同一个数据,就会出现这种并发冲突。3.PhantomRead是指在一个事务中查询某个数据范围内的数据。如果两次查询的结果不同,则称为“幻读”。事务A根据条件查询一定范围内的数据[10,20,30,40.50]。这时B在这个范围内插入符合条件的新数据。A再次读取这个范围内的数据后,发现范围内多出了一条数据60,此时就出现了“幻读”现象。幻读现象的本质也是由于未能保证事务的隔离类型造成的。那么,MySQL是如何解决并发一致性问题的呢?首先要强调的是MySQL不等于InnoDB。InnoDB是MySQL5.5之后默认使用的存储引擎。InnoDB使用MVCC来解决脏读和不可重复读。然而,MVCC并不是唯一能够解决并发一致性问题的措施。MVCC本质上是一种乐观锁,通过比较不同事务的版本号来解决问题。可以使用乐观锁,也可以使用悲观锁。MySQL的其他存储引擎如Myisam甚至不能使用事务,所以一般使用锁来解决并发一致性问题。在这里,关于InnoDB的MVCC和MySQL的各种锁我就不细说了,我们会在后面的文章中讲到。本文主要关注事务本身。隔离级别什么是数据库的隔离级别?指数据库中实现的安全级别。从ACID的实现程度,分为四个隔离级别。隔离级别越高的数据库越安全,可以解决更多的并发和一致性问题。那么数据库有多少个隔离级别呢?1.未提交读(ReadUncommitted)事务中的修改即使没有提交,其他事务也是可见的。在此隔离级别可能会发生脏读、不可重复读和幻读。所以它是最差的隔离级别。2.提交读取(ReadCommitted)一个事务只能读取提交事务所做的修改。也就是说,一个事务所做的修改在提交前对其他事务是不可见的,所以这个隔离级别解决了脏读的问题,也就是当你的数据库实现了提交读的隔离级别后,脏读现象就不会出现了再次发生。3、可重复读(RepeatableRead)保证在同一个事务中多次读取同一个数据的结果是一样的。这是第三个隔离级别,也是InnoDB默认实现的级别。当你的数据库达到committedread的隔离级别后,脏读和不可重复读就不会再发生了。4.可序列化(Serializable)强制事务串行执行,这样多个事务互不干扰,自然不会出现并发一致性问题。这种隔离级别需要通过加锁来实现,因为加锁机制是为了保证同一时间只执行一个事务。因为serializable是串行执行的,所以不会有并发问题。这也是最安全的,第四隔离级别。总结一下:readuncommitted是最差的数据库隔离级别,也就是说你的数据库在有多个事务的时候是非常不安全的;committedreads可以解决脏读问题;可重复读可以解决不可重复读和脏读的问题;序列化可以解决脏读、不可重复读和幻读。为什么InnoDB默认不实现可串行化?数据库提出这四种隔离级别是为了解决不同的并发一致性问题。但是,隔离级别是不是越高越好呢?对于各大编程语言来说,不仅要考虑“安全”,还要考虑“性能”,数据库也是如此。隔离级别越高越安全,但同时性能效率越低。你想一想,当你的数据库是序列化的时候,就意味着没有并发问题。但是此时,你读取的数据是有锁的,一个数据一次只能被一个事务访问,所以剩下的事务拿不到就只能排队了。在实际生产环境中,数据库往往是在并发环境下运行的。业务高峰时,甚至可能同时有几万、几十万笔交易,所以序列化往往不能满足业务。这就需要在“安全”和“性能”之间取得平衡,所以MySQL的InnoDB存储引擎默认实现的隔离级别是“可重复读”,而不是可序列化的。总结事务本质上是一系列逻辑操作,不同的数据库和存储引擎对事务的支持不同。比如Mysql数据库的InnoDB引擎自然是支持事务的,而Myisam引擎是不支持事务的。数据库事务只有满足ACID的四个特性,才能被我们安全地执行。如果某一时刻只有一个事务在运行,那么就不存在并发一致性问题,ACID很容易满足。因为可以满足隔离,所以只要满足原子性,就可以满足一致性。但是在多事务的并发环境中,由于事务的隔离性难以满足,会出现脏读、不可重复读、幻读等并发和一致性问题。为了解决这些并发一致性问题,数据库系统标准化了四种隔离级别:UncommittedRead、CommittedRead、RepeatableRead和Serializable。隔离级别越高,数据库在并发环境下越安全,但性能越低。所以为了平衡安全性和性能,InnoDB默认实现的隔离级别是“可重复读”。那么如何实现可重复读呢?你可以使用锁,也可以使用MVCC。