MySQL事务指的是 在完成某一个操作时,比如扫码付款操作,所涉及到一系列SQL语句的集合。这一系列SQL语句,要么 全部执行成功,要么 "一个都不执行"。
这里的 "一个都不执行" 并非真的是一条SQL语句都没有执行,而是数据库事务发生了"回滚"。比如在扫码付款这一事务中,扫完码准备付款时,已经输完了密码,此时网络断了或者手机关机了,支付操作异常而终止了,付款方和收款方的账户余额都没有发生变化,这就是数据库事务的"回滚"。
1. 事务的特性
MySQL事务具有的特性有:原子性、一致性、持久性、隔离性。
原子性:一个事务整体,要么都执行成功,要么都没有执行(恢复如初) 的这一特性。
一致性:发生事务的前后,数据库的数据不会遭到破坏。比如,A给B转账10元成功后,A与B 的账户余额总和不变。
持久性:事务对数据库的数据进行修改之后,即使重启服务器,数据库中的数据仍是更新后的数据。
隔离性:多个事务并发执行的时候,每个事务的进行 不影响到 其它事务正确执行。
数据库有多个事务并发执行的时候,数据库的执行效率提高了,但获取的数据可能会出现问题,一般会涉及到 脏读,不可重复读 和 幻读 的问题,这也与事务的隔离性相关。
脏读:事务A在写数据的过程中,事务B就去读取事务A的数据,事务B读取完数据后,事务A又对相关数据进行了修改并提交了事务,这就会导致 事务B读到的数据是"脏数据",也就是无效的、过时的数据。
解决 "脏读" 问题,采取的办法就是 "写加锁",事务A在写的过程中,其它事务不能读事务A的数据。
此时,事务的 并发性降低了。隔离性提高了,效率降低了,数据的准确性提高了。
不可重复读:针对"脏读"问题,采取了"写加锁",当事务A写完数据提交之后,事务B可以去读事务A的数据了,但在事务B读取事务A的数据完成后,事务A又对相关数据进行了修改并提交了事务。当事务B再次去读取事务A 的数据时,发现两次读取的同一数据不一样,这就是 "不可重复读" 问题。
"不可重复读" 与 "脏读" 的区别是,脏读是事务A在读取一个还未提交的事务B的数据,而不可重复读 是 两次读取 已提交事务的 相同数据 发生不一致 的情况。
解决 "不可重复读" 的问题,就得进行"读加锁",约定事务A在读取事务B的数据时,事务B不要去更新数据。
此时,事务的 并发性又降低了,隔离性进一步提高,效率又降低了,数据的准确性提高了。
幻读:为了避免 "脏读" 和 "不可重复读" 的问题,对 读和写 都进行了加锁,但这都是只针对操作同一文件时。比如,事务A在读取事务B的文件1时,事务B不可以去修改文件1的内容。但事务B不想闲着,于是事务B就去写文件2。也就是事务A在读 事务B的文件1的同时,事务B在写文件2。当事务A读取完成后,事务B也写完了并提交了事务。当事务A再次读取事务B的数据时,发现事务B多了一个文件2。因此,幻读 就是 事务A两次读取事务B,发现第二次读取事务B时多了一块数据。
解决"幻读"的问题,就得引入"串行化"的方式,保证事务一个一个的进行,同一时刻只能有一个事务进行。此时事务的并发性 最低,隔离性 最高,效率 最低,数据的准确性 最高。
2. 事务的隔离级别
不同的业务场景,对事务的关注点也有一定的差异。在有的情况下,事务的执行效率更重要,而不太在意数据的准确性,比如一些评论区中评论的数量,1000个评论和1001个评论是没有区别的,页面显示的都是999+。而在有的情况下,事务的数据准确性更重要,比如转账付款,金额应该准确无误。因此,根据不同的业务场景,MySQL设置了不同的事务隔离级别。
数据库设置的事务隔离等级有:
(1)read uncommitted(读未提交)
隔离性最低,并发程度最高,数据准确性最低,效率最高。
(2)read committed(读已提交)
引入"写加锁",并发程度 和 效率降低了,事务的隔离性 和 数据准确性提高了。
(3)repeatable read(可重复读)
引入"写加锁"和"读加锁",并发程度,效率进一步降低了,隔离性和数据的准确性又进一步提高了。
(4)serializable(串行化)
让事务一个一个的执行,同一时刻,只有一个事务正在执行。此时,事务的并发程度最低,执行效率最低,隔离性最高,数据的准确性最高。
以上四个事务隔离等级也就对应着事务的"脏读","不可重复读","幻读"问题。