搜索
首页数据库mysql教程MySQL InnoDB四个事务级别与脏读、不重复读、幻读是什么

1、MySQL InnoDB事务隔离级别脏读、可重复读、幻读

MySQL InnoDB事务的隔离级别有四级,默认是“可重复读”(REPEATABLE READ)。

·        1).未提交读(READUNCOMMITTED)。另一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据(脏读)( 隔离级别最低,并发性能高 )

·        2).提交读(READCOMMITTED)。本事务读取到的是最新的数据(其他事务提交后的)。问题是,在同一个事务里,前后两次相同的SELECT会读到不同的结果(不重复读)。会出现不可重复读、幻读问题(锁定正在读取的行)

·        3).可重复读(REPEATABLEREAD)。在同一个事务里,SELECT的结果是事务开始时时间点的状态,因此,同样的SELECT操作读到的结果会是一致的。但是,会有幻读现象(稍后解释)。会出幻读(锁定所读取的所有行)。

·        4).串行化(SERIALIZABLE)。读操作会隐式获取共享锁,可以保证不同事务间的互斥(锁表)。


四个级别逐渐增强,每个级别解决一个问题。

·        1).脏读。另一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据。

·        2).不重复读。解决了脏读后,会遇到,同一个事务执行过程中,另外一个事务提交了新数据,因此本事务先后两次读到的数据结果会不一致。

·        3).幻读。解决了不重复读,保证了同一个事务里,查询的结果都是事务开始时的状态(一致性)。但是,如果另一个事务同时提交了新数据,本事务再更新时,就会“惊奇的”发现了这些新数据,貌似之前读到的数据是“鬼影”一样的幻觉。

    具体地:

    1). 脏读 

      首先区分脏页和脏数据

      脏页是内存的缓冲池中已经修改的page,未及时flush到硬盘,但已经写到redo log中。读取和修改缓冲池的page很正常,可以提高效率,flush即可同步。脏数据是指事务对缓冲池中的行记录record进行了修改,但是还没提交!!!,如果这时读取缓冲池中未提交的行数据就叫脏读,违反了事务的隔离性。脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。

    2). 不可重复读 

     是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,第二个事务已经提交。那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取文档时,文档已更改。原始读取不可重复。如果只有在作者全部完成编写后编辑人员才可以读取文档,则可以避免该问题

    3). 幻读 :

     是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。例如,一个编辑人员更改作者提交的文档,但当生产部门将其更改内容合并到该文档的主复本时,发现作者已将未编辑的新材料添加到该文档中。如果在编辑人员和生产部门完成对原始文档的处理之前,任何人都不能将新材料添加到文档中,则可以避免该问题。

2、隔离级别实验

       以下实验基于博主MySQL Server 5.6

       首先创建一个表,如下:

