事物的ACID

事务是一个不可分割的数据库操作序列,是数据库并发控制的基本单位,事物具有acid四大特征,其中a是基础,c是约束,i是手段,d是目的

  • 原子性automic,事物是执行的最小单位,不可再分,事物中的一系列数据库操作要么都执行,要么不执行,依靠mysql的undo log来保证,undo log保存了事务发生之前的数据的一个版本,可以用于回滚
  • 一致性consistency:事务前后数据的完整性必须保持一致,满足业务逻辑规则,一致性依靠其它三点来保证
  • 隔离性isolation: 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间的数据和操作对是相互独立的,不同的隔离级别互相干扰的程度不同,只有serialable的隔离级别才能真正做到,隔离性由MVCC来保证。
  • 持久性duriable: 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。由redo log来保证,mysql在事物执行在内存层面修改数据时同时会将修改备份到redo log中,这样方便在数据库宕机时也能通过redo log进行数据恢复

常见的四种并发问题

脏写:
事务A写数据成功提交后受到事务B异常回滚的影响导致提交的数据回滚丢失,例如银行转账操作,事务AB同一时间开启,原账户余额100元事物A更改为了200元然后提交,事务A提交成功后事物B执行过程中出现异常,原账户余额回滚到100元,那么转账的100元就莫名丢失了,问题非常严重,任何数据库都会默认避免这种情况

读脏数据:
事务A读到了事务B还没有提交的数据,例如银行转账操作,原账户余额100元事物B更改为了200元(此时还未提交),事物A读取到当前账户为200元,这就是脏数据,我们认为事物A应该读取到100元才对,因为事物B一单回滚那么事物A读取到的就是非法数据

不可重复读:
事物A多次读数据的过程中事物B修改数据导致事物A两次读取的结果不一致,这样事物A多次读取的数据就会不一样,因此称为不可重复读、例如事物A第一次读取的账户余额是100,事物B将账户余额更新后余额变成300并提交事物将修改持久化,事物A再次读取时明显和第一次读取结果是不一致的,这就称为不可重复读

幻读:
事物A读取某行数据时,事物B插入了一些数据,当事物A再次查询时就会发现之前不存在的几行,就好像发生幻觉一样

幻读 vs 和不可重复读
都是在同一个事务中多次读取了其他事务已经提交的事务的数据导致每次读取的数据不一致,所不同的是不可重复读读取的是同一条数据,而幻读针对的是一批数据整体的统计(比如数据的个数)

数据库并发问题场景总结

  • 事务A读- 事务B读:不存在任何问题。
  • 事务A读- 事务B写: 有线程安全问题,可能出现脏读、幻读、不可重复读。
  • 事务A写- 事务B写:有线程安全问题,可能存在更新丢失等。

以MYSQL数据库来分析四种隔离级别

第一种隔离级别:Read uncommitted(读未提交)
如果一个事务已经开始写数据,则另外一个事务不允许同时进行写操作,但允许其他事务读(未提交的也能读到)此行数据,这样就避免了更新丢失,却可能出现脏读,也就是说事务B读取到了事务A未提交的数据,

第二种隔离级别:Read committed(读提交)
读已提交,一个事务只能看到其他并发的已提交事务所作的修改解决了脏读问题,但无法解决可重复读,这是oracle的默认隔离级别

第三种隔离级别:Repeatable read(可重复读取)
可重复读取是指在一个事务内,多次读同一个数据,结果都是一样的,原理是第一次select时mvcc生成view read,后续再次读取读到的都是第从这个view read中取出数据,即便是后续这条数据被修改多次,但读取的时候不会收到影响,解决了不可重复读同时也解决了幻读的问题,这是mysql的默认隔离级别

第四种隔离级别:Serializable(可序化)
提供严格的事务隔离,它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行,如果仅仅通过“行级锁”是无法实现序列化的,必须通过其他机制保证新插入的数据不会被执行查询操作的事务访问到。序列化是最高的事务隔离级别,同时代价也是最高的,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻读,解决一切事物并发问题

总结

mysql ddl 事务 mysql事务acid_mysql


以上四种隔离级别最高的是Serializable级别,最低的是Read uncommitted级别,当然级别越高,执行效率就越低,像Serializeble这样的级别,就是以锁表的方式(类似于Java多线程中的锁)使得其他线程只能在锁外等待,所以平时选用何种隔离级别应该根据实际情况来,在MYSQL数据库中默认的隔离级别是Repeatable read(可重复读)

在MYSQL数据库中,支持上面四种隔离级别,默认的为Repeatable read(可重复读);而在Oracle数据库中,只支持Serializeble(串行化)级别和Read committed(读已提交)这两种级别,其中默认的为Read committed级别


mysql相关操作

记住:设置数据库的隔离级别一定要是在开启事务之前:

#查看当前数据库隔离级别
select @@transaction_isolation; 
#设置数据库全局/当前会话的隔离级别为读未提交/读已提交/可重复读/串行化
SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL
  {
       REPEATABLE READ
     | READ COMMITTED
     | READ UNCOMMITTED
     | SERIALIZABLE
   }
#全局设置(下次链接依然会生效)当前mysql隔离级别是读未提交
例如:SET GLOBAL  TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
#当前会话设置(下次链接依然会失效)当前mysql隔离级别是读已提交
例如:SET GLOBAL  TRANSACTION ISOLATION LEVEL READ COMMITTED;

注意:
1 如果参数是session那么隔离级别的设置只对当前连接有效,对于使用MYSQL命令窗口而言,一个窗口就相当于一个连接,当前窗口设置的隔离级别只对当前窗口中的事务有效,对于JDBC操作数据库来说,一个Connection对象相当与一个连接,而对于Connection对象设置的隔离级别只对该Connection对象有效,与其他连接Connection对象无关
2 如果是GLOBAL ,那么修改就会永久生效