• 再谈事务


    上一篇咱们简单了解了事务,这一篇我们再深入一下吧

    Ⅰ、事务隔离级别

    事务一共有四种隔离级别

    简称 全称 -
    ru read-uncommited 未提交读
    rc read-committed 提交读
    rr repeatable-read 可重复读
    srz serializable 可串行

    从真正意义上来看只有srz达到真正隔离性的要求

    oracel、sqlserver默认rc,mysql默认rr(99.99%达到隔离性要求)

    事务隔离级别越低,事务请求的锁越少或者保持锁的时间就越短

    1.1 四种隔离级别解读

    事务隔离级别解决了三个问题,脏读,不可重复读,幻读

    ①read-uncommitted

    可以读到其他线程未提交的数据,没人用,这就是脏读

    ②read-committed

    解决了脏读,不会读到其他线程未提交的数据

    但是a线程事务开始没提交,b线程读不到对应的数据,a线程事务提交后,b读到了

    这就是不可重复读,两次读到不一样,破坏了隔离性,一个线程的事务所做的修改被另一个线程可见了

    ③repeatable-read

    解决不可重复读,一边提交了,另一边还是看不到,两次读结果一样

    重复读还可以解决一个问题,幻读(读到之前不存在的记录,讲锁的时候再演示)

    例外情况:

    • for update锁定读就可以读到第一个线程中事务提交的数据,可以说是幻读,也可以说是一个不可重复读,这就是99.999%,哈哈
    • 这里也是一种不符合隔离性的,读到了之前不存在的记录,只要是支持mvcc就很难做到完全隔离性

    ④serializable

    两阶段加锁可串行化保证隔离性:加锁阶段,只加锁不放锁,解锁阶段,只放锁,不解锁

    对于所有的写,每行上面都有锁,会有大量的锁竞争和超时

    这样就失去了MVCC特性(非锁定一致性读)

    所有操作都要加共享锁,lock in share mode ,执行selct * from xxx;

    其实重写为select * from xxx lock in share mode; 对这条记录加共享锁,

    强制事务排序,使得不会互相冲突,这样就不存在并发问题,都是串行了,读写相互阻塞

    1.2 好奇心,我们对比下rr和sr

    RR隔离级别
    sesion1:
    mysql> set tx_isolation="REPEATABLE-READ";
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> create table t_lock(a int, b int, primary key(a));
    Query OK, 0 rows affected (0.11 sec)
    
    mysql> insert into t_lock values(1,1);
    Query OK, 1 row affected (0.00 sec)
    
    mysql> select * from t_lock;
    +---+------+
    | a | b    |
    +---+------+
    | 1 |    1 |
    +---+------+
    1 row in set (0.00 sec)
    
    mysql> begin;
    Query OK, 0 rows affected (0.02 sec)
    
    mysql> select b from t_lock where a=1;
    +------+
    | b    |
    +------+
    |    1 |
    +------+
    1 row in set (0.00 sec)
    
    session2:
    mysql> set tx_isolation="REPEATABLE-READ";
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> begin;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> update  t_lock set b=2 where a = 1;
    Query OK, 1 row affected (0.00 sec) Rows matched: 1  Changed: 1  Warnings: 0
    
    mysql> commit;
    Query OK, 0 rows affected (0.03 sec)
    
    session1:
    mysql> select b from t_lock where a=1;  -- 再执行一次,得到的结果是1
    +------+
    | b    |
    +------+
    |    1 |
    +------+
    1 row in set (0.00 sec)
    
    mysql> select b from t_lock where a=1 for update;  -- for update的去读,得到的结果是2
    +------+
    | b    |
    +------+
    |    2 |
    +------+ 1 row in set (0.00 sec)
    session1中, RR隔离级别下,前两次读都是读取的快照,最后一次读取的当前更新的值
    
    
    SR隔离级别
    session1:
    mysql> select * from t_lock;
    +---+------+
    | a | b    |
    +---+------+
    | 1 |    1 |
    +---+------+
    1 row in set (0.00 sec)
    
    mysql> set tx_isolation='SERIALIZABLE';
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> begin;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select b from t_lock where a=1;
    +------+
    | b    |
    +------+
    |    1 |
    +------+ 1
    row in set (0.00 sec)
    
    session2:
    mysql> show engine innodb statusG
    -- ------------省略部分输出------------
    2 lock struct(s), heap size 360, 1 row lock(s)
    MySQL thread id 3, OS thread handle 0x7f946bc94700, query id 30 localhost root cleaning up
    TABLE LOCK table `burn_test`.`t_lock` trx id 5390 lock mode IS
    RECORD LOCKS space id 15 page no 3 n bits 72 index `PRIMARY` of table `burn_test`.`t_lock` trx id 5390 lock mode S locks rec but not gap  -- 有S锁
    Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
    0: len 4; hex 80000001; asc     ;;
    1: len 6; hex 00000000150c; asc       ;;
    2: len 7; hex 8c000000340110; asc     4  ;;
    3: len 4; hex 80000001; asc     ;;
    
    mysql> set tx_isolation='SERIALIZABLE';
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> begin;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> update t_lock set b=2 where a=1;  -- 在SR的隔离级别下,直接阻塞,因为a=1上有一个S锁
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    
    SERIALIZABLE的隔离级别一般来说是不会去用
    

    1.3 事务隔离级别的选择

    show variables like 'tx_isolation';
    set global 后只对之后创建的session生效,之前的session没用
    

    如何查看当前各个线程的事务隔离级别,sys库里面的session表看不了啊?

    应该看performance_schema中user_variables_by_thread表

    select * from user_variables_by_thread where variable_name = 'tx_isolation';
    找到thread_id
    select processlist_id from threads where thread_id in(xxx);
    再对应到processlist id
    

    重点:

    • 我们生产中一定要用rc,不要用默认的rr,一般场景rc性能会好很多,rc的锁好理解
    • 为了同步数据一致性binlog_format用row,mixed会遇到各种bug导致主从不一致,5.7官方默认用row了,没有协商的余地,组复制也必须用row(目前MySQL若启用rc,不管binlog_format怎么设置都会强行转为row)
    transaction_isolation = read-committed
    binlog_format = row
    上面两个一定要配到my.cnf中,没有任何理由
    
    • 有一种情况rr比rc好,条件是,应用偏读,一个事务里面有10个select,后面会提到,read_view的原因
  • 相关阅读:
    继承与多态
    String作业
    课后作业及动手动脑
    作业3
    动手动脑
    实验任务四
    动手动脑
    课堂练习01
    《大道至简》第一章读后感伪代码
    C++常用编程关键字
  • 原文地址:https://www.cnblogs.com/---wunian/p/9064205.html
Copyright © 2020-2023  润新知