zsdnishishui的博客

唯有创造才是“真”

数据库事务

1.事务:对数据库的一系列指令的操作,这个整体叫做事务。细分成ACID这四个特性。

原子性:强调的是一个整体,要不全部成功,要不全部失败。

一致性:说的不是数据一致性,而是完整约束性。

隔离性:并发的两个事务应该不能相互影响。

持久性:对数据库的操作,最终要持久化硬盘中。

2.但是我们一般都关注的是隔离性。隔离性的愿望是美好的,实际上隔离性分成了四个级别:串行化、可重复读、提交读、未提交读。

一旦涉及到并发,为了不相互影响,一定会加入锁机制,加入锁之后又会导致性能的下降。根据加锁的程度不同,才分了四个级别。这里的事务锁分为:读锁、写锁、范围锁。

可重复读的隔离级别只有读锁、写锁保持到事务结束,不要求范围锁,所以会出现幻读。

提交读:只有写锁保持到事务结束,也不要求范围锁,所以会出现不可重复读、幻读。

未提交读:没有锁保持到事务结束,所以会出现脏读、不可重复读、幻读。

其中,脏读是最难发生的,因为需要事务回滚到之前的状态,才会出现脏读。mysql默认的是可重复读的隔离级别,是有可能发生幻读的。但是如果上来就生成了快照读,就不会发生幻读了。pg的默认隔离级别是提交读。

下面就生成了快照读,导致没有复现幻读

窗口1

set autocommit = 0;
-- 设置事务隔离级别为不可重复读
set session transaction isolation level REPEATABLE READ;
begin;
select * from user_money where id BETWEEN 3 AND 5;

DO SLEEP(10);

select * from user_money where id BETWEEN 3 AND 5;
commit;

窗口2

set autocommit = 0;
begin;
INSERT INTO `user_money`(`id`, `user_id`, `user_name`, `money`, `email`) VALUES (4, 10, '809898', 100, '128004@156.com');
commit;

如果窗口1改成下面这样,就会发生幻读

set autocommit = 0;
-- 设置事务隔离级别为不可重复读
set session transaction isolation level REPEATABLE READ;
begin;
select * from user_money where id BETWEEN 3 AND 5;

DO SLEEP(10);
update user_money set money = 8963 where id = 4; //这一句是关键,致使快照读失效

select * from user_money where id BETWEEN 3 AND 5;
commit;

3.@Transactional

能够保证多个select语句的读的一致性,也就是可重复读。

@Transactional(readOnly = true)

加上这种只读事务的话,就不允许有写的操作了,切记。

4.mysql有表锁和行锁,记录一下。

表锁:全表扫描,没有命中索引。

行锁:命中索引,就会发生行锁。

可参考:加耀:深入理解数据库行锁与表锁

目录