数据库并发时出现的几个现象(脏读,不可重复读,幻读)

    在数据库并发执行时,会出现多个并发操作同一条数据或者同一个表的情况,有些操作会造成逻辑上的数据矛盾。例如:两个会话连接同一数据库,操作同一条数据。第一步会话一查询第一条数据的结果为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;

    幻读和不可重复读的问题道理是相同的,幻读是在多次查询的之间插入了数据,导致前后查询的数据量不一样。

你可能感兴趣的:(数据库并发时出现的几个现象(脏读,不可重复读,幻读))