最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MySQL實現分布式鎖

 更新時間:2022年08月01日 09:55:50   作者:俗世游子???????  
這篇文章主要介紹了MySQL實現分布式鎖,文章圍繞主題展開詳細的內容介紹,具有一定的參考價值,需要的小伙伴可以參考一下

基于MySQL分布式鎖實現原理及代碼

工欲善其事必先利其器,在基于MySQL實現分布式鎖之前,我們要先了解一點MySQL鎖自身的相關內容

MySQL鎖

我們知道:鎖是計算機協調多個進程或者線程并發(fā)訪問同一資源的機制,而在數據庫中,除了傳統(tǒng)的機器資源的爭用之外,存儲下來的數據也屬于供用戶共享的資源,所以如何保證數據并發(fā)的一致性,有效性是每個數據庫必須解決的問題。

除此之外,鎖沖突也是影響數據庫并發(fā)性能的主要因素,所以鎖對于數據庫而言就顯得非常重要,也非常復雜。

存儲引擎是MySQL中非常重要的底層組件,主要用來處理不同類型的SQL操作,其中包括創(chuàng)建,讀取,刪除和修改操作。在MySQL中提供了不同類型的存儲引擎,根據其不同的特性提供了不同的存儲機制,索引和鎖功能。

根據show engines;能夠列出MySQL下支持的存儲引擎

如果沒有特殊指定,那么在MySQL8.0中會設置InnoDB為默認的存儲引擎

在實際工作中,根據需求選擇最多的兩種存儲引擎分別為:

  • InnoDB
  • MyISAM

所以我們主要針對這兩種類型來介紹MySQL的鎖

InnoDB

InnoDB支持多粒度鎖定,可以支持行鎖,也可以支持表鎖。如果沒有升級鎖粒度,那么默認情況下是以行鎖來設計的。

關于行鎖和表鎖的介紹:

  • 行鎖對指定數據進行加鎖,鎖定粒度最小,開銷大,加鎖慢,容易出現死鎖問題,出現鎖沖突的概率最小,并發(fā)性最高
  • 表鎖對整個表進行加鎖,鎖定粒度大,開銷小,加鎖快,不會出現死鎖,出現鎖沖突的概率最大,并發(fā)性最低

這里沒法說明那種鎖最好,只有合適不合適

在行級鎖中,可以分為兩種類型

  • 共享鎖
  • 排他鎖

共享鎖

共享鎖又稱為讀鎖,允許其他事務讀取被鎖定的對象,也可以在其上獲取其他共享鎖,但不能寫入。

舉個例子:

  • 事務T在數據A擁有共享鎖,那么當前事務T對數據A可以讀,但是不能修改。而且事務T2同樣可以對數據A擁有共享鎖,這樣相當于在數據A上分別存在不同事務的共享鎖
  • 數據A擁有了事務T的共享鎖,那么就不能再擁有其他事務的排他鎖

下面是關于共享鎖的具體實現,關鍵代碼:select .. from table lock in share mode

 -- 創(chuàng)建實例表
 create table tb_lock(
     id bigint primary key auto_increment,
     t_name varchar(20)
 ) engine=InnoDB;

開啟兩個窗口來測試:

session1session2
set autocommit=0;set autocommit=0;
select * from tb_lock where t_name = ‘zs’ lock in share mode; 
 select * from tb_lock where t_name = ‘zs’ lock in share mode;
 select * from tb_lock where t_name = ‘lsp’ lock in share mode;
update tb_lock set t_name = ‘lzs’ where t_name = ‘zs’; 
update tb_lock set t_name = ‘lsp111’ where t_name = ‘lsp’; 
 select * from tb_lock where t_name = ‘zs’;
commit; 

自動提交全部關閉,可以通過select @@autocommit;來查看

通過以上實驗,我們總結:

  • 共享鎖基于行鎖處理,不同事務可以在同一條數據上獲取共享鎖
  • 如果多個事務在同一條數據上獲取共享鎖,當想要修改該條數據的時候,會出現阻塞狀態(tài)。直到其他事務將鎖釋放,該能夠繼續(xù)修改

修改,刪除,插入會默認對涉及到的數據加上排他鎖

  • 單純的select操作不會有任何影響,select不會加任何鎖
  • 執(zhí)行commit;自動釋放鎖

排它鎖

又叫寫鎖。只允許獲取鎖的事務對數據進行操作【更新,刪除】,其他事務對相同數據集只能進行讀取,不能有跟新或者刪除操作。而且也不能在相同數據集獲取到共享鎖。

沒錯,就是這么霸道

在MySQL中,想要基于排它鎖實現行級鎖,就需要對表中索引列加鎖,否則的話,排它鎖就屬于表級鎖

下面一一來展示,關鍵代碼:select .. from XX for update

首先是有索引列狀態(tài)

