zoukankan      html  css  js  c++  java
  • MYSQL 锁

    锁的类型

    共享锁(S)

    共享锁可以被多个事务持有,事务T1获取了行r的共享锁,那么事务T2也可以获得行r的共享锁,但是获取不到事务T1的排它锁

    显示的加共享锁:select * from xx lock in share mode;

    排它锁(X)

    排他锁只能被一个事务持有,事务T1持有了行r的排它锁,那么事务T2获取不到行r的排它锁,也获取不到行r的共享锁

    显式的加排它锁:select * from xx for update;

     

    S

    X

    S

    兼容

    不兼容

    X

    不兼容

    不兼容

    创建一张测试表a,id是主键,a2为唯一索引,a3为普通所以

    CREATE TABLE `a` (
      `id` int(11) NOT NULL AUTO_INCREMENT ,
      a1 int(11) NOT NULL ,
      a2 int(11) not NULL,
      a3 int(11) not null,
      PRIMARY KEY (`id`),
      unique key uniq_a2(a2),
      index idx_a3(a3)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
    insert into a(a1,a2,a3) values(1,2,3),(2,4,6),(3,6,12);

    事务1获取共享锁,事务2获取排它锁,事务2将获取失败

    事务1

    事务2

    start TRANSACTION;

    select * from a where id=2 lock in share mode;

     

    获取S锁成功

     
     

    start TRANSACTION;

    select * from a where id=2 for update;

     

    等待...

     

    获取X锁超时(Lock wait timeout exceeded; try restarting transaction)

    commit;

     
     

    start TRANSACTION;

    select * from a where id=2 for update;

       

    在事务2等待锁过程中执行 select * from information_schema.innodb_trx; 查看锁当前状态,可以看到事务1处于RUNNING状态,事务2处于LOCK WAIT状态

    其中trx_weight表示事务的权重,反应了一个事务修改和锁住的行数。在INNODB中,当发生死锁需要回滚时,InnoDB会选择该值最小的进行回滚,因为这个值越小需要进行回滚操作的数据越少

    在事务2等待锁过程中执行 select * from information_schema.innodb_locks 查看锁信息,lock_data表示事务锁定的主键的值,由于查询条件是id=2,因此lock_data=2,由于两个事务对同一行记录分别申请S所和X锁,因此会发生等待

    lock_rec 事务锁定行的数量(明明是按主键锁主了一条记录,为什么这里的lock_rec是3)

    lock_page 事务锁定页的数量

     

     

    使用 select * from information_schema.innodb_lock_waits; 查看信息,可以看到那些事务获取到了锁,那些事务在阻塞中

    一致性非阻塞读(consistent nonblocking read)

    一致性非阻塞读表示存储引擎通过MVCC方式读取当前时间数据库中的数据,如果正在读取的行正在执行delete或者update操作,读操作被会被阻塞,而是会读取该行记录的快照数据。

    在事务隔离级别为READ COMMITTED 和 REPEATABLE READ 下,InnoDB采用非阻塞读方式。但是在READ COMMITTED隔离级别下,读取到的快照数据是最新的一个快照数据(不可重复读),而在REPEATABLE READ模式下,读取到的快照数据总是在事务开始时读取到的快照数据(可重复读)

    REPEATABLE READ隔离级别下,在同一个事务内,读取到的数据总是一致的(因为msyql默认的隔离级别是RR,因此我们使用RR测试)

    会话A

    会话B

    start TRANSACTION;

     

    select * from a where id=1;(a1=1)

     
     

    start TRANSACTION;

     

    select * from a where id=1; (a1=10)

    select * from a where id=1;(a1=1)

     
     

    commit;

    select * from a where id=1;(a1=1)

     

    commit;

     

    select * from a where id=1;(a1=10)

     

    在READ COMMITTED 隔离级别下,在事务内总是读取到最新的快照,也就是可以读取到别的事务提交的数据。(需要手动设置隔离级别为RC,同时将列a1的值重置为1)

    会话A

    会话B

    start TRANSACTION;

     

    select * from a where id=1;(a1=1)

     
     

    start TRANSACTION;

     

    select * from a where id=1; (a1=10)

    select * from a where id=1;(a1=1)

     
     

    commit;

    select * from a where id=1;(a1=10)

     

    commit;

     

    select * from a where id=1;(a1=10)

     

    一致性阻塞读

    行锁的3种算法

    Record Lock: 单行记录上的锁,锁住的是索引记录,如果表没有任何创建索引,那么InnoDB默认使用隐式的主键进行锁定

    Gap Lock:间隙锁,锁定一个范围,不包含记录本身

    Next-Key Lock:Gap+Record Lock,锁定一个范围,包括记录本身

    如果采用Next-Key Lock算法,那么针对表a中的列a3(普通索引,值分为为 3,6,12),那么可能被Next-Key Locking的区间为:

    (-∞, 3], (3,6], (6,12], (12,+∞)

    采用Next-Key Lock的锁定技术称为Next-Key Locking,目的是为了解决幻读问题(Phantom Problem),除了next-key locking ,还有previous-key locking 技术,如果采用previous-key locking,那么上述a3可锁定的区间为:

    (-∞, 3), [3,6), [6,12), [12,+∞)

    当查询的索引是唯一索引时,InnoDB会对Next-Key Locking进行优化,降级为Record Lock,值锁住索引本身,而不是范围

    以下针对列a2进行测试,a2为唯一索引,a2的值为 2,4,6

    在Next-Key Lock算法下,可锁住的区间为(-∞, 2], (2,4], (4,6], (6,+∞),5和3处于索引范围内,会话B获取5和3的锁应该要等待,但是由于a2是唯一索引,因此降级为Record Lock,仅锁住a2=4本身,所以会话B不会被阻塞

    会话A

    会话B

    start TRANSACTION;

     

    select * from a where a2=4 for update;

     
     

    start TRANSACTION;

     

    select * from a where a2=5 for update;(成功,不需要等待)

     

    select * from a where a2=3 for update;(成功,不需要等待)

     

    commit;

    commit;

     
       

    看下普通索引在Next-key Lock算法下的情况

    使用表a的列a1测试,a1为普通索引

    采用Next-Key Lock 算法,可锁定的区间为(-∞, 1), (1,2), (2,3),(3,9), (9,+∞),

    会话A

    会话B

    start TRANSACTION;

     

    select * from a where a1=3 for update;

     
     

    start TRANSACTION;

     

    select * from a where a1=5 for update;(获取锁不成功,因为5处于(3,9]之间)

     

    select * from a where a1=2 for update;(获取锁不成功,因为2处于锁定范围内)

     

    select * from a where a1=3 lock in share mode; (获取锁不成功,因为3本身已被锁定)

     

    select * from a where id=1 for update;(获取锁不成功) 为什么1会被锁定

     

    select * from a where id=10 for update;(获取锁成功,因为会话A锁定的范围是[1,9),10不在锁定范围内,因此获取锁成功)为什么锁定范围不是(2,9)

     

    select * from a where id=0 for update;(获取锁成功)

    死锁

    死锁是由于两个或两个以上的事务在执行过程中,因争夺资源而造成的一种相互等待的现象。

    解决死锁最简单的方式是不要有等待,将任何的等待转化为回滚。两个事务相互等待,当其中一个等待时间超过设置的某一个阈值,根据权重,将其中一个事务回滚,另外一个事务就可以正常运行。

    权重在前面提到过(txr_weight),权重的大小取决于事务修改和锁定的行数,更新的行数越小,占用的undo log 也就越少,回滚需要的时间也就越少

    测试下死锁

    会话A

    会话B

    会话A

    会话B

    start TRANSACTION;

     

    delete from a where id=3; (成功)

     
     

    start TRANSACTION;

     

    delete from a where id=2;(成功)

    delete from a where id=2;(等待)

     
     

    delete from a where id=3; (等待)

     

    超时回滚其中一个事务

    show engine innodb status ; 查看死锁日志

     
    =====================================
    2019-07-26 02:02:56 0x70000b18b000 INNODB MONITOR OUTPUT
    =====================================
    Per second averages calculated from the last 15 seconds
    -----------------
    BACKGROUND THREAD
    -----------------
    srv_master_thread loops: 84 srv_active, 0 srv_shutdown, 167877 srv_idle
    srv_master_thread log flush and writes: 167921
    ----------
    SEMAPHORES
    ----------
    OS WAIT ARRAY INFO: reservation count 155
    OS WAIT ARRAY INFO: signal count 154
    RW-shared spins 0, rounds 270, OS waits 133
    RW-excl spins 0, rounds 666, OS waits 6
    RW-sx spins 0, rounds 0, OS waits 0
    Spin rounds per wait: 270.00 RW-shared, 666.00 RW-excl, 0.00 RW-sx
    ------------------------
    LATEST DETECTED DEADLOCK
    ------------------------
    2019-07-26 02:02:42 0x70000b037000
    *** (1) TRANSACTION:
    TRANSACTION 53206, ACTIVE 10 sec starting index read
    mysql tables in use 1, locked 1
    LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
    MySQL thread id 25, OS thread handle 123145487921152, query id 2377 localhost 127.0.0.1 root updating
    delete from a where id=3
    *** (1) WAITING FOR THIS LOCK TO BE GRANTED:
    RECORD LOCKS space id 246 page no 3 n bits 80 index PRIMARY of table `hotel_mint`.`a` trx id 53206 lock_mode X locks rec but not gap waiting
    Record lock, heap no 4 PHYSICAL RECORD: n_fields 6; compact format; info bits 32
     0: len 4; hex 80000003; asc     ;;
     1: len 6; hex 00000000cfd1; asc       ;;
     2: len 7; hex 220000012822e4; asc "   (" ;;
     3: len 4; hex 80000003; asc     ;;
     4: len 4; hex 80000006; asc     ;;
     5: len 4; hex 8000000c; asc     ;;
    
    *** (2) TRANSACTION:
    TRANSACTION 53201, ACTIVE 13 sec starting index read
    mysql tables in use 1, locked 1
    3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
    MySQL thread id 23, OS thread handle 123145487085568, query id 2378 localhost 127.0.0.1 root updating
    delete from a where id=2
    *** (2) HOLDS THE LOCK(S):
    RECORD LOCKS space id 246 page no 3 n bits 80 index PRIMARY of table `hotel_mint`.`a` trx id 53201 lock_mode X locks rec but not gap
    Record lock, heap no 4 PHYSICAL RECORD: n_fields 6; compact format; info bits 32
     0: len 4; hex 80000003; asc     ;;
     1: len 6; hex 00000000cfd1; asc       ;;
     2: len 7; hex 220000012822e4; asc "   (" ;;
     3: len 4; hex 80000003; asc     ;;
     4: len 4; hex 80000006; asc     ;;
     5: len 4; hex 8000000c; asc     ;;
    
    *** (2) WAITING FOR THIS LOCK TO BE GRANTED:
    RECORD LOCKS space id 246 page no 3 n bits 80 index PRIMARY of table `hotel_mint`.`a` trx id 53201 lock_mode X locks rec but not gap waiting
    Record lock, heap no 3 PHYSICAL RECORD: n_fields 6; compact format; info bits 32
     0: len 4; hex 80000002; asc     ;;
     1: len 6; hex 00000000cfd6; asc       ;;
     2: len 7; hex 250000030f0d82; asc %      ;;
     3: len 4; hex 80000002; asc     ;;
     4: len 4; hex 80000004; asc     ;;
     5: len 4; hex 80000006; asc     ;;
    
    *** WE ROLL BACK TRANSACTION (2)
    ------------
    TRANSACTIONS
    ------------
    Trx id counter 53212
    Purge done for trx's n:o < 53212 undo n:o < 0 state: running but idle
    History list length 52
    LIST OF TRANSACTIONS FOR EACH SESSION:
    ---TRANSACTION 281479538886448, not started
    0 lock struct(s), heap size 1136, 0 row lock(s)
    ---TRANSACTION 281479538887352, not started
    0 lock struct(s), heap size 1136, 0 row lock(s)
    ---TRANSACTION 53206, ACTIVE 24 sec
    3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 2
    MySQL thread id 25, OS thread handle 123145487921152, query id 2380 localhost 127.0.0.1 root
    --------
    FILE I/O
    --------
    I/O thread 0 state: waiting for i/o request (insert buffer thread)
    I/O thread 1 state: waiting for i/o request (log thread)
    I/O thread 2 state: waiting for i/o request (read thread)
    I/O thread 3 state: waiting for i/o request (read thread)
    I/O thread 4 state: waiting for i/o request (read thread)
    I/O thread 5 state: waiting for i/o request (read thread)
    I/O thread 6 state: waiting for i/o request (write thread)
    I/O thread 7 state: waiting for i/o request (write thread)
    I/O thread 8 state: waiting for i/o request (write thread)
    I/O thread 9 state: waiting for i/o request (write thread)
    Pending normal aio reads: [0, 0, 0, 0] , aio writes: [0, 0, 0, 0] ,
     ibuf aio reads:, log i/o's:, sync i/o's:
    Pending flushes (fsync) log: 0; buffer pool: 0
    2182 OS file reads, 1151 OS file writes, 505 OS fsyncs
    0.00 reads/s, 0 avg bytes/read, 1.53 writes/s, 0.80 fsyncs/s
    -------------------------------------
    INSERT BUFFER AND ADAPTIVE HASH INDEX
    -------------------------------------
    Ibuf: size 1, free list len 0, seg size 2, 0 merges
    merged operations:
     insert 0, delete mark 0, delete 0
    discarded operations:
     insert 0, delete mark 0, delete 0
    Hash table size 34673, node heap has 0 buffer(s)
    Hash table size 34673, node heap has 0 buffer(s)
    Hash table size 34673, node heap has 0 buffer(s)
    Hash table size 34673, node heap has 0 buffer(s)
    Hash table size 34673, node heap has 1 buffer(s)
    Hash table size 34673, node heap has 0 buffer(s)
    Hash table size 34673, node heap has 0 buffer(s)
    Hash table size 34673, node heap has 0 buffer(s)
    1.53 hash searches/s, 1.13 non-hash searches/s
    ---
    LOG
    ---
    Log sequence number 3022251932
    Log flushed up to   3022251932
    Pages flushed up to 3022251932
    Last checkpoint at  3022251923
    0 pending log flushes, 0 pending chkp writes
    306 log i/o's done, 0.47 log i/o's/second
    ----------------------
    BUFFER POOL AND MEMORY
    ----------------------
    Total large memory allocated 137428992
    Dictionary memory allocated 439113
    Buffer pool size   8191
    Free buffers       5999
    Database pages     2191
    Old database pages 818
    Modified db pages  0
    Pending reads      0
    Pending writes: LRU 0, flush list 0, single page 0
    Pages made young 0, not young 0
    0.00 youngs/s, 0.00 non-youngs/s
    Pages read 2109, created 82, written 769
    0.00 reads/s, 0.07 creates/s, 1.00 writes/s
    Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not 0 / 1000
    Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
    LRU len: 2191, unzip_LRU len: 0
    I/O sum[0]:cur[0], unzip sum[0]:cur[0]
    --------------
    ROW OPERATIONS
    --------------
    0 queries inside InnoDB, 0 queries in queue
    0 read views open inside InnoDB
    Process ID=103, Main thread ID=123145481420800, state: sleeping
    Number of rows inserted 5879, updated 11, deleted 11, read 798826
    0.00 inserts/s, 0.00 updates/s, 0.07 deletes/s, 0.07 reads/s
    ----------------------------
    END OF INNODB MONITOR OUTPUT
    ============================
    View Code

    避免死锁的方法

    1、系统中事务的数量尽量少

    2、避免大事务,事务尽量小

    3、避免大sql,能拆则拆,避免长时间占用资源,导致其他事务等待或者与其他事务冲突发生死锁

     

  • 相关阅读:
    综合练习:词频统计
    Dart SDK 2.0安装问题
    The DartEditor executable launcher was unable to locate its companion shared library.
    pycharm中如何正确配置pyqt5
    发现黑苹果带双显示器无法启动的原因
    Pycharm中用鼠标改变字体大小
    失望的visual studio for mac
    laravel 函数测试 --- Route::has()
    laragon 之Nginx
    laragon 之xdebug
  • 原文地址:https://www.cnblogs.com/tracer-dhy/p/11409483.html
Copyright © 2011-2022 走看看