USE test;  
CREATE TABLE `t` (  
  
  `a` int(11) NOT NULL PRIMARY KEY  
  
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


2.1、实验一:解释脏读、可重复读问题  


 

事务A READ-UNCOMMITTED

事务B READ-COMMITTED,

事务C-1 REPEATABLE-READ

事务C-2 REPEATABLE-READ

事务D SERIALIZABLE

set autocommit =0;

         

start transaction ;

     

start transaction;

 

insert into t(a)values(4);

         
 

select * from t;

1,2,3,4(脏读:读取到了未提交的事务中的数据)

select * from t;

1,2,3(解决脏读)

select * from t;

1,2,3

select * from t;

1,2,3

select * from t;

1,2,3

           
           

commit;

         
 

select * from t:

1,2,3,4

select * from t:

1,2,3,4

select * from t:

1,2,3,4 (与上面的不在一个事务中,所以读到为事务提交后最新的,所以可读到4)

select * from t:

1,2,3(重复读:由于与上面的在一个事务中,所以只读到事务开始事务的数据,也就是重复读)

select * from t:

1,2,3,4

       

commit(提交事务,下面的就是一个新的事务,所以可以读到事务提交以后的最新数据)

 
       

select * from t:

1,2,3,4

 

READ-UNCOMMITTED 会产生脏读,基本很少适用于实际场景,所以基本不使用。


2.2、实验二:测试READ-COMMITTED与REPEATABLE-READ

事务A

事务B READ-COMMITTED

事务C REPEATABLE-READ

set autocommit =0;

   

start transaction ;

start transaction;

start transaction;

insert into t(a)values(4);

   
 

select * from t;

1,2,3

select * from t;

1,2,3

     
     

commit;

   
 

select * from t:

1,2,3,4

select * from t:

1,2,3(重复读:由于与上面的在一个事务中,所以只读到事务开始事务的数据,也就是重复读)

   

commit(提交事务,下面的就是一个新的事务,所以可以读到事务提交以后的最新数据)

   

select * from t:

1,2,3,4

REPEATABLE-READ可以确保一个事务中读取的数据是可重复的,也就是相同的读取(第一次读取以后,即使其他事务已经提交新的数据,同一个事务中再次select也并不会被读取)。

READ-COMMITTED只是确保读取最新事务已经提交的数据。

当然数据的可见性都是对不同事务来说的,同一个事务,都是可以读到此事务中最新数据的。如下,

  1. start transaction;  
    insert into t(a)values(4);  
    select *from t;    
    1,2,3,4;  
    insert into t(a)values(5);  
    select *from t;  
    1,2,3,4,5;


2.3、实验三:测试SERIALIZABLE事务对其他的影响


事务A SERIALIZABLE

事务B READ-UNCOMMITTED

事务C READ-COMMITTED,

事务D REPEATABLE-READ

事务E SERIALIZABLE

set autocommit =0;

       

start transaction ;

   

start transaction;

 

select a from t union all select sleep(1000) from dual;

       
 

insert into t(a)values(5);

insert into t(a)values(5);

insert into t(a)values(5);

insert into t(a)values(5);

 

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

 

SERIALIZABLE 串行化执行,导致所有其他事务不得不等待事务A结束才行可以执行,这里特意使用了sleep函数,直接导致事务B,C,D,E等待事务A持有释放的锁。由于我sleep了1000秒,而innodb_lock_wait_timeout为120s。所以120s到了就报错HY000错误。

SERIALIZABLE是相当严格的串行化执行模式,不管是读还是写,都会影响其他读取相同的表的事务。是严格的表级读写排他锁。也就失去了innodb引擎的优点。实际应用很少。


2.4、实验四:幻读

一些文章写到InnoDB的可重复读避免了“幻读”(phantom read),这个说法并不准确。做个实验:(以下所有试验要注意存储引擎和隔离级别)

  1. CREATE TABLE `t_bitfly` (  

  2. `id` bigint(20) NOT NULL default '0',  

  3. `value` varchar(32) default NULL,  

  4. PRIMARY KEY (`id`)  

  5. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  

  6.   

  7. select @@global.tx_isolation, @@tx_isolation;  

  8. +-----------------------+-----------------+  

  9. | @@global.tx_isolation | @@tx_isolation  |  

  10. +-----------------------+-----------------+  

  11. REPEATABLE-READ       | REPEATABLE-READ |  

  12. +-----------------------+-----------------+  

实验4-1:


Session A

Session B

start transaction ; start transaction ;

SELECT * FROM t_bitfly;
empty set

 


INSERT INTO t_bitfly VALUES (1, 'a');COMMIT;
SELECT * FROM t_bitfly;
| empty set


INSERT INTO t_bitfly VALUES (1, 'a');
|ERROR 1062 (23000):
|Duplicate entry '1' for key 1
(刚刚明明告诉我没有这条记录的)
I

如此就出现了幻读,以为表里没有数据,其实数据已经存在了,提交后,才发现数据冲突了。

实验4-2:


Session A

Session B

start transaction ;

start transaction ;

SELECT * FROM t_bitfly;
| +------+-------+
| | id   | value |
| +------+-------+
| |    1 |a     |
| +------+-------+

 
 

INSERT INTO t_bitfly VALUES (2, 'b');

SELECT * FROM t_bitfly;
| +------+-------+
| | id   | value |
| +------+-------+
| |    1 |a     |
| +------+-------+

 

 

COMMIT;

SELECT * FROM t_bitfly;
| +------+-------+
| | id   | value |
| +------+-------+
| |    1 |a     |
| +------+-------+

 

UPDATE t_bitfly SET value='z';
| Rows matched: 2  Changed:2  Warnings: 0

(怎么多出来一行)

 

SELECT * FROM t_bitfly;
| +------+-------+
| | id   | value |
| +------+-------+
| |    1 |z     |
| |    2 |z     |
| +------+-------+

 

本事务中第一次读取出一行,做了一次更新后,另一个事务里提交的数据就出现了。也可以看做是一种幻读。

附说明


那么,InnoDB指出的可以避免幻读是怎么回事呢?

http://dev.mysql.com/doc/refman/5.0/en/innodb-record-level-locks.html

By default, InnoDB operatesin REPEATABLE READ transaction isolation level and with the innodb_locks_unsafe_for_binlog system variable disabled. In this case, InnoDB uses next-key locks for searches and index scans, which prevents phantom rows (see Section 13.6.8.5, “Avoidingthe Phantom Problem Using Next-Key Locking”).

准备的理解是,当隔离级别是可重复读,且禁用innodb_locks_unsafe_for_binlog的情况下,在搜索和扫描index的时候使用的next-keylocks可以避免幻读。

关键点在于,是InnoDB默认对一个普通的查询也会加next-key locks,还是说需要应用自己来加锁呢?如果单看这一句,可能会以为InnoDB对普通的查询也加了锁,如果是,那和序列化(SERIALIZABLE)的区别又在哪里呢?

MySQL manual里还有一段:

13.2.8.5. Avoiding the PhantomProblem Using Next-Key Locking (http://dev.mysql.com/doc/refman/5.0/en/innodb-next-key-locking.html)

Toprevent phantoms, InnoDB usesan algorithm called next-key locking that combinesindex-row locking with gap locking.

Youcan use next-key locking to implement a uniqueness check in your application:If you read your data in share mode and do not see a duplicate for a row you are going to insert, then you can safely insert your row and know that the next-key lock set on the success or of your row during the read prevents anyone mean while inserting a duplicate for your row. Thus, the next-key locking enables you to “lock” the nonexistence of something in your table.

我的理解是说,InnoDB提供了next-key locks,但需要应用程序自己去加锁。manual里提供一个例子:

SELECT * FROM child WHERE id> 100 FOR UPDATE;

这样,InnoDB会给id大于100的行(假如child表里有一行id为102),以及100-102,102+的gap都加上锁。

可以使用show engine innodb status来查看是否给表加上了锁。


再看一个实验,要注意,表t_bitfly里的id为主键字段。

实验4-3:


Session A

Session B

start transaction ;

start transaction ;

SELECT * FROM t_bitfly
 WHERE id<=1
 FOR UPDATE;
| +------+-------+
| | id   | value |
| +------+-------+
| |    1 |a     |
| +------+-------+

 
 

 INSERT INTO t_bitfly   VALUES (2, 'b');
| Query OK, 1 row affected

SELECT * FROM t_bitfly;
| +------+-------+
| | id   | value |
| +------+-------+
| |    1 |a     |
| +------+-------+

 

 

INSERT INTO t_bitfly VALUES (0, '0');
|  (waiting for lock ...
|   then timeout) ERROR 1205 (HY000):Lock wait timeout exceeded;
|try restarting transaction

SELECT * FROM t_bitfly;
| +------+-------+
| | id   | value |
| +------+-------+
| |    1 |a     |
| +------+-------+

 

 

COMMIT;

SELECT * FROM t_bitfly;
| +------+-------+
| | id   | value |
| +------+-------+
| |    1 |a     |
| +------+-------+

 

可以看到,用id<=1加的锁,只锁住了id<=1的范围,可以成功添加id为2的记录,添加id为0的记录时就会等待锁的释放。

附说明:

MySQL manual里对可重复读里的锁的详细解释:

http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html#isolevel_repeatable-read

For locking reads (SELECT with FOR UPDATE or LOCK IN SHARE MODE),UPDATE, and DELETE statements, locking depends on whether the statement uses a unique index with a unique search condition, or a range-type search condition. For a unique index with a unique search condition, InnoDB locks only the index record found, not the gap before it. For other search conditions, InnoDB locks the index range scanned, using gap locks or next-key (gap plus index-record)locks to block insertions by other sessions into the gaps covered by the range.

一致性读和提交读,先看实验,

实验4-4:


Session A

Session B

start transaction ;

start transaction ;

SELECT * FROM t_bitfly;
| +------+-------+
| | id   | value |
| +------+-------+
| |    1 |a     |
| +------+-------+

 
 

 INSERT INTO t_bitfly   VALUES (2, 'b');

 

COMMIT;

SELECT * FROM t_bitfly;
| +------+-------+
| | id   | value |
| +------+-------+
| |    1 |a     |
| +------+-------+

 

SELECT * FROM t_bitfly LOCK IN SHARE MODE;
| +----+-------+
| | id | value |
| +----+-------+
| |  1 |a     |
| |  2 |b     |
| +----+-------+

 

SELECT * FROM t_bitfly FOR UPDATE;
| +----+-------+
| | id | value |
| +----+-------+
| |  1 |a     |
| |  2 |b     |
| +----+-------+

 

SELECT * FROM t_bitfly;
| +----+-------+
| | id | value |
| +----+-------+
| |  1 |a     |
| +----+-------+

 

附说明:如果使用普通的读,会得到一致性的结果,如果使用了加锁的读,就会读到“最新的”“提交”读的结果。

本身,可重复读和提交读是矛盾的。在同一个事务里,如果保证了可重复读,就会看不到其他事务的提交,违背了提交读;如果保证了提交读,就会导致前后两次读到的结果不一致,违背了可重复读。

可以这么讲,InnoDB提供了这样的机制,在默认的可重复读的隔离级别里,可以使用加锁读去查询最新的数据。

http://dev.mysql.com/doc/refman/5.0/en/innodb-consistent-read.html

If you want to see the “freshest” state of the database, you should use either theREAD COMMITTED isolation level or a locking read:
SELECT * FROM t_bitfly LOCK IN SHARE MODE;

------

3、总结

结论:MySQL InnoDB事务默认隔离级别是可重复读并不保证避免幻读,需要应用使用加锁读来保证。而这个加锁度使用到的机制就是next-key locks。

以上是MySQL InnoDB四个事务级别与脏读、不重复读、幻读是什么的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
MySQL索引基数如何影响查询性能?MySQL索引基数如何影响查询性能?Apr 14, 2025 am 12:18 AM

MySQL索引基数对查询性能有显着影响:1.高基数索引能更有效地缩小数据范围,提高查询效率;2.低基数索引可能导致全表扫描,降低查询性能;3.在联合索引中,应将高基数列放在前面以优化查询。

MySQL:新用户的资源和教程MySQL:新用户的资源和教程Apr 14, 2025 am 12:16 AM

MySQL学习路径包括基础知识、核心概念、使用示例和优化技巧。1)了解表、行、列、SQL查询等基础概念。2)学习MySQL的定义、工作原理和优势。3)掌握基本CRUD操作和高级用法,如索引和存储过程。4)熟悉常见错误调试和性能优化建议,如合理使用索引和优化查询。通过这些步骤,你将全面掌握MySQL的使用和优化。

