在数据库并发执行时,会出现多个并发操作同一条数据或者同一个表的情况,有些操作会造成逻辑上的数据矛盾。例如:两个会话连接同一数据库,操作同一条数据。第一步会话一查询第一条数据的结果为20,第二步会话二更新同一条数据结果为30,但未提交事务,第三步,会话一又一次查询第一条数据,发现结果变为了30。第四步,会话二进行了事务回滚,第五步,会话一再一次查询第一条数据结果又为30。(我们在数据库执行时会发现第三步的结果也为20,是因为数据库已经对并发执行可能出现的现象进行了相应的处理,既只能查询到已提交的数据)会话一由于会话二的影响在相同查询条件下出现了不同的查询结果,这就是并发可能出现的情况之一。
(一) 数据库并发出现的现象有:
1.脏读
读未提交。一个事物读取了另一个事物改写但是并未提交的数据,如果另一个事物之后又进行了回滚操作。
2.不可重复读
一个事物进行相同的条件的查询连续俩次或者是两次以上,每次结果都不同。有其他事物做了update操作(已提交);
3.幻读
和(2)很像,其他事务做了insert操作.
(二)对于数据库并发出现的这些现象,数据库采用隔离的形式,这时就有了数据库的隔离级别。
1.隔离级别
1 //read uncommitted ,读未提交
2 //read committed ,读已提交
4 //repeatable read ,可以重复读
8 //serializable ,串行化(悲观锁)(相反对应乐观锁)
数据库的事物级别有4种,不同的数据库采用的策略也不同。常说的1,2,4,8是分别对应4种状态的JAVAJDBC的常量值。其实是4位二进制的表达式转化而来的值。
2.演示mysql事务隔离级别
------------------------
(1).开启mysql客户端
mysql>
(2).关闭自动提交
mysql>set autocommit 0 ;
(3).每次操作数据,都要开启事务,提交事务。
每种数据库都会有默认的数据库隔离级别。比如MySQL采用的默认隔离级别为2。有的伙伴在测试脏读等问题时会发现并不会出现并发造成的问题,因为数据库的隔离级别已经设置了。为了测试,我们需要自己再设置默认级别。
3.脏读现象
----------------
[A]
1)mysql>start transaction ; -- 开始事务
2)msyql>update users set age = age + 1 where id = 1 ; -- 更新数据,没有提交
6)mysql>rollback ; -- 回滚
7)mysql>select * from users ;
[B]
3)mysql>set session transaction isolation level read uncommitted ; -- 读未提交
4)msyql>start transaction ; -- 开始事务
5)mysql>select * from users ; -- 13
脏读现象就是某个事物在查询数据时,另一个事物对其要查询的数据进行update,但是没有提交,在查询结束后,另一个事物又将update的操作进行了回滚,导致数据库的数据和查询的数据不一样。这就是读到了别人没有提交的数据。
4.避免脏读
----------------
[A]
1)mysql>start transaction ; -- 开始事务
2)msyql>update users set age = age + 1 where id = 1 ; -- 更新数据,没有提交
6)mysql>rollback ; -- 回滚
7)mysql>select * from users ;
[B]
3)mysql>set session transaction isolation level read committed ; -- 读已提交
4)msyql>start transaction ; -- 开始事务
5)mysql>select * from users ; -- 13
脏读产生的原因是读到了别人没有提交的update的数据,解决办法是设置隔离级别为2,只能读取别人提交的数据,没提交的数据不能读到。一般的像在一个事物中update 数据,然后再查询,会查询到update的数据,但是换一个会话,就不会查询到update的数据了。
5.测试不可重复读(隔离级别设置为读已提交不能避免不可重复读。)
------------------
[A]
1)mysql>commit ;
2)mysql>set session transaction isolation level read committed ; -- 读已提交
3)mysql>start transaction ; -- 开始事务
4)mysql>select * from users ; -- 查询
9)mysql>select * from users ;
[B]
5)mysql>commit;
6)mysql>start transaction ;
7)mysql>update users set age = 15 where id = 1 ; -- 更新
8)mysql>commit;
不可重复读的现象是不能讨论其好坏的,需要根据具体的业务场景来确定。不可重复读:两次或者两次以上的查询出现不同的结果,原因是因为在两次查询之间有其他事物update并提交相关数据。但是这是合理的逻辑,因为数据已经被修改。但是可能存在特殊场景需要即使修改了,但在同一事物中多次查询的结果需要是一样的。
6.测试避免不可重复读(隔离级别设置为读已提交不能避免不可重复读。)
------------------
[A]
1)mysql>commit ;
2)mysql>set session transaction isolation level repeatable read ; -- 可以重复读
3)mysql>start transaction ; -- 开始事务
4)mysql>select * from users ; -- 查询
9)mysql>select * from users ;
[B]
5)mysql>commit;
6)mysql>start transaction ;
7)mysql>update users set age = 15 where id = 1 ; -- 更新
8)mysql>commit;
想要可以重复读,就需要隔离级别设置为4,在多次查询中的结果是一样的。
7.测试幻读(隔离级别设置为repeatable)
------------------
[A]
1)mysql>commit ;
2)mysql>set session transaction isolation level serializable; -- 串行化
3)mysql>start transaction ; -- 开始事务
4)mysql>select * from users ; -- 查询
9)mysql>select * from users ;
[B]
5)mysql>commit;
6)mysql>start transaction ;
7)mysql>insert into users(name,age) values('tomas',13); -- 更新
8)mysql>commit;
幻读和不可重复读的问题道理是相同的,幻读是在多次查询的之间插入了数据,导致前后查询的数据量不一样。