重新认识Database-MySQL④--事务原理

66 阅读4分钟

阳后的第三次奔跑,感觉下降不少.继续努力;2023年工作加油,继续保持输出文档.奔跑也不能断,需要保持.向阳而立.

事务

一组数据要么成功,要么失败.保证数据最终一致性.

事务特性

事务通常具有4个属性(简称ACID)

  1. 原子性(Atomicity): 事务操作要么成功,要么失败.主要是通过undo log日志来实现该特性.
  2. 一致性(Consistent): 事务的最终目标,通过AID以及业务代码逻辑实现该特性.
  3. 隔离性(Isolation): 事务并发过程中不相互干扰.由MySQL锁和MVCC机制来实现该特性.
  4. 持久性(Durable): 事务一旦提交,改变就是永久性.由redo log日志实现该特性.

事务处理的问题

更新丢失或脏写

多个事务修改相同数据及最后事务修改了其他事务的更新操作.

脏读

事务1读到事务2已经修改但未提交的数据.

不可重复读

事务1内部相同查询在不同时间段结果不一致.

幻读

事务1读到事务2提交的新数据.

脏读与幻读的区别: 脏读是修改数据,幻读是新增数据

事务隔离级别

脏读,不可重复读,幻读.由数据库的事务隔离机制来解决.

隔离级别脏读不可重复读幻读
读未提交(Read uncommitted)可能可能可能
读已提交(Read committed)不可能可能可能
可重复读(Repeatableread)不可能不可能可能
读已提交(Read committed)不可能不可能不可能

数据库的隔离级别,从上到下越来越严格.同理越严格越限制数据库的并发要求,但同时许多应用可能更关系系统的并发访问能力,不可重复读或幻读并非很关注. 查看当前数据库的隔离级别: show variables like 'tx_ioslation';

image.png

MySQL 事务默认是可重复读.(spring开发如果定义了isolation,就已定义的为准,否则就是数据库默认的隔离级别)

隔离验证

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL,
  `password` varchar(500) NOT NULL,
  `age` tinyint(1) NOT NULL,
  PRIMARY KEY (`id`,`username`),
  UNIQUE KEY `idx_name` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

INSERT INTO `users` (`id`, `username`, `password`, `age`) VALUES ('1', 'zhangsan', '123', '1');
INSERT INTO `users` (`id`, `username`, `password`, `age`) VALUES ('2', 'lisi', '1234', '4');
INSERT INTO `users` (`id`, `username`, `password`, `age`) VALUES ('3', 'wangwu', '12345', '5');

读未提交

两个编辑器操作都设置为read-uncommitted隔离级别 其中操作A修改年龄age=3,但未提交

-- 读未提交,修改年龄age=3
set tx_isolation='read-uncommitted';
BEGIN;
update users SET age = 3 WHERE id = 1;
COMMIT;

image.png

另一个操作B直接查询,就查询到了未提交的数据

-- 读未提交
set tx_isolation='read-uncommitted';
BEGIN;
SELECT * FROM users;
COMMIT;

image.png

但此时,如若操作A回滚,则所有操作都会撤销.操作B查询的数据就是脏数据.解决问题的方式就需要更新隔离级别为读已提交

读已提交

两操作同时设置读已提交 read-committed

操作A查询
-- 读已提交
set tx_isolation='read-committed';
BEGIN;
SELECT * FROM users;
COMMIT;

image.png

操作B修改

但数据未提交

image.png

再次执行操作A,数据未变化.由于B未提交数据,故未查询到数据.解决脏读问题

image.png

数据B提交 再进行操作A查询会产生两次一样的查询,可结果不一致.出现不可重复读问题

可重复读

可重复读同上操作. 语句设置如下:

-- 可重复读
set tx_isolation='repeatable-read';
begin;
select * from users;
commit;

结果: 两者操作互不影响.可重复读隔离级别使用MVCC(Multi-version concurrency control)多版本并发控制,select 查询的是读取快照数据;insert update delete 当前读.

查询是否需要事务?

1:单条SQL查询不需要。

2:如果需要执行多条sql查询来合成最终的统计结果,就可以利用事务隔离级别保证数据查询的一致性。(如若不需要已执行,同1)

结果:建议多条sql查询(针对可重复读RR),设置只读事务(readOnly=true)还是有必要的