本文共 2292 字,大约阅读时间需要 7 分钟。
SQL标准定义了四个隔离级别:
脏读 ——读未提交(read-uncommitted)
脏读,也叫读未提交。它允许在一个事务中,一个连接修改数据后,另一个连接在同一事务中读取修改后的数据,即使这个修改尚未提交。这通常会导致数据不一致的问题,例如第一个连接可能读到第二个连接修改的未提交数据,而第二个连接后来回滚了。
避免脏读 —— 读已提交(read committed)
在读已提交的隔离级别下,任何读操作都只能读到已经提交的事务数据。因此,如果一个连接修改数据后没有提交,另一个连接在读取时不会看到该修改,直到修改被提交。而如果是可重复读或串行化的隔离级别,则需要额外的机制来确保读到的数据一致性。
不可重复读(reapeatable-read)
不可重复读的定义是,在一个事务范围内,两个或多个相同的查询却返回了不同的结果。这种现象通常是由于事务的并发读写导致的。例如,在可重复读隔离级别下,一个事务可能修改数据,而另一个事务在读取时看到修改后的数据,但由于事务的提交和回滚机制,结果可能不一致。
可重复读
可重复读的核心思想是,在一个事务中,多次读取相同的数据,结果应该是一致的,除非数据是由当前事务本身修改过的。在可重复读隔离级别下,数据库会记录当前事务的版本信息,以避免读到未提交的数据,从而保证数据一致性。
幻读
幻读(phantom read)是指在一个事务中,两次执行相同的查询,结果却不同。这种现象主要发生在支持多版本并发控制(MVCC)的数据库系统中。当一个事务读取数据时,可能会读到由其他事务修改的未提交版本,而这些修改在读取时尚未被提交,从而导致结果不一致。
怎么解决幻读? (serializable)
解决幻读的方式有以下几种:
SERIALIZABLE
:在串行化隔离级别下,数据库会对每个事务的操作加锁,确保事务的操作顺序不被打乱,从而避免幻读现象。在MySQL事务中,要么全部执行,要么全部回滚。事务的核心是基于两个文件 redo log 和 undo log
redo log
redo log 是数据库的一部分,记录物理数据变化的日志。它保证了数据的持久性,因为每次修改操作都会写入到redo log中。redo log 一部分存在内存中,一部分存在磁盘文件中,和主流的日志框架一样,日志优先存入内存,再异步持久化到磁盘。
undo log
undo log 是回滚日志,记录数据的逻辑变化。它有两个主要作用:提供回滚操作和多个行版本控制(MVCC:Muti-Version-Concurrency-Control)。当一个事务回滚时,系统会利用undo log来恢复数据到修改前的状态。
多行版本控制(MVCC)
MVCC通过版本链的机制实现行版本控制。在每个数据行中,除了存储实际数据之外,还存储了一个逻辑链表。这个链表包含了数据行的所有版本信息,包括修改的事务ID和上一个版本的指针。每次修改操作都会生成一个新的版本,并将旧版本信息记录到undo log中。
版本链的组成
版本链的每个节点包含以下信息:
读Review
MVCC仅在读已提交和可重复读两个隔离级别下工作。在这两种隔离级别下,数据库在执行普通的SELECT操作时,会生成一个ReadReview。ReadReview的作用是检查当前事务的版本信息,以确定哪些版本的数据是可见的。
ReadReview的核心思想是通过检查trx_ids列表,存储当前活跃的读写事务ID,来判断哪些版本的数据是当前事务可以读取的。这样可以确保在多个事务并发读写的情况下,数据库能够正确地读取最新的数据版本。
快照读与当前读的区别
1. 快照读(普通读):普通读是指没有加锁的读操作。它利用MVCC机制,结合ReadReview,来确保读到的数据是最新的。普通读不会对记录加锁,因此可以支持多个并发读操作。
2. 当前读:当前读需要加锁,确保在读取数据时,其他事务无法修改相同的数据行。当前读会读取到最新的数据版本,并在读取时加锁,防止其他事务的修改操作干扰。
例如,在执行以下SQL语句时,会触发当前读:
SELECT ... FOR UPDATESELECT ... LOCK IN SHARE MODEUPDATE,DELETE,INSERT
转载地址:http://yabfk.baihongyu.com/