现实世界Mysql:示例和用例现实世界Mysql:示例和用例Apr 14, 2025 am 12:15 AM

MySQL在现实世界的应用包括基础数据库设计和复杂查询优化。1)基本用法:用于存储和管理用户数据,如插入、查询、更新和删除用户信息。2)高级用法:处理复杂业务逻辑,如电子商务平台的订单和库存管理。3)性能优化:通过合理使用索引、分区表和查询缓存来提升性能。

MySQL中的SQL命令:实践示例MySQL中的SQL命令:实践示例Apr 14, 2025 am 12:09 AM

MySQL中的SQL命令可以分为DDL、DML、DQL、DCL等类别,用于创建、修改、删除数据库和表,插入、更新、删除数据,以及执行复杂的查询操作。1.基本用法包括CREATETABLE创建表、INSERTINTO插入数据和SELECT查询数据。2.高级用法涉及JOIN进行表联接、子查询和GROUPBY进行数据聚合。3.常见错误如语法错误、数据类型不匹配和权限问题可以通过语法检查、数据类型转换和权限管理来调试。4.性能优化建议包括使用索引、避免全表扫描、优化JOIN操作和使用事务来保证数据一致性

InnoDB如何处理酸合规性?InnoDB如何处理酸合规性?Apr 14, 2025 am 12:03 AM

