欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[转]MySQL中的隔离级别和悲观锁及乐观锁

程序员文章站 2022-07-11 16:34:24
...

 

一、事务支持

    MySQL的事务支持不是绑定在MySQL服务器本身,而是与存储引擎相关。

    1. MyISAM 不支持事务,用于只读程序提高性能;

    2. InnoDB 支持ACID事务,行级锁、并发;

    3. Berkeley DB 支持事务。

 

二、隔离级别

    隔离级别决定了一个session中的事务可能对另一个session的影响、并发session对数据库的操作、一个session中所见数据的一致性。

    ANSI准定4个隔离级别MySQLInnoDB都支持:

    1. READ-UNCOMMITTED:最低级别的隔离,通常又称dirty read,它允一个事务读没commit的数据,这样可能会提高性能,但是dirty read可能不是我想要的。

    2. READ-COMMITTED:在一个事中只允commit记录,如果session中select查询中,另一session此insert一条记录新添加的数据不可见。

    3. REPEATABLE-READ:在一个事开始后,其他session数据的修改在本事中不可,直到本事commit或rollback。在一个事中重复select的果一,除非本事中update数据

    4. SERIALIZABLE:最高级别的隔离,只允串行行。了达到此目的,数据住每行已经读取的记录,其他session不能修改数据直到前一事务结束,事commit或取消

 

    MySQL 隔离级别设置:

# 设置
SET TRANSACTION ISOLATION LEVEL {READ-UNCOMMITTED | READ-COMMITTED | REPEATABLE-READ | SERIALIZABLE} 
# 查看
SELECT @@tx_isolation

    MySQL的隔离级别REPEATABLE READ,在置隔离级别为READ UNCOMMITTEDSERIALIZABLE要小心,READ UNCOMMITTED致数据完整性的问题,而SERIALIZABLE致性能问题并增加死的机率。

 

三、悲观锁及乐观锁

    悲观锁:在读取数据时锁住那几行,其他对这几行的更新需要等到悲观锁结束时才能继续;
    乐观所:读取数据时不锁,更新时检查是否数据已经被更新过,如果是则取消当前更新

    一般在悲观锁的等待时间过长而不能接受才会选择乐观锁 。两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。

    悲观锁示例:

CREATE PROCEDURE tfer_funds   
       (from_account INT, to_account INT,tfer_amount NUMERIC(10,2),   
        OUT status INT, OUT message VARCHAR(30))   
BEGIN   
    DECLARE from_account_balance NUMERIC(10,2);   
  
    START TRANSACTION;   
  
    SELECT balance   
      INTO from_account_balance   
      FROM account_balance   
     WHERE account_id=from_account   
       FOR UPDATE;   
  
    IF from_account_balance>=tfer_amount THEN   
  
         UPDATE account_balance   
            SET balance=balance-tfer_amount   
          WHERE account_id=from_account;   
  
         UPDATE account_balance   
            SET balance=balance+tfer_amount   
          WHERE account_id=to_account;   
         COMMIT;   
  
         SET status=0;   
         SET message='OK';   
    ELSE   
         ROLLBACK;   
         SET status=-1;   
         SET message='Insufficient funds';   
    END IF;   
END;  

     乐观锁示例:

CREATE PROCEDURE tfer_funds   
    (from_account INT, to_account INT, tfer_amount NUMERIC(10,2),   
        OUT status INT, OUT message VARCHAR(30) )   
BEGIN   
    DECLARE from_account_balance    NUMERIC(8,2);   
    DECLARE from_account_balance2   NUMERIC(8,2);   
    DECLARE from_account_timestamp1 TIMESTAMP;   
    DECLARE from_account_timestamp2 TIMESTAMP;   
  
    SELECT account_timestamp,balance   
        INTO from_account_timestamp1,from_account_balance   
            FROM account_balance   
            WHERE account_id=from_account;   
  
    IF (from_account_balance>=tfer_amount) THEN   
  
        -- Here we perform some long running validation that   
        -- might take a few minutes */   
        CALL long_running_validation(from_account);   
  
        START TRANSACTION;   
  
        -- Make sure the account row has not been updated since   
        -- our initial check   
        SELECT account_timestamp, balance   
            INTO from_account_timestamp2,from_account_balance2   
            FROM account_balance   
            WHERE account_id=from_account   
            FOR UPDATE;   
  
        IF (from_account_timestamp1 <> from_account_timestamp2 OR   
            from_account_balance    <> from_account_balance2)  THEN   
            ROLLBACK;   
            SET status=-1;   
            SET message=CONCAT("Transaction cancelled due to concurrent update",   
                " of account"  ,from_account);   
        ELSE   
            UPDATE account_balance   
                SET balance=balance-tfer_amount   
                WHERE account_id=from_account;   
  
            UPDATE account_balance   
                SET balance=balance+tfer_amount   
                WHERE account_id=to_account;   
  
            COMMIT;   
  
            SET status=0;   
            SET message="OK";   
        END IF;   
  
    ELSE   
        ROLLBACK;   
        SET status=-1;   
        SET message="Insufficient funds";   
    END IF;   
END$$  

 

参考文章:http://blog.csdn.net/zztfj/article/details/6319740