session1session2
set autocommit=0;set autocommit=0;
select * from tb_lock;select * from tb_lock;
select * from tb_lock where id = 1 for update; 
 select * from tb_lock where id = 1 for update;
select * from tb_lock where id = 2 for update; 
commit; 

通過以上實驗,得到結論:

  • 對索引列進行加鎖的鎖定級別為行級鎖,如上所示,當其他事務想要對相同的數據再次加鎖的時候,就會進行到阻塞狀態(tài)。并且如果等待時間過長,會出現如下異常:
 Lock wait timeout exceeded; try restarting transaction
  • 對不同行數據再次加排它鎖,是沒有任何問題的。
  • 對已經上鎖的相同數據做修改和刪除操作不需要多說,因為InnoDB默認會對其加入排它鎖

下面是無索引列狀態(tài)

session1session2
set autocommit=0;set autocommit=0;
select * from tb_lock;select * from tb_lock;
select * from tb_lock where t_name = ‘ls’ for update; 
 select * from tb_lock where t_name = ‘ls’ for update;
commit 

通過以上實驗,得到結論:

  • 對非索引列其中一條數據加入了排它鎖后,在其他事務中對不同數據再次加入排它鎖,進入了阻塞狀態(tài)
  • 說明當加鎖列屬于非索引時,InnoDB會對整個表進行上鎖,進入到表級鎖

接下來我們來看看MyISAM的方式

MyISAM

MyISAM屬于表級鎖,被用來防止任何其他事務訪問表的鎖。

其中表鎖又分為兩種形式

  • 表共享讀鎖: READ
  • 表獨占寫鎖: WRITE

這里我們要注意:表級鎖只能防止其他會話進行不適當的讀取或寫入。

  • 持有WRITE 鎖的會話可以執(zhí)行表級操作,比如DELETE或者TRUNCATE
  • 持有會話READ鎖,不能夠執(zhí)行DELETE或者TRUNCATE操作

表共享讀鎖

不管是READ還是WRITE,都是通過lock table 來獲取表鎖的,而READ鎖擁有如下特性:

  • 持有鎖的會話可以讀取表,但是不能進行寫入操作
  • 多個會話可以同時獲取READ表的鎖,而其他會話可以在不顯式獲取READ鎖的情況下讀取該表:也就是說直接通過select來操作

那么,接下來我們來看實際操作,關鍵代碼:lock tables table_name read

 create table tb_lock_isam(
     id bigint primary key auto_increment,
     t_name varchar(20)
 ) engine=MyISAM;

開啟兩個窗口來進行操作:

session1session2
set autocommit=0;set autocommit=0;
LOCK TABLES tb_lock_isam READ; 
select * from tb_lock_isam; 
select * from tb_lock; 
 select * from tb_lock_isam;
 LOCK TABLES tb_lock_isam READ;
 select * from tb_lock_isam;
 select * from tb_lock;
unlock tables;insert into tb_lock_isam(t_name) values(‘ll’);
  

通過以上實戰(zhàn),驗證以下結論:

  • 在當前事務下,獲取到讀鎖,直接查詢鎖定表是沒有問題的,但是如果想要讀取其他表下的數據,那么就會出現以下異常:因為其他表并沒有LOCK在其中
 Table 'tb_lock' was not locked with LOCK TABLES
  • 事務A獲取到讀鎖之后,在其他事務中是可以正常讀取的,并且也可以再次獲取讀鎖。
  • 在讀鎖中如果想要進行插入操作是不會成功的,出現以下異常:
 Table 'tb_lock_isam' was locked with a READ lock and can't be updated
  • 當前表獲取到讀鎖之后,在當前表沒有釋放讀鎖之前,再獲取寫鎖會一直進入到阻塞狀態(tài)。
  • 可以通過非加鎖方式來讀取數據,但是要注意:一定是在不同的事務下

表獨占寫鎖

WRITE鎖的特性和排它鎖的特性非常相似,都特別霸道:

  • 持有鎖的會話可以讀寫表
  • 只有持有鎖的會話才能訪問該表。在釋放鎖之前,沒有其他會話可以訪問它
  • 其他會話對表的鎖請求在WRITE持有鎖時被阻塞

還是通過具體實戰(zhàn)來進行演示效果,關鍵代碼:lock tables table_name write

session1session2
select * from tb_lock_isam;select * from tb_lock_isam;
lock table tb_lock_isam write; 
select * from tb_lock_isam; 
insert into tb_lock_isam(t_name) values(‘66’); 
 select * from tb_lock_isam;
unlock tables; 

通過以上實戰(zhàn),驗證以下結論:

  • 當事務獲取到當前表的WRITE鎖的時候,在當前事務下可以對獲取鎖的表進行任何操作,其他事務無法對表進行任意操作。
  • 在不同事務下不會對其他表的操作有影響
  • 在當前事務獲取到WRITE鎖之后,只能在當前事務下操作獲取鎖的表,無法操作其他表,否則會出現以下異常
  Table 'tb_index' was not locked with LOCK TABLES'

