mysql学习-当前读

115 阅读3分钟
  1. 当前读的定义

    • 在MySQL中,当前读是一种读取数据的方式,它读取的是记录的最新版本。当前读可以保证事务读取到的数据是最新的,不会出现读取到已经过时的数据的情况。这与快照读(读取事务开始时的数据版本)相对应。
  2. 如何进行当前读

    • 使用SELECT... FOR UPDATE语句
      • 例如,在一个银行转账的事务场景中,如果要从账户A转账到账户B,需要先查询账户A的余额并且确保在转账过程中余额不会被其他事务修改。可以使用以下SQL语句:
        START TRANSACTION;
        SELECT balance FROM accounts WHERE account_id = 'A' FOR UPDATE;
        -- 在这里进行余额检查和转账操作
        UPDATE accounts SET balance = balance - transfer_amount WHERE account_id = 'A';
        UPDATE accounts SET balance = balance + transfer_amount WHERE account_id = 'B';
        COMMIT;
        
      • 当执行SELECT... FOR UPDATE语句时,会对查询的记录加排他锁(X锁)。这意味着其他事务不能同时对这些记录进行写操作(如UPDATEDELETE),直到当前事务结束(提交或回滚)。
    • 使用SELECT... LOCK IN SHARE MODE语句
      • 这种方式会对查询的记录加共享锁(S锁)。例如,在一个统计多个账户总余额的事务中,不希望其他事务对这些账户进行修改,但允许其他事务进行查询操作,可以使用如下语句:
        START TRANSACTION;
        SELECT balance FROM accounts WHERE account_id IN ('A', 'B', 'C') LOCK IN SHARE MODE;
        -- 在这里进行余额统计操作
        COMMIT;
        
      • 当一个事务对记录加了共享锁后,其他事务可以对这些记录进行查询(也可以加共享锁进行查询),但不能进行排他的写操作。只有当所有共享锁都释放后,其他事务才能对这些记录进行排他写操作。
  3. 当前读可能存在的问题

    • 死锁问题
      • 当多个事务以不同的顺序对多个记录进行当前读并加锁时,可能会导致死锁。例如,事务1对记录A加排他锁,然后试图对记录B加排他锁;而事务2已经对记录B加了排他锁,又试图对记录A加排他锁。这样两个事务就会相互等待对方释放锁,从而形成死锁。
      • 为了避免死锁,需要合理安排事务的加锁顺序。比如在多个事务需要对多个资源加锁时,规定所有事务都按照相同的顺序(如按照记录的主键顺序)进行加锁。
    • 性能问题
      • 当前读会对记录加锁,这可能会导致并发性能下降。尤其是在高并发的场景下,如果大量事务频繁地进行当前读操作并长时间持有锁,会导致其他事务等待锁的时间过长,从而影响整个系统的吞吐量。
      • 为了缓解性能问题,可以尽量缩短事务的执行时间,减少锁的持有时间。例如,在事务中避免进行复杂的、耗时的操作,如大量的循环计算、远程调用等;或者对数据库进行合理的分区和优化,减少锁冲突的范围。
    • 锁升级问题
      • 在某些情况下,MySQL可能会进行锁升级。例如,当一个事务对大量记录进行共享锁(SELECT... LOCK IN SHARE MODE)操作,然后又试图对其中部分记录进行排他写操作时,MySQL可能会将共享锁升级为排他锁。锁升级过程可能会带来额外的性能开销和锁竞争问题。
      • 可以通过合理的设计事务逻辑,避免不必要的锁升级。比如在事务开始前就明确是进行只读操作还是读写操作,对于只读操作尽量不使用可能导致锁升级的语句。