MySQL中的事务

62 阅读4分钟

何为数据库事务?

事务是一个业务,一个逻辑工作单元,这个工作单元中的所有的操作要么都执行,要么都不执行.

事务有哪些特性?

  • 原子性:同一个事务内部的所有操作,要么都执行,要么都不执行.
  • 一致性:事务前和事务后,数据的最终结果应该是一样的(例如转账操作,转入和转出的金额应该一致的)
  • 隔离性:事务与事务之间应该是相互隔离的,互不影响(例如存钱业务,多个存钱的并发事务应该是相互隔离的)
  • 持久性:事务一旦提交,对数据库数据的更新是要写到数据库的.

MySQL中事务特性是如何实现的?

  1. 原子性:通过undolog(回滚日志)实现-执行回滚;
  2. 隔离性:通过锁(Lock)以及MVCC(多版本并发控制);
  3. 一致性:通过undolog(回滚日志),redolog(重做日志),binlog(归档日志);
  4. 持久性:通过redolog日志实现;(commit了不一定会马上更新后的数据写入到磁盘)

MySQL中的事务隔离级别有哪些?

为了解决事务并发执行时,可能会带来的一些问题(脏读,不可重复读,幻读),MySQL内部提供了如下几种事务隔离级别:

  • Read-UnCommitted (读未提交) : 这个隔离级别可能会出现脏读现象(一个事务读取了其它事务未提交的数据)
  • Read-Committed (读已提交):这个隔离级别解决了脏读问题,但是会出现不可重复读问题.
  • Repeatable-read(可重复读):解决了事务的可重复读问题,但会是会出现幻读问题(读到的数据可能已经被删除)
  • Seriablizable 可串行化:所有事务对表的操作是顺序执行的. 如何查看事务的隔离级别呢?
select @ @tx _isolation; #mysql5.7
select @@transaction_isolation; #mysql8.0

如何设置的事务的隔离级别呢?

set tx_isolation ='read-committed'; #mysql5.7
set session transaction isolation level read committed; #mysql8.0 此隔离级别对当前会话有效
set global transaction isolation level read committed; #mysql8.0设置全局事务隔离级别,新会话有效.

多事务并发执行时可能会有什么问题?

  • 脏读 (一个事务读取了其它事务未提交的数据)
  • 不可重复读(一个事务内部多次对相同sql的查询结果是不一致)
  • 幻影读(对表中数据进行统计时,统计出的结果和实际表中的结果不一致)

脏读问题实验分析(mysql5.7) 打开两个会话窗口,分别启动事务A和事务B

事务A事务B
设置事务隔离级别set tx_isolation='read-uncommitted'设置事务隔离级别set tx_isolation='read-uncommitted'
设置事务手动提交set autocommit=0设置事务手动提交set autocommit=0
开启事务begin开启事务begin
执行查询select * from regions;执行写入操作insert into regions values (10,'RA');
执行查询select * from regions;这里会发现事务B中的写入的RA记录,这条记录在事务B中的还没有提交,我们通常称之为脏数据.
commit;执行回滚操作rollback;

脏读问题的解决方案:修改事务隔离级别为read-committed;

不可重复读实验分析 mysql 5.7 (10.5.17-MariaDB 隔离级别不生效)

事务A事务B
设置事务隔离级别set tx_isolation='read-committed'设置事务隔离级别set tx_isolation='read-committed'
设置事务手动提交set autocommit=0设置事务手动提交set autocommit=0
开启事务begin开启事务begin
执行查询select * from regions;执行写入操作insert into regions values (10,'RA');commit;
执行查询select * from regions;commit;

MySQL 8.0 (查看隔离级别使用 select @@transaction_isolation;)

事务A事务B
设置事务隔离级别set session transaction isolation level read commit;设置事务隔离级别set session transaction isolation level read commit;
设置事务手动提交set autocommit=0设置事务手动提交set autocommit=0
开启事务begin开启事务begin
执行查询select * from regions;执行写入操作insert into regions values (10,'RA');commit;
执行查询select * from regions;commit;

假如希望同一个事务内部,基于同一个sql进行的数据读取,始终是一致的,可以将事务的隔离级别修改repeatable read;

幻影读实验分析(mysql8.0 )

事务A事务B
设置事务隔离级别set session transaction isolation level repeatable read;设置事务隔离级别set session transaction isolation level repeatable read;
设置事务手动提交set autocommit=0设置事务手动提交set autocommit=0
开启事务begin开启事务begin
执行查询select * from regions;执行写入操作delete from regions where region_id=5;commit;
执行查询select * from regions;在这里依旧可以读取到事务B删除的记录,那对这个数据的读取就是幻读.commit;
对幻影读问题,可以修改事务的隔离级别为serializable.但是,隔离级别越高,事务的并发就会越低.

当事务的隔离级别设置为serializable时,多个更新(insert,update,delete)事务的执行会串行化.