注意

MyISAM在執(zhí)行查詢語句之前,會自動給涉及的所有表加讀鎖,在執(zhí)行更新操作前,會自動給涉及的表加寫鎖,這個過程并不需要用戶干預,因此用戶一般不需要使用命令來顯式加鎖

分布式鎖實現

既然已經了解到了MySQL鎖相關內容,那么我們就來看看如何實現,首先我們需要創(chuàng)建一張數據表

當然,只需要初始化創(chuàng)建一次

 create table if not exists fud_distribute_lock(
     id bigint unsigned primary key auto_increment,
     biz varchar(50) comment '業(yè)務Key'
     unique(biz)
 ) engine=innodb;

在其中,biz是為了區(qū)分不同的業(yè)務,也可以理解為資源隔離,并且對biz設置唯一索引,也能夠防止其鎖級別變?yōu)楸砑夋i

既然for udpate就是加鎖成功,事務提交就自動釋放鎖,那么這個事情就非常好辦了:

 // 省略了構造方法,需要傳入DataSource和biz
 ?
 private static final String SELECT_SQL = 
     "SELECT * FROM fud_distribute_lock WHERE `biz` = ? for update";
 private static final String INSERT_SQL = 
     "INSERT INTO fud_distribute_lock(`biz`) values(?)";
 ?
 // 從構造方法中傳入
 private final DataSource source;
 private Connection connection;
 ?
 public void lock() {
     PreparedStatement psmt = null;
     ResultSet rs = null;
 ?
     try {
         // while(true); 
         for (; ; ) {
             connection = this.source.getConnection();
             // 關閉自動提交事務
             connection.setAutoCommit(false);
             
             psmt = connection.prepareStatement(SELECT_SQL);
             psmt.setString(1, biz);
             rs = psmt.executeQuery();
             if (rs.next()) {
                 return;
             }
             connection.commit();
             close(connection, psmt, rs);
             // 如果沒有相關查詢,需要插入
             Connection updConnection = this.source.getConnection();
             PreparedStatement insertStatement = null;
             try {
                 insertStatement = updConnection.prepareStatement(INSERT_SQL);
                 insertStatement.setString(1, biz);
                 if (insertStatement.executeUpdate() == 1) {
                     LOGGER.info("創(chuàng)建鎖記錄成功");
                 }
             } catch (Exception e) {
                 LOGGER.error("創(chuàng)建鎖記錄異常:{}", e.getMessage());
             } finally {
                 close(insertStatement, updConnection);
             }
         }
     } catch (Exception e) {
         LOGGER.error("lock異常信息:{}", e.getMessage());
         throw new BusException(e);
     } finally {
         close(psmt, rs);
     }
 }
 ?
 public void unlock() {
     try {
         // 事務提交之后自動解鎖
         connection.commit();
         close(connection);
     } catch (Exception e) {
         LOGGER.error("unlock異常信息:{}", e.getMessage());
         throw new BusException(e);
     }
 }
 ?
 public void close(AutoCloseable... closeables) {
     Arrays.stream(closeables).forEach(closeable -> {
         if (null != closeable) {
             try {
                 closeable.close();
             } catch (Exception e) {
                 LOGGER.error("close關閉異常:{}", e.getMessage());
             }
         }
     });
 }

難點:為什么需要for(;

如果一個請求是第一次進來的,比如biz=order,在這個表中是不會存儲order這條記錄,那么select ...for update就不會生效,所以就需要先將order插入到表記錄中,也就是執(zhí)行insert操作。

insert執(zhí)行成功之后,記錄select...for update,這樣獲取鎖才能生效

總結

基于MySQL的分布式鎖在實際開發(fā)過程中很少使用,但是我們還是要有一個思路在。那么本節(jié)針對MySQL的分布式鎖實現到這里就結束了,掌握了MySQL的基礎鎖,那么就會非常簡單了。

到此這篇關于MySQL實現分布式鎖的文章就介紹到這了,更多相關MySQL分布式鎖內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論

泰顺县| 卫辉市| 威宁| 东莞市| 西安市| 济源市| 扶绥县| 门头沟区| 门头沟区| 衡南县| 隆化县| 富顺县| 临海市| 延川县| 大安市| 大理市| 抚顺县| 绥德县| 凤台县| 浦北县| 陆河县| 佳木斯市| 海城市| 阳西县| 松原市| 上思县| 英吉沙县| 旺苍县| 扎囊县| 新源县| 绥宁县| 雅安市| 鹰潭市| 双柏县| 安化县| 涡阳县| 晴隆县| 威海市| 邢台县| 湛江市| 亚东县|