數據庫進階

 


鎖  mysql

  • 目的
    • 解決併發狀況下資源搶奪問題, 維護數據的一致性
    • mysql的鎖雖然開發者能夠手動設置, 但比較影響併發性, 通常會使用樂觀鎖代替( 如Django中到庫存問題)
    • 因爲mysql會自動使用鎖, 因此須要瞭解鎖機制, 以便優化數據庫併發能力
  • 粒度/覆蓋範圍
    • 表級鎖
      • 對整個表鎖定,併發差,資源消耗少
    • 行級鎖
      • 對數據行鎖定,併發好,資源消耗多
    • 不一樣數據庫引擎支持的鎖也不一樣
      • MyISAM(5.5以前默認)支持表級鎖
      • InnoDB支持行級鎖和表級鎖
  • 鎖和事務
    • 不管操做是否在事務中,均可以獲取鎖,只不過在事務中,獲取的鎖只有執行完事務纔會釋放
  • MyISAM
    • 只支持表級鎖
    • 表讀鎖/共享鎖
      • 獲取後,其餘請求能夠讀不能寫
    • 表寫鎖/排它鎖
      • 獲取後,其餘請求即不能夠讀也不能寫
    • 加鎖方式
      • 數據庫自動管理,查詢前給涉及的表添加讀鎖,更新、刪除、修改前給涉及的表加寫鎖
  • InnoDB
    • 支持行級鎖和表級鎖,優先使用行級鎖
    • 行共享鎖
      • 獲取後,其餘事務也能夠獲取目標集的共享鎖,可是不能獲取目標集的排它鎖(排隊等待)
    • 行排它鎖
      • 獲取後,其餘事務既不能獲取目標集的共享鎖,也不能獲取對應的排它鎖
    • 加鎖方式
      • 增刪改必須獲取排它鎖,普通查詢不須要獲取鎖
      • 加鎖查詢
select * from t_user where name = 'xx' lock in share mode 
獲取目標集共享鎖後,執行查詢       
select * from t_user where name = 'xx' for update
獲取目標集排他鎖後,執行查詢
  • 行鎖與讀寫權限
    • 行共享鎖
      • 獲取行共享鎖後,當前事務能夠讀(不影響),不必定能寫(其餘事務也獲取讀鎖,只能等待);其餘事務能夠讀,不能寫
      • 共享鎖容易出現死鎖陷阱
      • 1 #準備數據
         2 create table t_deadlock(
         3    id int not null auto-increment,
         4    name varchar(20),   
         5    type int,
         6    key (type),
         7    primary key (id)      
         8 );
         9 
        10 insert into t_deadlock (name,type) VALUES ('zs',1);
        11 insert into t_deadlock (name,type) VALUES ('ls',2);
        12 insert into t_deadlock (name,type) VALUES ('ww',3);
      •  1 # 需求: 對zs的type作加1操做, 爲防⽌資源搶奪(更新丟失), 設置鎖
         2 --事務1-------------
         3 begin; 
         4 select type from t_deadlock where name='zs' lock in share mode; # 共享鎖
         5 --事務2-------------
         6 begin; 
         7 select type from t_deadlock where name='zs' lock in sharemode; # 共享鎖
         8 --事務1-------------
         9 update t_deadlock set type=2 where name='zs'; # 等待事務2釋放共享鎖
        10 -事務2------------- 
        11 update t_deadlock set type=2 where name='zs'; # 等待事務1釋放共享鎖
        12 # 相互等待, 產生死鎖  
      • 1 #更新丟失的解決方法:
        2 1.使用update子查詢更新(樂觀鎖)
        3 update t_deadlock set type=type+1 where name='zs';
        4 2.查詢時直接使用排它鎖(悲觀鎖)
        5 select type from t_deadlock where name='zs' for update;
    • 行排他鎖
      • 獲取後,當前事務既能夠讀,也能夠寫;其餘事務能夠讀,不能寫
      •  1  # 需求: 記錄的數量=3,才插入一條數據
         2 --事務1-------------
         3 begin; 
         4 select count(*) from t_deadlock; # 獲取記錄數量爲3 
         5 --事務2-------------
         6 begin; 
         7 select count(*) from t_deadlock; # 獲取記錄數量爲3 
         8 --事務1-------------
         9 insert into t_deadlock (name, type) values ('zl', 1);
        10 commit; # 插入成功
        11 --事務2-------------
        12 insert into t_deadlock (name, type) values ('fq', 1);
        13 commit; # 插入成功, 結果插入了兩條數據

         

         1 # 併發插⼊的解決辦法: insert後邊不能直接鏈接where, 而且insert只鎖對應的行,
         2 不鎖表, 不會影響併發的插入操做(沒法使用樂觀鎖完成需求), 只能在查詢時就手動設置
         3 排它鎖(悲觀鎖)
         4 --事務1-------------
         5 begin; 
         6 select count(*) from t_deadlock for update; # 獲取記錄數量爲3 
         7 --事務2-------------
         8 begin; 
         9 select count(*) from t_deadlock for update; # 等待獲取排它鎖
        10 --事務1-------------
        11 insert into t_deadlock (name, type) values ('zl', 1)
        12 commit; # 插入成功
        13 --事務2-------------
        14 select count(*) from t_deadlock for update; # 事務1完成, 獲取到記錄數量爲4, 再也不執行插入操做

         

    • 行鎖是經過給索引加鎖實現的,若是查詢時沒有觸發索引,就會鎖表
      • 合理的索引很重要
      • 使用RC級別,只鎖行,不鎖表
  • 間隙鎖
    • 在擊中索引的狀況下, 獲取行鎖時, InnoDB不只會對符合條件的已有數據行加鎖(record lock),
      對於鍵值在條件範圍內但並不存在的記錄,叫作「間隙(GAP)」,InnoDB也會對這個「間隙」加
      鎖( gap lock)
    • InnoDB完整的行鎖機制爲next key lock(下鍵鎖) = record lock + gap lock
    • 缺點
      • 會阻塞符合條件的插入操做
      • 1 #gap鎖場景1:使用範圍條件
        2 
        3 begin; #事務1
        4 select * from t_user where age<30 for update;
        5 #若是此時事務2插入記錄(age<30),則會阻塞 (age不是索引觸發表鎖,age是索引觸發的是間隙鎖)
        6 
        7 #gap鎖場景2:鎖定索引的先後區間 [prev,next)
        8 update t_user set name='lisi' where age=30;
        9 #若是age爲索引,且數據中最接近age=30的值爲20和40,則age=[20,40)的範圍也會被鎖定
    • 目的
      • 防止幻讀
    • 解決辦法
      • 儘可能不要對有頻繁插入的表進行範圍條件的檢索
      • 使用RC級別(不存在間隙鎖)
      • 使用惟一索引/主鍵索引進行查詢(間隙鎖只會對普通索引生成)
      •  1 #查看隔離級別
         2 select @@global.tx_isolation, @@session.tx_isolation;
         3 
         4 #設置隔離級別(重啓後會重置)
         5 SET [SESSION|GLOBAL] TRANSACTION ISOLATION LEVEL [READ
         6 UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]
         7 
         8 #修改配置文件設置隔離級別(重啓不重置)
         9 [mysqld]
        10 transaction-isolation = READ-COMMITTED
相關文章
相關標籤/搜索