ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MySQL 事务:事务自动提交、查看/修改事务隔离级别、读未提交(RU)、读提交(RC)、可重复读(RR)、串行化

MySQL 事务:事务自动提交、查看/修改事务隔离级别、读未提交(RU)、读提交(RC)、可重复读(RR)、串行化 目录1. 如果没有事务2. 什么是事务3. 为什么会出现事务4. 事务的版本支持5. 事务的提交方法5.1 查看事务提交方式5.2 用 set 改变 MySQL 的自动提交模式6. 事务常见操作方式6.1 查看/修改事务隔离级别6.2 事务的开始与回滚6.3 事务结束后客户端崩溃MySQL 数据不受影响已经持久化6.4 begin 不受 autocommit 的影响6.5 单条 SQL 与事务的关系6.6 结论7. 事务隔离级别7.1 如何理解隔离性7.2 隔离级别7.3 查看与设置隔离性7.4 读未提交【Read Uncommitted】7.5 读提交【Read Committed】不可重复读可能造成的问题7.6 可重复读【Repeatable Read】insert 对可重复读的影响7.8 串行化【serializable】7.9 总结7.10 一致性1. 如果没有事务1. CURD 不加控制会有什么问题如果没有事务在两件事并发的时候就会出现问题比如只剩最后一张票了这时候多人并发购票就会造成最后一张票被重复买走的情况2. CURD 满足什么属性能解决上述问题买票的过程得是原子买票互相应该不能影响买完票应该要永久有效买前和买后都要是确定的状态2. 什么是事务事务就是一组 DML数据库操作语言 语句组成这些语句在逻辑上存在相关性这一组 DML 语句要么全部成功要么全部失败是一个整体。MySQL 提供一种机制保证我们达到这样的效果。事务还规定不同的客户端看到的数据是不相同的。事务就是要做的或所做的事情主要用于处理操作量大复杂度高的数据。假设一种场景你毕业了学校的教务系统后台 MySQL 中不在需要你的数据要删除你的所有信息那么要删除你的基本信息姓名电话籍贯等的同时也删除和你有关的其他信息比如你的各科成绩你在校表现甚至你在论坛发过的文章等。这样就需要多条 MySQL 语句构成那么所有这些操作合起来就构成了一个事务。正如我们上面所说一个 MySQL 数据库可不止你一个事务在运行同一时刻甚至有大量的请求被包装成事务在向 MySQL 服务器发起事务处理请求。而每条事务至少一条 SQL 这样如果大家都访问同样的表数据在不加保护的情况就绝对会出现问题。甚至因为事务由多条 SQL 构成那么也会存在执行到一半出错或者不想再执行的情况那么已经执行的怎么办呢所以一个完整的事务绝对不是简单的 sql 集合还需要满足如下四个属性原子性一个事务transaction中的所有操作要么全部完成要么全部不完成不会结束在中间某个环节。事务在执行过程中发生错误会被回滚Rollback到事务开始前的状态就像这个事务从来没有执行过一样。一致性在事务开始之前和事务结束以后数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。隔离性数据库允许多个并发事务同时对其数据进行读写和修改的能力隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别包括读未提交Read uncommitted、读提交read committed、可重复读repeatable read和串行化Serializable持久性事务处理结束后对数据的修改就是永久的即便系统故障也不会丢失。上面四个属性可以简称为 ACID。原子性Atomicity或称不可分割性一致性Consistency隔离性Isolation又称独立性持久性Durability3. 为什么会出现事务事务被 MySQL 编写者设计出来本质是为了当应用程序访问数据库的时候事务能够简化我们的编程模型不需要我们去考虑各种各样的潜在错误和并发问题。可以想一下当我们使用事务时要么提交要么回滚我们不会去考虑网络异常了服务器宕机了同时更改一个数据怎么办对吧因此事务本质上是为了应用层服务的而不是伴随着数据库系统天生就有的。我们后面把 MySQL 中的一行信息称为一行记录4. 事务的版本支持在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务MyISAM 不支持。mysql show engines \G *************************** 1. row *************************** Engine: InnoDB -- 引擎名称 Support: DEFAULT -- 默认引擎 Comment: Supports transactions, row-level locking, and foreign keys -- 描述 Transactions: YES -- 支持事务 XA: YES Savepoints: YES -- 支持事务保存点 *************************** 5. row *************************** Engine: MyISAM Support: YES Comment: MyISAM storage engine Transactions: NO -- MyISAM不支持事务 XA: NO Savepoints: NO *************************** 2. row *************************** Engine: MRG_MYISAM Support: YES Comment: Collection of identical MyISAM tables Transactions: NO XA: NO Savepoints: NO *************************** 3. row *************************** Engine: MEMORY --内存引擎 Support: YES Comment: Hash based, stored in memory, useful for temporary tables Transactions: NO XA: NO Savepoints: NO *************************** 4. row *************************** Engine: BLACKHOLE Support: YES Comment: /dev/null storage engine (anything you write to it disappears) Transactions: NO XA: NO Savepoints: NO *************************** 6. row *************************** Engine: CSV Support: YES Comment: CSV storage engine Transactions: NO XA: NO Savepoints: NO *************************** 7. row *************************** Engine: ARCHIVE Support: YES Comment: Archive storage engine Transactions: NO XA: NO Savepoints: NO *************************** 8. row *************************** Engine: PERFORMANCE_SCHEMA Support: YES Comment: Performance Schema Transactions: NO XA: NO Savepoints: NO *************************** 9. row *************************** Engine: FEDERATED Support: NO Comment: Federated MySQL storage engine Transactions: NULL XA: NULL Savepoints: NULL 9 rows in set (0.00 sec)5. 事务的提交方法事务的提交方式常见的有两种自动提交手动提交5.1 查看事务提交方式mysql show variables like autocommit; ---------------------- | Variable_name | Value | ---------------------- | autocommit | ON | 事务提交方式为默自动提交 ---------------------- 1 row in set (0.06 sec)5.2 用 set 改变 MySQL 的自动提交模式mysql set autocommit0; #SET AUTOCOMMIT0 禁止自动提交 Query OK, 0 rows affected (0.00 sec) mysql show variables like autocommit; ---------------------- | Variable_name | Value | ---------------------- | autocommit | OFF | ---------------------- 1 row in set (0.00 sec)mysql set autocommit1; #SET AUTOCOMMIT1 开启自动提交 Query OK, 0 rows affected (0.01 sec) mysql show variables like autocommit; ---------------------- | Variable_name | Value | ---------------------- | autocommit | ON | ---------------------- 1 row in set (0.01 sec)6. 事务常见操作方式6.1 查看/修改事务隔离级别1. 查看事务隔离级别select transaction_isolation; ——MySQL 8.0 以后select tx_isolation; ——MySQL 5.7之前mysql select transaction_isolation; ------------------------- | transaction_isolation | ------------------------- | REPEATABLE-READ | ------------------------- 1 row in set (0.00 sec)2. 修改事务隔离级别为读未提交修改后需要重启 MySQL 才能生效mysql set global transaction isolation level read uncommitted; Query OK, 0 rows affected (0.00 sec) ## 需要重启 MySQL 才能生效 mysql select transaction_isolation; ------------------------- | transaction_isolation | ------------------------- | READ-UNCOMMITTED | ------------------------- 1 row in set (0.00 sec)创建测试表CREATE TABLE IF NOT EXISTS account ( id INT PRIMARY KEY, name VARCHAR(50) NOT NULL DEFAULT , blance DECIMAL(10,2) NOT NULL DEFAULT 0.0 ) ENGINEInnoDB DEFAULT CHARSETutf8;6.2 事务的开始与回滚开始事务start transaction; 或者 begin;结束事务commit;保存节点savepoint point_name;事务回滚到指定节点rollback to point_name;事务回滚到最开始rollback;mysql show variables like autocommit; -- 事务设置为自动提交 ---------------------- | Variable_name | Value | ---------------------- | autocommit | ON | ---------------------- 1 row in set (0.00 sec) mysql start transaction; -- 开始一个事务begin也可以推荐begin Query OK, 0 rows affected (0.00 sec) mysql savepoint save1; -- 创建一个保存点save1 Query OK, 0 rows affected (0.00 sec) mysql insert into account values (1, 张三, 100); -- 插入一条记录 Query OK, 1 row affected (0.05 sec) mysql savepoint save2; -- 创建一个保存点save2 Query OK, 0 rows affected (0.01 sec) mysql insert into account values (2, 李四, 10000); -- 在插入一条记录 Query OK, 1 row affected (0.00 sec) mysql select * from account; -- 两条记录都在了 ---------------------- | id | name | blance | ---------------------- | 1 | 张三 | 100.00 | | 2 | 李四 | 10000.00 | ---------------------- 2 rows in set (0.00 sec) mysql rollback to save2; -- 回滚到保存点save2 Query OK, 0 rows affected (0.03 sec) mysql select * from account; -- 一条记录没有了 -------------------- | id | name | blance | -------------------- | 1 | 张三 | 100.00 | -------------------- 1 row in set (0.00 sec) mysql rollback; -- 直接rollback回滚在最开始 Query OK, 0 rows affected (0.00 sec) mysql select * from account; -- 所有刚刚的记录没有了 Empty set (0.00 sec)6.3 事务结束后客户端崩溃MySQL 数据不受影响已经持久化事务 commit 结束后客户端崩溃MySQL 数据不会受影响已经持久化在数据库中存储了。--终端 A mysql show variables like autocommit; -- 依旧自动提交 ---------------------- | Variable_name | Value | ---------------------- | autocommit | ON | ---------------------- 1 row in set (0.00 sec) mysql select * from account; -- 当前表内无数据 Empty set (0.00 sec) mysql begin; -- 开启事务 Query OK, 0 rows affected (0.00 sec) mysql insert into account values (1, 张三, 100); -- 插入记录 Query OK, 1 row affected (0.00 sec) mysql commit; --提交事务 Query OK, 0 rows affected (0.04 sec) mysql Aborted -- ctrl \ 异常终止MySQL--终端 B mysql select * from account; 化到MySQL中 -------------------- | id | name | blance | -------------------- | 1 | 张三 | 100.00 |--数据存在了所以commit的作用是将数据持久 -------------------- 1 row in set (0.00 sec)6.4 begin 不受 autocommit 的影响begin操作会自动更改提交方式不会受MySQL是否自动提交影响-- 终端 A mysql select *from account; --查看历史数据 -------------------- | id | name | blance | -------------------- | 1 | 张三 | 100.00 | -------------------- 1 row in set (0.00 sec) mysql show variables like autocommit; --查看事务提交方式 ---------------------- | Variable_name | Value | ---------------------- | autocommit | ON | ---------------------- 1 row in set (0.00 sec) mysql set autocommit0; --关闭自动提交 Query OK, 0 rows affected (0.00 sec) mysql show variables like autocommit; --查看关闭之后结果 ---------------------- | Variable_name | Value | ---------------------- | autocommit | OFF | ---------------------- 1 row in set (0.00 sec) mysql begin; --开启事务 Query OK, 0 rows affected (0.00 sec) mysql insert into account values (2, 李四, 10000); --插入记录 Query OK, 1 row affected (0.00 sec) mysql select *from account; --查看插入记录同时查看终端B ---------------------- | id | name | blance | ---------------------- | 1 | 张三 | 100.00 | | 2 | 李四 | 10000.00 | ---------------------- 2 rows in set (0.00 sec) mysql Aborted --再次异常终止-- 终端B mysql select * from account; --终端A崩溃前 ---------------------- | id | name | blance | ---------------------- | 1 | 张三 | 100.00 | | 2 | 李四 | 10000.00 | ---------------------- 2 rows in set (0.00 sec) mysql select * from account; --终端A崩溃后自动回滚 -------------------- | id | name | blance | -------------------- | 1 | 张三 | 100.00 | -------------------- 1 row in set (0.00 sec)6.5 单条 SQL 与事务的关系实验一关闭事务自动提交单条 SQL 不 commit 手动提交会有什么影响先说结论关闭事务自动提交单条 SQL 不 commit 手动提交客户端崩溃后之前未提交的这些 SQL 被回滚了操作无效了-- 终端A mysql select * from account; -------------------- | id | name | blance | -------------------- | 1 | 张三 | 100.00 | -------------------- 1 row in set (0.00 sec) mysql show variables like autocommit; ---------------------- | Variable_name | Value | ---------------------- | autocommit | ON | ---------------------- 1 row in set (0.00 sec) mysql set autocommit0; --关闭自动提交 Query OK, 0 rows affected (0.00 sec) mysql insert into account values (2, 李四, 10000); --插入记录 Query OK, 1 row affected (0.00 sec) mysql select *from account; --查看结果已经插入。此时可以再查看终端B ---------------------- | id | name | blance | ---------------------- | 1 | 张三 | 100.00 | | 2 | 李四 | 10000.00 | ---------------------- 2 rows in set (0.00 sec) mysql ^DBye --ctrl \ or ctrl d,终止终端--终端B mysql select * from account; --终端A崩溃前 ---------------------- | id | name | blance | ---------------------- | 1 | 张三 | 100.00 | | 2 | 李四 | 10000.00 | ---------------------- 2 rows in set (0.00 sec) mysql select * from account; --终端A崩溃后 -------------------- | id | name | blance | -------------------- | 1 | 张三 | 100.00 | -------------------- 1 row in set (0.00 sec)实验二开启事务自动提交单条 SQL 不 commit 手动提交会有什么影响先说结论开启事务自动提交单条 SQL 不 commit 手动提交客户端崩溃后之前未手动提交的这些 SQL 不会被回滚操作是有效的。--终端A mysql show variables like autocommit; --开启默认提交 ---------------------- | Variable_name | Value | ---------------------- | autocommit | ON | ---------------------- 1 row in set (0.00 sec) mysql select * from account; -------------------- | id | name | blance | -------------------- | 1 | 张三 | 100.00 | -------------------- 1 row in set (0.00 sec) mysql insert into account values (2, 李四, 10000); Query OK, 1 row affected (0.01 sec) mysql select *from account; --数据已经插入 ---------------------- | id | name | blance | ---------------------- | 1 | 张三 | 100.00 | | 2 | 李四 | 10000.00 | ---------------------- 2 rows in set (0.00 sec) mysql Aborted --异常终止--终端B mysql select * from account; --终端A崩溃前 ---------------------- | id | name | blance | ---------------------- | 1 | 张三 | 100.00 | | 2 | 李四 | 10000.00 | ---------------------- 2 rows in set (0.00 sec) mysql select * from account; --终端A崩溃后并不影响已经持久化。autocommit 起作用 ---------------------- | id | name | blance | ---------------------- | 1 | 张三 | 100.00 | | 2 | 李四 | 10000.00 | ---------------------- 2 rows in set (0.00 sec)6.6 结论在开启事务自动提交的情况下单条 sql 就是一个事务无需手动提交commit在关闭事务自动提交的情况下单条 sql 也需要手动提交commit只要输入 begin 或者 start transaction事务便必须要通过 commit 提交才会持久化与是否设置 set autocommit 无关事务可以手动回滚同时当操作异常MySQL 会自动回滚对于 InnoDB 每一条 SQL 语言都默认封装成事务自动提交。select 有特殊情况因为MySQL有 MVCC从上面的例子我们能看到事务本身的原子性回滚持久性commit。事务操作注意事项如果没有设置保存点也可以回滚但只能回滚到事务的开始。直接使用 rollback前提是事务还没有提交如果一个事务被提交了commit则不可以回退rollback可以选择回退到哪个保存点InnoDB 支持事务MyISAM 不支持事务开始事务可以使 start transaction 或者 begin7. 事务隔离级别7.1 如何理解隔离性MySQL 服务可能会同时被多个客户端进程线程访问访问的方式以事务方式进行。一个事务可能由多条 SQL 构成也就意味着任何一个事务都有执行前执行中执行后的阶段。而所谓的原子性其实就是让用户层要么看到执行前要么看到执行后。执行中出现问题可以随时回滚。所以单个事务对用户表现出来的特性就是原子性。但毕竟所有事务都要有个执行过程那么在多个事务各自执行多个 SQL 的时候就还是有可能会出现互相影响的情况。比如多个事务同时访问同一张表甚至同一行数据。就如同你妈妈给你说你要么别学要学就学到最好。至于你怎么学中间有什么困难你妈妈不关心。那么你的学习对你妈妈来讲就是原子的。那么你学习过程中很容易受别人干扰此时就需要将你的学习隔离开保证你的学习环境是健康的。数据库中为了保证事务执行过程中尽量不受干扰就有了一个重要特征隔离性。数据库中允许事务受不同程度的干扰就有了一种重要特征隔离级别7.2 隔离级别读未提交【Read Uncommitted】在该隔离级别所有的事务都可以看到其他事务没有提交的执行结果。实际生产中不可能使用这种隔离级别的相当于没有任何隔离性也会有很多并发问题如脏读幻读不可重复读等我们上面为了做实验方便用的就是这个隔离性。读提交【Read Committed】该隔离级别是大多数数据库的默认的隔离级别MySQL默认是Repeatable Read。它满足了隔离的简单定义一个事务只能看到其他的已经提交的事务所做的改变。这种隔离级别会引起不可重复读即一个事务执行时如果多次 select可能得到不同的结果可重复读【Repeatable Read】这是 MySQL 默认的隔离级别它确保同一个事务在执行中多次读取操作数据时会看到同样的数据行即别的事务对数据最终产生的影响当前事务只有在结束后再次读才能看见事务起降就算其他事务对数据产生影响也读不到但是会有幻读问题。串行化【Serializable】这是事务的最高隔离级别它通过强制事务排序使之不可能相互冲突从而解决了幻读的问题。它在每个读的数据行上面加上共享锁但是可能会导致超时和锁竞争这种隔离级别太极端实际生产基本不使用。隔离级别如何实现隔离基本都是通过锁实现的不同的隔离级别锁的使用是不同的。常见有表锁行锁读锁写锁间隙锁GAPNext-Key 锁GAP 行锁等。不过我们目前现有这个认识就行先关注上层使用。7.3 查看与设置隔离性1. 查看隔离性查看全局隔离级别select global.transaction_isolation;查看当前会话隔离级别select session.transaction_isolation; 或者select transaction_isolation;上面的操作是 MySQL 8.0 之后的如果是 MySQL 5.7 之前的需要把 transaction 换成 tx比如select global.tx_isolation;## 查看全局隔离级别 mysql select global.transaction_isolation; -------------------------------- | global.transaction_isolation | -------------------------------- | READ-UNCOMMITTED | -------------------------------- 1 row in set (0.00 sec) ## 查看当前会话隔离级别 mysql select session.transaction_isolation; --------------------------------- | session.transaction_isolation | --------------------------------- | READ-UNCOMMITTED | --------------------------------- 1 row in set (0.01 sec) ## 查看当前会话隔离级别 mysql select transaction_isolation; ------------------------- | transaction_isolation | ------------------------- | READ-UNCOMMITTED | ------------------------- 1 row in set (0.00 sec)2. 设置隔离性SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE};set [session / global] transaction isolation level {read uncommitted / read committed / repeatable read / serializable};新启会话的隔离级在创建之初受全局隔离级别的影响7.4 读未提交【Read Uncommitted】-- 终端A mysql begin; --开启事务 Query OK, 0 rows affected (0.00 sec) mysql update account set blance123.0 where id1; --更新指定行 Query OK, 1 row affected (0.05 sec) Rows matched: 1 Changed: 1 Warnings: 0 --没有commit哦--终端B mysql begin; mysql select * from account; ---------------------- | id | name | blance | ---------------------- | 1 | 张三 | 123.00 | --读到终端A更新但是未commit的数据[insertdelete同样] | 2 | 李四 | 10000.00 | ---------------------- 2 rows in set (0.00 sec)一个事务在执行中读到另一个执行中事务的更新(或其他操作)但是未 commit 的数据这种现象叫做脏读(dirty read)7.5 读提交【Read Committed】比如现在同时启动两个事务终端A 负责更新数据终端B 需要在终端A 更新数据后查看表的内容看看终端A 这边的事务提交前后终端B 这边查表的内容是否有变化--终端A mysql begin; --手动开启事务同步的开始终端B事务 Query OK, 0 rows affected (0.00 sec) mysql update account set blance321.0 where id1; --更新张三数据 Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql commit; --commit提交 Query OK, 0 rows affected (0.01 sec)可以看到在终端A 提交事务后终端B 此时还在当前事务中并未commit它看到了表的变化那么就造成了同一个事务内同样的读取在不同的时间段(依旧还在事务操作中)读取到了不同的值这种现象叫做不可重复读(non reapeatable read)--终端B mysql begin; --手动开启事务和终端A一前一后 Query OK, 0 rows affected (0.00 sec) mysql select * from account; --终端A commit之前查看不到 ---------------------- | id | name | blance | ---------------------- | 1 | 张三 | 123.00 | --老的值 | 2 | 李四 | 10000.00 | ---------------------- 2 rows in set (0.00 sec) --终端A commit之后看到了 mysql select *from account; ---------------------- | id | name | blance | ---------------------- | 1 | 张三 | 321.00 | --新的值 | 2 | 李四 | 10000.00 | ---------------------- 2 rows in set (0.00 sec)不可重复读可能造成的问题《年终奖事件·查表篇》年底了老板把财务叫到办公室去查工资表按表发年终奖。3 万以上的发金条3 万以下的发银条。财务使用 MySQL 开了个事务在查表准备挨个核对。他先查 3 万以下的查到了小王月薪2万5记下来银条。刚记完小王去找老板涨工资老板同意后数据库管理员就给小王的工资更新成 3万5了。财务还搁那里查表呢结果因为事务是读提交的是不可重复读的小王的工资更新财务这边立马就能看见于是财务查 3 万以上的人的时候把小王又记下来了。然后财务把统计结果给了老板老板看到了以下两条数据小王 月薪2万5 --发银条小王 月薪3万2 --发金条老板知道后气得拍桌子我让你查一次表按一次标准发你怎么查出了两个小王同一笔奖金你读了两次工资两次不一样多花了一根金条这就是不可重复读同一个事务老李查工资表发奖金里两次读取同一行数据小王的工资中间被另一个事务涨工资并更新表修改了导致两次读到的值不一致最终结果出错。7.6 可重复读【Repeatable Read】两个事务同时进行事务A 提交后的结果事务B 在事务期间无法看到只有结束事务B 后再次查询才能看到这样可以保证事务B 在事务期间每次读到的信息都不会变不会多出没进行过的操作这就是可重复读。--终端A mysql select *from account; --查看当前数据 ---------------------- | id | name | blance | ---------------------- | 1 | 张三 | 321.00 | | 2 | 李四 | 10000.00 | ---------------------- 2 rows in set (0.00 sec) mysql begin; --开启事务同步的终端B也开始事务 Query OK, 0 rows affected (0.00 sec) mysql update account set blance4321.0 where id1; --更新数据 Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 --切换到终端B查看另一个事务是否能看到 mysql commit; --提交事务--终端B mysql begin; Query OK, 0 rows affected (0.00 sec) mysql select * from account; --终端A中事务 commit之前查看当前表中数据数据未更新 ---------------------- | id | name | blance | ---------------------- | 1 | 张三 | 321.00 | | 2 | 李四 | 10000.00 | ---------------------- 2 rows in set (0.00 sec) mysql select * from account; --终端A中事务 commit 之后查看当前表中数据数据未更新 ---------------------- | id | name | blance | ---------------------- | 1 | 张三 | 321.00 | | 2 | 李四 | 10000.00 | ---------------------- 2 rows in set (0.00 sec) --可以看到在终端B中事务无论什么时候进行查找看到的结果都是一致的这叫做可重复读 mysql commit; --结束事务 Query OK, 0 rows affected (0.00 sec) mysql select * from account; --再次查看看到最新的更新数据 ---------------------- | id | name | blance | ---------------------- | 1 | 张三 | 4321.00 | | 2 | 李四 | 10000.00 | ---------------------- 2 rows in set (0.00 sec)insert 对可重复读的影响一般的数据库在可重复读情况的时候无法屏蔽其他事务 insert 的数据为什么因为隔离性实现是对数据加锁完成的而insert待插入的数据因为并不存在那么一般加锁无法屏蔽这类问题会造成虽然大部分内容是可重复读的但是insert 的数据在可重复读情况被读取出来导致多次查找时会多查找出来新的记录就如同产生了幻觉。这种现象叫做幻读phantom read。很明显而 MySQL 在 RR 级别的时候是解决了幻读问题的解决的方式是用 Next‑Key 锁GAP行锁解决的。这块比较难有兴趣可以自行了解一下。7.8 串行化【serializable】对所有操作全部加锁进行串行化不会有问题但是只要串行化效率很低几乎完全不会被采用--终端A mysql begin; --开启事务终端B同步开启 Query OK, 0 rows affected (0.00 sec) mysql select * from account; --两个读取不会串行化共享锁 ---------------------- | id | name | blance | ---------------------- | 1 | 张三 | 4321.00 | | 2 | 李四 | 10000.00 | | 3 | 王五 | 5432.00 | ---------------------- 3 rows in set (0.00 sec) --终端A中有更新或者其他操作会阻塞。直到终端B事务提交。 mysql update account set blance1.00 where id1; Query OK, 1 row affected (18.19 sec) Rows matched: 1 Changed: 1 Warnings: 0--终端B mysql begin; Query OK, 0 rows affected (0.00 sec) mysql select * from account; --两个读取不会串行化 ---------------------- | id | name | blance | ---------------------- | 1 | 张三 | 4321.00 | | 2 | 李四 | 10000.00 | | 3 | 王五 | 5432.00 | ---------------------- 3 rows in set (0.00 sec) mysql commit; --提交之后终端A中的update才会提交。 Query OK, 0 rows affected (0.00 sec)7.9 总结其中隔离级别越严格安全性越高但数据库的并发性能也就越低往往需要在两者之间找一个平衡点。不可重复读的重点是修改和删除同样的条件你读取过的数据再次读取出来发现值不一样了幻读的重点在于新增同样的条件第 1 次和第 2 次读出来的记录数不一样。mysql 默认的隔离级别是可重复读一般情况下不要修改。上面的例子可以看出事务也有长短事务这样的概念。事务间互相影响指的是事务在并行执行的时候即都没有 commit 的时候影响会比较大。7.10 一致性事务执行的结果必须使数据库从一个一致性状态变到另一个一致性状态。当数据库只包含事务成功提交的结果时数据库处于一致性状态。如果系统运行发生中断某个事务尚未完成而被迫中断而该未完成的事务对数据库所做的修改已被写入数据库此时数据库就处于一种不正确不一致的状态。因此一致性是通过原子性来保证的。其实一致性和用户的业务逻辑强相关一般 MySQL 提供技术支持但是一致性还是要用户业务逻辑做支撑也就是一致性是由用户决定的。而技术上ACID 是通过 AID 来保证 C 的。
返回列表