1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Mysql的鎖你知道多少個

        共 12483字,需瀏覽 25分鐘

         ·

        2021-07-17 17:32


        我們知道,數(shù)據(jù)也是一種供許多用戶共享訪問的資源。如何保證數(shù)據(jù)并發(fā)訪問的一致性、有效性,是所有數(shù)據(jù)庫必須解決的一個問題,鎖的沖突也是影響數(shù)據(jù)庫并發(fā)訪問性能的一個重要因素。從這一角度來說,鎖對于數(shù)據(jù)庫而言就顯得尤為重要。接下來我們了解各種鎖的特征。

        1.1 表鎖

        表級鎖是mysql鎖中粒度最大的一種鎖,表示當前的操作對整張表加鎖,資源開銷比行鎖少,不會出現(xiàn)死鎖的情況,但是發(fā)生鎖沖突的概率很大。該鎖定機制最大的特點是實現(xiàn)邏輯非常簡單,帶來的系統(tǒng)負面影響最小。所以獲取鎖和釋放鎖的速度很快。由于表級鎖一次會將整個表鎖定,所以可以很好地避免困擾我們的死鎖問題。InnoDB支持表級鎖。但是默認的行鎖,而且只有在查詢或者其他SQL語句通過索引才會使用行鎖。

        1.2 行鎖

        行鎖的是mysql鎖中粒度最小的一種鎖,因為鎖的粒度很小,所以發(fā)生資源爭搶的概率也最小,并發(fā)性能最大,但是也會造成死鎖,每次加鎖和釋放鎖的開銷也會變大。目前主要是Innodb使用行鎖。根據(jù)使用方式也分為共享鎖(S鎖或者讀鎖)和排它鎖(X鎖或者寫鎖)。

        1.3共享鎖(讀鎖,S鎖)

        共享鎖的具體邏輯如下案例:

        若事務A對數(shù)據(jù)對象o加上S鎖,則事務A可以讀數(shù)據(jù)對象o但不能修改,其他事務只能再對數(shù)據(jù)對象o加S鎖,而不能加X鎖,直到事務A釋放數(shù)據(jù)對象o上的S鎖。這保證了其他事務可以讀數(shù)據(jù)對象o,但在事務A釋放數(shù)據(jù)對象o上的S鎖之前不能對數(shù)據(jù)對象o做任何修改。

        語法和案例如下:

        # 加讀鎖
         
         Lock table tablename read;
         
        #釋放鎖
         
        unlock table;
         
        案例如下 :
         
        #session1 給表創(chuàng)建讀鎖(創(chuàng)建一個連接就是一個回話session)
         
        mysql> lock table userinfo read;
         
        Query OK, 0 rows affected (0.01 sec)
         
        #可以讀取當前表的數(shù)據(jù)
         
        mysql> select * from userinfo ;
         
        +----+----------+------+
         
        | id | name     | age  |
         
        +----+----------+------+
         
        |  1 | zhangsan |   27 |
         
        |  2 | lisi     |   27 |
         
        |  3 | dd       | NULL |
         
        |  4 | dd       |    2 |
         
        +----+----------+------+
         
        4 rows in set (0.00 sec)
         
        #session1  因為給userinfo表加了讀鎖,所以不能讀取其他表數(shù)據(jù)
         
        mysql> select * from student_score;
         
        ERROR 1100 (HY000): Table 'student_score' was not locked with LOCK TABLES
         
        #session1  因為給表加的讀鎖,所以當前回話不能對表進行其他操作,
         
        mysql> update userinfo set age=age+1;
         
        ERROR 1099 (HY000): Table 'userinfo' was locked with a READ lock and can't be updated
         
          
         
        #session2,在session1 沒有釋放鎖之前,session2可以進行讀取userinfo
         
        mysql> select * from userinfo;
         
        +----+----------+------+
         
        | id | name     | age  |
         
        +----+----------+------+
         
        |  1 | zhangsan |   27 |
         
        |  2 | lisi     |   27 |
         
        |  3 | dd       | NULL |
         
        |  4 | dd       |    2 |
         
        +----+----------+------+
         
        4 rows in set (0.00 sec)
         
        #session2,在session1 沒有釋放鎖之前,session2修改或刪除表userinfo的任何數(shù)據(jù)都被阻塞
         
        mysql> update  userinfo set age=1 where name='
        dd';
         
        。。。。一直等待,直到seesion1 釋放鎖
         
        #session1 釋放鎖,session1在執(zhí)行下面釋放鎖語句后,session2才會提交上面修改語句。
         
        mysql> unlock tables;

        1.4 排它鎖(寫鎖,X鎖)

        共享鎖的具體邏輯如下案例:

        若事務A對數(shù)據(jù)對象o加上X鎖,事務A可以讀數(shù)據(jù)對象o也可以修改數(shù)據(jù)對象o,其他事務不能再對數(shù)據(jù)對象o加任何鎖,直到事務A釋放數(shù)據(jù)對象o上的鎖。這保證了其他事務在事務A釋放數(shù)據(jù)對象o上的鎖之前不能再讀取和修改數(shù)據(jù)對象o。

        # 加寫鎖給表
         
         Lock table tablename write;
         
        # 加寫鎖給行
         
         select ... for update;
         
        #釋放表鎖
         
        unlock table;
         
        案例如下 :
         
        # session1 當前會話session1給表加寫鎖。
         
        mysql> lock table userinfo write;
         
        Query OK, 0 rows affected (0.00 sec)
         
        #session1 當前會話可以修改此表數(shù)據(jù)
         
        mysql> update userinfo set age=9;
         
        Query OK, 4 rows affected (0.01 sec)
         
        Rows matched: 4  Changed: 4  Warnings: 0
         
        #session1  當前會話可以查詢此表
         
        mysql> select * from userinfo;
         
        +----+----------+------+
         
        | id | name     | age  |
         
        +----+----------+------+
         
        |  1 | zhangsan |    9 |
         
        |  2 | lisi     |    9 |
         
        |  3 | dd       |    9 |
         
        |  4 | dd       |    9 |
         
        +----+----------+------+
         
        4 rows in set (0.00 sec)
         
        #session2,在session1 沒有釋放鎖之前,session2對表的任何數(shù)據(jù)任何操作(增刪改查)userinfo的任何數(shù)據(jù)都被阻塞
         
        mysql> update  userinfo set age=1 where name='dd';
         
        。。。。一直等待,直到seesion1 釋放鎖
         
          
         
        如上是鎖表,接下來我們看看給行加鎖的效果:
         
           #修改提交模式,改為手動提交
         
        mysql> set autocommit=0;
         
        Query OK, 0 rows affected (0.00 sec)
         
        #session1 當前會話給id=1的行加寫鎖。
         
        mysql>  select * from userinfo where id=1 for update;
         
        +----+----------+------+
         
        | id | name     | age  |
         
        +----+----------+------+
         
        |  1 | zhangsan |   10 |
         
        +----+----------+------+
         
        1 row in set (0.00 sec)
         
        #session2 當前會話2 可以查詢當前表數(shù)據(jù)。
         
        mysql> select * from userinfo;
         
        +----+----------+------+
         
        | id | name     | age  |
         
        +----+----------+------+
         
        |  1 | zhangsan |   11 |
         
        |  2 | lisi     |   12 |
         
        |  3 | dd       |   12 |
         
        |  4 | dd       |   11 |
         
        +----+----------+------+
         
        4 rows in set (0.00 sec)
         
        #session2 當前會話2 可以操作其他行數(shù)據(jù)。
         
        mysql> update userinfo set age=12 where id=2;
         
        Query OK, 0 rows affected (0.01 sec)
         
        Rows matched: 1  Changed: 0  Warnings: 0
         
        #session2 當前會話2修改數(shù)據(jù)涉及到id=1的行,所以被阻塞知道行鎖被釋放
         
        mysql> update userinfo set age=12 ;
         
        。。。。一直被阻塞,直到sesssion1 執(zhí)行commit語句。
         
        #session1 回話1 提交數(shù)據(jù),也就是釋放鎖
         
        mysql> commit;
         
        Query OK, 0 rows affected (0.00 sec)

         

        1.5意向鎖

        意向共享鎖和意向排它鎖總稱為意向鎖。意向鎖的出現(xiàn)是為了支持Innodb支持多粒度鎖。它是表級別的鎖。兩種鎖含義如下:

         

        1.意向共享鎖:事務想要在獲得表中某些記錄的共享鎖(讀鎖),需要在表上先加意向共享鎖。

        2.意向互斥鎖:事務想要在獲得表中某些記錄的互斥鎖(寫鎖),需要在表上先加意向互斥鎖。

         

        當我們需要給一個加表鎖的時候,我們需要根據(jù)意向鎖去判斷表中有沒有數(shù)據(jù)行被鎖定,以確定是否能加成功。如果意向鎖是行鎖,那么我們就得遍歷表中所有數(shù)據(jù)行來判斷。如果意向鎖是表鎖,則我們直接判斷一次就知道表中是否有數(shù)據(jù)行被鎖定了。所以說將意向鎖設置成表級別的鎖的性能比行鎖高的多。

        所以一項所的作用就是當一個事務在需要獲取資源的鎖定時,如果該資源已經被排他鎖占用,則數(shù)據(jù)庫會自動給該事務申請一個該表的意向鎖。如果自己需要一個共享鎖定,就申請一個意向共享鎖。如果需要的是某行(或者某些行)的排他鎖定,則申請一個意向排他鎖。

        業(yè)務執(zhí)行流程如下:

        有了意向鎖之后,事務A在申請行鎖(寫鎖)之前,數(shù)據(jù)庫會自動先給事務A申請表的意向排他鎖。當事務B去申請表的寫鎖時就會失敗,因為表上有意向排他鎖之后事務B申請表的寫鎖時會被阻塞。明顯提高性能??焖倥袛嗪么吮碛斜绘i的數(shù)據(jù),所以,不能獲取到此表的表級別的鎖。

        1.6 樂觀鎖

        樂觀鎖不是數(shù)據(jù)庫自帶的,是需要我們自己寫業(yè)務去實現(xiàn)。樂觀鎖是指操作數(shù)據(jù)庫時(更新操作),想法很樂觀,認為這次的操作不會導致沖突,在操作數(shù)據(jù)時,并不進行任何其他的特殊處理(也就是不加鎖),而在進行更新后,再去判斷是否有沖突了。

        操作流程如下:

        在表中的數(shù)據(jù)進行更新操作時,先給數(shù)據(jù)表加一個版本(version)字段,每操作一次,將那條記錄的版本號加1。也就是先查詢出那條記錄,獲取出version字段,如果要對那條記錄進行操作(更新),則先判斷此刻version的值是否與剛剛查詢出來時的version的值相等,如果相等,則說明這段期間,沒有其他程序對其進行操作,則可以執(zhí)行更新,將version字段的值加1;如果更新時發(fā)現(xiàn)此刻的version值與剛剛獲取出來的version的值不相等,則說明這段期間已經有其他程序對其進行操作了,則不進行更新操作。這樣做的好處是避免了長事務中的數(shù)據(jù)庫加鎖開銷,大大提升了大并發(fā)量下的系統(tǒng)整體性能表現(xiàn)。

        偽業(yè)務代碼如下:
         
        #首先獲取修改之前的數(shù)據(jù)和版本號
         
        SELECT data AS old_data, version AS old_version FROM …;
         
        #進行修改,條件是當前數(shù)據(jù)的版本號和之前的老版本號一致,表示沒有人進行過修改
         
        Int updaterow= UPDATE SET data = new_data, version = new_version WHERE version = old_version
         
        // 表示修改成功,拿到了樂觀鎖
         
        if (updated row > 0) {
         
        // 樂觀鎖獲取成功,操作完成
         
        else {
         
        // 樂觀鎖獲取失敗,回滾并重試
         
        }

        1.7 悲觀鎖

        悲觀鎖指的是對數(shù)據(jù)被外界(包括本系統(tǒng)當前的其他事務,以及來自外部系統(tǒng)的事務處理)修改持保守態(tài)度,因此,在整個數(shù)據(jù)處理過程中,將數(shù)據(jù)處于鎖定狀態(tài)。悲觀鎖的實現(xiàn),往往依靠數(shù)據(jù)庫提供的鎖機制(也只有數(shù)據(jù)庫層提供的鎖機制才能真正保證數(shù)據(jù)訪問的排他性,否則,即使在本系統(tǒng)中實現(xiàn)了加鎖機制,也無法保證外部系統(tǒng)不會修改數(shù)據(jù))

        案例如下:

        #關閉自動提交后
         
        set autocommit=0;
         
        #開始事務
         
         start transaction;
         
        #查詢當前商品信息,然后通過for update鎖定數(shù)據(jù)防止其他事務修改
         
        select status  from goods where goodsid=1 for update;
         
        #根據(jù)商品信息生成訂單
         
        insert into orders (id,goodsid) values (null,1);
         
        #修改商品status為2
         
        update t_goods set status=2;
         
        #執(zhí)行完畢,提交事務
         
        commit;

         

        上述案例就實現(xiàn)了悲觀鎖,悲觀鎖就是悲觀主義者,它會認為我們在事務A中操作數(shù)據(jù)1的時候,一定會有事務B來修改數(shù)據(jù)1,所以,在第2步我們將數(shù)據(jù)查詢出來后直接加上排它鎖(X)鎖,防止別的事務來修改事務1,直到我們commit后,才釋放了排它鎖。

         

        需要注意的是,樂觀鎖保證了數(shù)據(jù)處理時的安全性但是更新有可能會失敗,甚至是更新幾次都失敗,這是有風險的。所以如果寫入居多,對吞吐要求不高,可使用悲觀鎖。而且要注意的是悲觀鎖加鎖造成了開銷增加,降低了性能,而且還會出現(xiàn)死鎖。

        1.8 間歇鎖

        間隙鎖(Gap Lock)是Innodb在提交下為了解決幻讀問題時引入的鎖機制,(下面的所有案例沒有特意強調都使用可重復讀隔離級別)幻讀的問題存在是因為新增或者更新操作,這時如果進行范圍查詢的時候(加鎖查詢),會出現(xiàn)不一致的問題,這時使用不同的行鎖已經沒有辦法滿足要求,需要對一定范圍內的數(shù)據(jù)進行加鎖,間隙鎖就是解決這類問題的。在可重復讀隔離級別下,數(shù)據(jù)庫是通過行鎖和間隙鎖共同組成的(next-key lock),來實現(xiàn)的。加鎖規(guī)則有以下特性:

         

        1.加鎖的基本單位是(next-key lock),他是前開后閉原則

        2.插敘過程中訪問的對象會增加鎖

        3.索引上的等值查詢--給唯一索引加鎖的時候,next-key lock升級為行鎖

        4.索引上的等值查詢--向右遍歷時最后一個值不滿足查詢需求時,next-key lock 退化為間隙鎖

        5.唯一索引上的范圍查詢會訪問到不滿足條件的第一個值為止。

         

        id(主鍵)

        name

        age

        5

        name1

        18

        10

        name2

        19

        15

        name3

        20

        20

        name4

        21

        25

        name5

        22

         

        以上數(shù)據(jù)為了解決幻讀問題,更新的時候不只是對上述的五條數(shù)據(jù)增加行鎖,還對于中間的取值范圍增加了6間隙鎖,(-∞,5](5,10](10,15](15,20](20,25](25,+∞]

         如下案例:

        #session1 操作會話session1 開啟事務
         
        mysql> begin;
         
        Query OK, 0 rows affected (0.00 sec)
         
        #session1 更新id=11的數(shù)據(jù),獲取行鎖。
         
        mysql> select * from userinfo where id=11  for update;
         
        Empty set (0.00 sec)
         
          
         
        #session2 操作會話session2 開啟事務
         
        mysql> begin;
         
        Query OK, 0 rows affected (0.00 sec)
         
        #session2 在會話session2 新增數(shù)據(jù)主鍵為12
         
        mysql> insert into userinfo values(12,'hh',18);
         
        .... 一直處于阻塞狀態(tài)
         
        #如果等待時間過長,session1沒有釋放鎖,會拋出如下異常。
         
        ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

          

         如上事務session1和事務session2時,事務session1會對數(shù)據(jù)庫表增加(10,15]這個區(qū)間鎖,這時insert id = 12 的數(shù)據(jù)的時候就會因為區(qū)間鎖(10,15]而被鎖住無法執(zhí)行。

          

          特別注意,間歇鎖,如果操作不當會出現(xiàn)死鎖。如下:

         事務A

         事務B

        begin;
        select * from t where id = 9 for update;



        begin;
        select * from t where id = 6 for update; 

        insert into user value(7,7,7)
        一直阻塞

        insert into user value(7,7,7)
        一直阻塞


        如上,事務A獲取到(5,10]之間的間隙鎖不允許其他的DDL操作,在事務提交,間隙鎖釋放之前,事務B也獲取到了間隙鎖(5,10],這時兩個事務就處于死鎖狀態(tài)

         

        1.9記錄鎖

        記錄鎖,它封鎖索引記錄,作用于唯一索引上。如果執(zhí)行這條語句(select * from userinfo where id=4 for update;)它會在id=4的索引記錄上加鎖,以阻止其他事務插入,更新,刪除id=4的這一行。就是說,當一個會話執(zhí)行這條語句之后,其它會話,執(zhí)行下面這幾種語句,都會處于阻塞狀態(tài)。

        select * from userinfo where id=1 for update;
         
        delete from userinfo where id=4;
         
        insert into userinfo values(4,'hh',18);
         
        阻塞時間過長可能出拋出如下錯誤:
         
        ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

        需要注意的是,如果把事務的隔離級別降級為讀提交(Read Committed, RC),間隙鎖則會自動失效。

        1.10臨鍵鎖

         

        臨鍵鎖,作用于非唯一索引上,是記錄鎖與間隙鎖的組合,如下圖表格。

         

        id(主鍵)

        name

        age

        5

        name1

        18

        10

        name2

        19

        15

        name3

        20

        20

        name4

        21

        25

        name5

        22

         

         

        它的封鎖范圍,既包含索引記錄,又包含索引之前的區(qū)間,即(-∞,5](5,10](10,15](15,20](20,25](25,+∞]。

        如下在事務中執(zhí)行。
         
        Update userinfo SET age=19 where id= 10;
         
        Select * from userinfo where id=10 FOR UPDATE;

          

        這兩個語句都會鎖定(5,10],(10,15)這兩個區(qū)間。即, InnoDB 會獲取該記錄行的 臨鍵鎖 ,并同時獲取該記錄行下一個區(qū)間的間隙鎖。

         

         

        1.11 死鎖

          死鎖是指兩個或兩個以上事務在執(zhí)行過程中因爭搶鎖資源而造成的互相等待的現(xiàn)象。如下圖7-3所示的操作流程。

         

         

         

        上圖所示,這兩種情況即為死鎖產生的常規(guī)情景。事務A等著事務B釋放鎖,事務B等著事務A釋放鎖。就會出現(xiàn)相互等待,一直等待下去。避免這種問題的辦法有兩種,一種是,等待事務超時,主動回滾。第二種是進行死鎖檢查,主動回滾某條事務,讓別的事務能繼續(xù)走下去??梢允褂孟嚓P指令查詢當前查看正在被鎖的事務。

        #查看正在被鎖的事務
         
        SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;
         
        #可以通過如下指令,殺死當前事務進程,釋放鎖。trx_id字段對應上面語句結果對應的第一個字段值
         
        kill trx_id;



          作者 |  YLY969

        來源 |  https://www.cnblogs.com/yiming15/p/15001258.html


        瀏覽 31
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            大香蕉在线影院观看一区 | 亚洲天堂2824在线 | 日本巨乳电影 | 日本三级电影院 | 久久久高清 | 日本久久成人 | 几个装修工人把我压在茶几 | 1v1把尿揉捏h | 色噜噜色综合 | xxxxwwww视频 |