InnoDB通过undolog实现原子性,通过锁机制和MVCC实现一致性和隔离性,通过redolog实现持久性。1)原子性:使用undolog记录原始数据,确保事务可回滚。2)一致性:通过行级锁和MVCC确保数据一致。3)隔离性:支持多种隔离级别,默认使用REPEATABLEREAD。4)持久性:使用redolog记录修改,确保数据持久保存。

MySQL的位置:数据库和编程MySQL的位置:数据库和编程Apr 13, 2025 am 12:18 AM

MySQL在数据库和编程中的地位非常重要,它是一个开源的关系型数据库管理系统,广泛应用于各种应用场景。1)MySQL提供高效的数据存储、组织和检索功能,支持Web、移动和企业级系统。2)它使用客户端-服务器架构,支持多种存储引擎和索引优化。3)基本用法包括创建表和插入数据,高级用法涉及多表JOIN和复杂查询。4)常见问题如SQL语法错误和性能问题可以通过EXPLAIN命令和慢查询日志调试。5)性能优化方法包括合理使用索引、优化查询和使用缓存,最佳实践包括使用事务和PreparedStatemen

MySQL:从小型企业到大型企业MySQL:从小型企业到大型企业Apr 13, 2025 am 12:17 AM

MySQL适合小型和大型企业。1)小型企业可使用MySQL进行基本数据管理,如存储客户信息。2)大型企业可利用MySQL处理海量数据和复杂业务逻辑,优化查询性能和事务处理。

幻影是什么读取的,InnoDB如何阻止它们(下一个键锁定)?幻影是什么读取的,InnoDB如何阻止它们(下一个键锁定)?Apr 13, 2025 am 12:16 AM

InnoDB通过Next-KeyLocking机制有效防止幻读。1)Next-KeyLocking结合行锁和间隙锁,锁定记录及其间隙,防止新记录插入。2)在实际应用中,通过优化查询和调整隔离级别,可以减少锁竞争,提高并发性能。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
4 周前By尊渡假赌尊渡假赌尊渡假赌

热工具

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

螳螂BT

螳螂BT

Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中

MinGW - 适用于 Windows 的极简 GNU

MinGW - 适用于 Windows 的极简 GNU

这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。

SecLists

SecLists

SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。