写在前面:此篇写于让自己梳理清楚、表述明白、深刻理解事务的四个特性。希望也能对你有所帮助。
目录
2. 3.Repeatable Read(可重复读):读写操作都加锁
一、什么是事务
1.定义:
事务(Transaction)是指在数据库管理系统中,一系列对数据库的操作,这些操作被视为一个不可分割的工作单元。事务的目的是确保在并发访问数据库时,数据保持一致且可靠。
2.场景:红包转账
520到了,假设你有一个女朋友,你想在zfb转账999元给她。(为什么用zfb而不是vx,这里有没有懂王来懂一下hhh~)
其中包含的步骤:
1. 从你的的账户扣款100元。(-999)
2. 将999元存入你女朋友的账户。(+999)
假设只发生了1,没发生2。这时候你肯定要找zfb投诉的,扣了钱女朋友又没收到。
假设只发生了2,没发生1。你笑了,zfb哭了。zfb绝对不会让这种事情发生。
因此1,2要打包起来,1,2必须的状态一定要一致,要么都执行,要么都不执行。类似于捆绑销售,要么都买,要么都不买。
(用超市里捆绑销售的酸奶比喻事务)
3.数据库怎么达到这样的效果?靠回滚机制
回滚(Rollback):是指在事务处理过程中,如果发生错误或遇到意外情况,系统会撤销事务中已经执行的操作,将数据恢复到事务开始之前的状态。
二、事务的特性(ACID)
1.原子性 (Atomicity):
定义:事务内的所有操作要么全部完成,要么全部不完成。
实现:MySQL通过使用日志(如undo log)来记录事务的中间状态,如果事务失败,可以通过这些日志将数据恢复到事务开始前的状态。
理解:
原子这个词借用了其它学科的概念
原子(atom)指化学反应不可再分的基本微粒,原子在化学反应中不可分割。
把事务想象成一个原子,不可分割。如果任何一步失败,比如在从你的账户扣款之后,但在将款项存入女朋友账户之前出现了问题,整个事务必须回滚,恢复到最初的状态。
Undo Log
在MySQL中,回滚通常通过使用日志机制(如Undo Log)实现:
- Undo Log:在事务执行过程中,MySQL会记录每个修改操作的反向操作到Undo Log中。
- 事务失败:当事务失败时,系统会读取Undo Log,并执行反向操作,将数据恢复到原来的状态。
2.一致性 (Consistency):
定义:一致性确保事务在完成时,数据库从一个一致的状态转换到另一个一致的状态。在事务开始和结束时,数据库的完整性约束没有被破坏。
实现:MySQL通过约束(如外键、唯一性约束)和触发器来维护数据库的一致性。同时,应用程序逻辑也需要确保一致性。
理解:
转账操作完成后,整个zfb系统中的总金额应该保持一致。通俗来说就是账得对的上,从你那-999,女朋友那就得+999。不可以发生你那-999,女朋友那+1999。这个特性确保了zfb账户总金额的一致性。
3.持久性 (Durability):
定义:持久性保证了事务一旦提交,其所做的修改将永久保存到数据库中,即使系统崩溃,提交的事务数据也不会丢失。
实现:MySQL通过将事务日志(如redo log)写入磁盘来确保持久性。一旦事务提交,MySQL会将数据写入到持久存储中,这样即使系统崩溃,重启后也可以通过重做日志恢复数据。
理解:
数据如果存储在内存上,数据会随着程序重启/主机重启等情况消失,就是不持久的。把数据存储在硬盘上,程序重启/主机重启,数据仍然存在,就是持久。意思就是一旦事务提交,mysql就能保证你提交的事务一定能落到硬盘上。
一旦转账操作完成并提交,即使zfb系统发生崩溃,转账的结果(999元从你的账户到你女朋友账户)依然会被保存。系统恢复后,zfb的数据库会通过日志或其他持久化机制确保转账记录不会丢失。
Redo Log
事务执行过程中,每次数据变更同时会被记录到Redo Log中。提交事务时,Redo Log会确保这些变更记录被写入磁盘。即使系统崩溃,Redo Log中的记录也能帮助重做提交的事务,恢复数据一致性。
4.隔离性 (Isolation):
这是最难理解的。面试官考你事务的四个核心特性,其实就在考查隔离性。
定义:隔离性保证了并发事务之间的相互独立。一个事务的执行不能被其他事务的执行所干扰,不同的事务之间是相互隔离的。
实现:MySQL通过不同的隔离级别、和加锁等措施来控制事务的隔离性(后面详细介绍)。默认的隔离级别是可重复读(REPEATABLE READ)。
理解:
假设今天是5.20,在中午的13:14,有1000个人同时给自己女朋友转账999元红包。
这时会同时产生多个事务,如果数据库一个一个处理,处理的速度就会比较慢,所以就是要一起处理。这时候数据就要并发执行事务。
在数据库中,并发执行事务指的是多个事务同时在数据库中运行和执行操作。这种并发性是为了提高系统的吞吐量和性能,因为在多用户环境中,允许多个事务并发执行可以更有效地利用系统资源。
三、数据库中并发执行事务带来的问题【重要】
并发执行事务引发一些特有的问题,主要包括以下几个:
1. 脏读(Dirty Read)
定义:事务A读取了事务B尚未提交的更改。如果事务B回滚(撤销)这些更改,事务A就会读取到无效的数据。
示例:事务A读取到事务B修改后的数据,但事务B之后回滚了这些修改,导致事务A读取到的数据是无效的。
2. 不可重复读(Non-repeatable Read)
定义:指在一个事务内,连续两次读取同一行数据时,第二次读取的数据与第一次不同。这种现象发生在另一个并发事务在两次读取之间对数据进行了更新或删除操作。
示例:事务A第一次读取数据得到值X,事务B修改并提交了该数据,事务A再次读取时得到的值变成了Y。
3. 幻读(Phantom Read)
定义:事务A在一个事务内多次查询同一条件的数据集,结果集的行数或内容可能发生变化,因为在此期间事务B进行了插入、删除或修改操作。
示例:事务A第一次查询得到10条记录,事务B插入了一些新记录或删除了部分记录,事务A再次查询时得到的记录数不同于第一次查询。
四、MySQL的应对机制
1.锁机制:
1.1读锁(共享锁):解决了不可重复读问题。
定义:多个事务可以同时读取同一数据,但如果有事务持有读锁,其他事务不能对该数据进行修改。
也就是说,当一个事务对某一数据项(如行、表)加读锁时,其他事务也可以对该数据加读锁,但不能加写锁。
通俗理解:
我读的时候,别人可以读,但是不能写。这样子的话,别人得等我读完才能写,就要进入等待,就会使“并发程度”进一步降低,效率也随之降低,“隔离性”进一步提高,数据的准确性也进一步提高。
1.2写锁(排他锁):解决脏读问题。
定义:当一个事务对某一数据项(如行、表)加写锁时,其他事务既不能读取该数据,也不能对该数据进行修改,直到当前事务释放写锁为止。
通俗理解:
我写的时候,别人不能读、不能写。这样子的话,别人就不能执行了,需要等到我写完。
相当于降低了“并发能力”,也就会降低了数据库对服务器的处理效率,提高了“隔离性”,也提高数据的准确性。
2.事务隔离级别:
mysql给用户提供了四个隔离级别,可以在mysql配置文件中进行设置。
2.1Read Uncommitted(读未提交)
定义:
一个事务可以读取到其他未提交事务的修改。
性质:
这种隔离级别的并发性最高,但数据隔离性最低。
特性:存在脏读+不可重复读+幻读
1. 脏读:允许读取其他未提交事务的修改。
2. 不可重复读:可能会发生,因为事务可以读取到其他事务的中途修改。
3. 幻读:可能会发生,因为新记录的插入或删除可以影响结果集。
2.2 Read Committed(读已提交)
定义:
一个事务只能读取到其他已提交事务的修改。未提交的修改对其他事务不可见。
性质:
并发程度降低,隔离性提高。
特性:解决脏读+存在不可重复+存在幻读
1. 脏读:不允许,确保读取到的数据都是已提交的。
2. 不可重复读:可能会发生,因为同一事务中的连续读取可能会读取到不同的数据(中间有其他事务的提交修改)。
3. 幻读:可能会发生,因为其他事务的插入或删除会影响查询结果集。
2. 3.Repeatable Read(可重复读):读写操作都加锁
定义:
一个事务在读取某个数据时,会锁定该数据,确保在整个事务过程中读取的数据不会发生变化。其他事务可以读取但不能修改这些数据。
性质:
并发程度又降低,隔离性又提高。
特性:解决脏读、不可重复读+存在幻读
1. 脏读:不允许。
2. 不可重复读:不允许。通过底层复杂的机制,确保在一个事务内多次读取同一行数据时,读取到的始终是事务开始时的数据版本。
3. 幻读:可能会发生,尽管 Repeatable Read 隔离级别可以解决不可重复读的问题,但它不能完全解决幻读现象。这主要是因为幻读涉及到结果集的行数变化,而不仅仅是单个行数据的变化。
2.4.串行化(Serializable):
定义:
在数据库中,串行化是最高级别的事务隔离级别,确保事务按照严格的顺序执行,就像它们是一个接一个地完成的,而不是并发执行的。
这意味着在一个事务运行期间,其他事务不能对其正在使用的数据进行任何修改或插入操作,确保事务之间没有任何交互影响。
性质:
避免所有问题,但性能最差。
特性:解决脏读、不可重复读和幻读
串行化隔离级别通过强制执行事务的顺序性来避免所有类型的并发问题,包括脏读(、不可重复读和幻读等。