日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

mysql innodb 全表锁,Mysql InnoDB行锁及表锁分享

發布時間:2023/12/10 数据库 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mysql innodb 全表锁,Mysql InnoDB行锁及表锁分享 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一. 背景知識

二. 步入正題:表鎖和行鎖

1.1. 表鎖 vs 行鎖

在 MySQL 中鎖的種類有很多,但是最基本的還是表鎖和行鎖:表鎖指的是對一整張表加鎖,一般是 DDL 處理時使用,也可以自己在 SQL 中指定;而行鎖指的是鎖定某一行數據或某幾行,或行和行之間的間隙。行鎖的加鎖方法比較復雜,但是由于只鎖住有限的數據,對于其它數據不加限制,所以并發能力強,通常都是用行鎖來處理并發事務。表鎖由 MySQL 服務器實現,行鎖由存儲引擎實現,常見的就是InnoDb,所以通常我們在討論行鎖時,隱含的一層意義就是數據庫的存儲引擎為 InnoDb ,而 MyISAM 存儲引擎只能使用表鎖。

1.2. 表鎖

表鎖由 MySQL 服務器實現,所以無論你的存儲引擎是什么,都可以使用。一般在執行 DDL 語句時,譬? ??? ??? ??? ?如?ALTER TABLE?就會對整個表進行加鎖。在執行 SQL 語句時,也可以明確對某個表加鎖。

1.2.1 操作演示(user_test創建表腳本見2.4.1)

-- 事物A中 顯示對 user_test 加上讀鎖locktableuser_test read;select*fromuser_testwhereid=3;? -- 此時事物B中對其進行update或者write操作start TRANSACTION;-- 手動開啟事物insertintouser_test(age,name)values(18,'Tom');

結論:此時事物B一致處于等待事物A釋放鎖的狀態,最終會回去鎖超時

2.1. 行鎖(以下驗證是在默認的隔離級別(可重復讀的事務隔離級)下操作)

InnoDB? NEXT-KEY Locks,解決了在可重復讀的事務隔離級別下出現幻讀的問題。

什么是幻讀?

幻讀是在可重復讀的事務隔離級別下會出現的一種問題,簡單來說,可重復讀保證了當前事務不會讀取到其他事? ? ? ?務已提交的 UPDATE 操作。但同時,也會導致當前事務無法感知到來自其他事務中的 INSERT 或 DELETE 操? ???? 作,這就是幻讀。

2.2. 關于行鎖我們要知道的

行鎖在 InnoDB 中是基于索引實現的,所以一旦某個加鎖操作沒有使用索引,那么該鎖就會退化為表鎖。

2.3 行鎖分類

2.3.1 從加鎖范圍劃分

a)記錄鎖(Record Locks):存在與唯一索引包括主鍵索引 顧名思義,記錄鎖就是為某行記錄加鎖,它封鎖該行的索引記錄:

b)間隙鎖(Gap Locks):

存在與非唯一索引中,鎖定開區間范圍內的一段間隔,它是基于臨鍵鎖實現的。

間隙鎖基于非唯一索引,它鎖定一段范圍內的索引記錄。間隙鎖基于下面將會提到的Next-Key Locking 算法,請務必牢記:使用間隙鎖鎖住的是一個區間,而不僅僅是這個區間中的每一條數據。

是一種加在兩個索引之間的鎖,或者加在第一個索引之前,或最后一個索引之后的間隙。有時候又稱為范圍(Range Locks),這個范圍可以跨一個索引記錄,多個索引記錄,甚至是空的。使用間隙鎖可以防止其他事務在這個范圍內插入或修改記錄,保證兩次讀取這個范圍內的記錄不會變,從而不會出現幻讀現象。很顯然,間隙鎖會增加數據庫的開銷,雖然解決了幻讀問題,但是數據庫的并發性一樣受到了影響,所以在選擇數據庫的隔離級別時,要注意權衡性能和并發性,根據實際情況考慮是否需要使用間隙鎖,大多數情況下使用 read committed 隔離級別就足夠了,對很多應用程序來說,幻讀也不是什么大問題。

產生間隙鎖的條件(RR事務隔離級別下):

3.1.? 使普通索引鎖定;

3.2.? 使用多列唯一索引;

3.3.? 使用唯一索引鎖定多行記錄。

c)臨鍵鎖(Next-Key Locks)臨鍵鎖存在于非唯一索引中(主鍵中不存在臨鍵鎖),該類型的每條記錄的索引上都存在這種鎖,它是一種特殊的間隙鎖,鎖定一段左開右閉的索引區間。臨鍵鎖,是記錄鎖與間隙鎖的組合,它的封鎖范圍,既包含索引記錄,又包含索引區間。

注意: Next-Key 可以理解為一種特殊的間隙鎖,也可以理解為一種特殊的算法。通過臨建鎖可以解決幻讀的問題。 每個數據行上的非唯一索引列上都會存在一把臨鍵鎖,當某個事務持有該數據行的臨鍵鎖時,會鎖住一段左開右閉區間的數據。需要強調的一點是,InnoDB 中行級鎖是基于索引實現的,臨鍵鎖只與非唯一索引列有關,在唯一索引列(包括主鍵列)上不存在臨鍵鎖 , 但是存在間隙鎖。臨鍵鎖的主要目的,也是為了避免幻讀(Phantom Read)。如果把事務的隔離級別降級為RC,臨鍵鎖則也會失效。

2.3.2? 從兼容性和存在形態劃分

2.4. 操作演示

2.4.1 創建測試表并初始化數據

CREATETABLE`user_test`(`id`bigint(20)unsignedNOTNULLAUTO_INCREMENT COMMENT'自增id',`age`int(11)unsignedNOTNULLCOMMENT'年齡',`name`varchar(16)NOTNULLCOMMENT'姓名',PRIMARY KEY(`id`)COMMENT'主鍵')ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='行鎖測試表';INSERTINTO`user_test`(`id`,`age`,`name`)VALUES(1,10,'Lee');INSERTINTO`user_test`(`id`,`age`,`name`)VALUES(2,24,'Ted');INSERTINTO`user_test`(`id`,`age`,`name`)VALUES(3,32,'Zed');INSERTINTO`user_test`(`id`,`age`,`name`)VALUES(4,45,'Talon');

2.4.2Record lock 記錄鎖驗證

執行一下sql

-- 事物A 中更新數據 sql如下:-- 手動開啟事物或者 beginSTART TRANSACTION;-- 根據非唯一索引列 UPDATE 某條記錄? (只會產生記錄鎖,不會產生間隙鎖)UPDATEuser_testSETname='LISHI'WHEREid=2;-- 等觀看完下面事物B在開啟事物commit;rollback;-- 事物B 也同時更新統一條記錄START TRANSACTION;UPDATEuser_testSETname='ZHANGSAN'WHEREid=2;commit;rollback;

此時事物B會出現如圖結果

事物A commit 或者 rollback 此時事物B會拿到鎖執行成功;事物B更新其他id記錄不受影響:

START TRANSACTION;UPDATEuser_testSETname='ZHANGSAN'WHEREid=1;commit;

2.4.2Next-Key Locks 臨鍵鎖驗證

首先在age字段上創建普通索引

ALTERTABLE`user_test` ADD INDEX `index_age`(`age`);

此時該表中 age 列潛在的臨鍵鎖有:

(-∞, 10],

(10, 24],

(24, 32],

(32, 45],

(45, +∞],

事物A 中更新數據 sql如下:

START TRANSACTION;-- 根據非唯一索引列 UPDATE 某條記錄select*fromuser_testwhereage=10forupdate-- 事物B此時插入或者更新age小于10大于1的一條記錄-- 插入一條記錄為age=7的記錄start TRANSACTION;insertintouser_test(age,name)values(7,'Tom');COMMIT;

-- 事物B更新其中一條記錄age為8也會被阻塞(雖然條件id是主鍵索引,更新的字段是普通索引,因此也會加上間隙鎖)start TRANSACTION;UPDATEuser_testSETage=8WHEREid=2;COMMIT;

此時事物B會出現如圖結果

事物A commit 或者 rollback 此時事物B會拿到鎖執行成功

2.4.3. Gap Locks 間隙鎖驗證

打開間隙鎖設置首先查看 innodb_locks_unsafe_for_binlog 是否禁用:

show variableslike'innodb_locks_unsafe_for_binlog';

innodb_locks_unsafe_for_binlog:默認值為OFF,即啟用間隙鎖。因為此參數是只讀模式,如果想要禁用間隙鎖,需要修改my.cnf(windows是my.ini)重新啟動才行。

默認mac是沒有my.cnf文件的,因此要在 etc文件下創建 my.cnf文件(etc/my.cnf)

my.cnf內容如下(主要添加:)

innodb_locks_unsafe_for_binlog=1

3.my.cnf內容如下:

# Example MySQL config file formediumsystems.? #? # Thisisfor a system with little memory(32M-64M)whereMySQL plays# an important part,orsystems up to128MwhereMySQLisused together with# other programs(suchasa web server)? #? # MySQL programs look for option filesinasetof# locations which dependonthe deployment platform.? # You can copy this option file to one of those? # locations. For information about these locations,see:# http://dev.mysql.com/doc/mysql/en/option-files.html? #? #Inthis file,you can use alllongoptions that a program supports.# If you want to know which options a program supports,run the program# with the"--help"option.? # The following options will be passed to all MySQL clients? [client]default-character-set=utf8#password=your_passwordport=3306socket=/tmp/mysql.sock? # Here follows entries for some specific programs? ? # The MySQL server? [mysqld]character-set-server=utf8init_connect='SET NAMES utf8? port? ? ? ? = 3306? ? socket? ? ? = /tmp/mysql.sock? ? skip-external-locking? ? key_buffer_size = 16M? ? max_allowed_packet = 1M? ? table_open_cache = 64? ? sort_buffer_size = 512K? ? net_buffer_length = 8K? ? read_buffer_size = 256K? ? read_rnd_buffer_size = 512K? ? myisam_sort_buffer_size = 8M? ? character-set-server=utf8? ? init_connect='SETNAMES utf8'? innodb_locks_unsafe_for_binlog = 1# Don't listenona TCP/IP port at all. This can be a security enhancement,# if all processes that need to connect to mysqld runonthe same host.# All interaction with mysqld must be made via Unix socketsornamed pipes.# Note that using this option without enabling named pipesonWindows#(via the"enable-named-pipe"option)will render mysqld useless!#? #skip-networking # Replication Master Server(default)# binary loggingisrequired for replicationlog-bin=mysql-bin # binary logging format-mixed recommendedbinlog_format=mixed # required unique idbetween1and2^32-1# defaults to1if master-hostisnotset# but willnotfunctionasa master if omittedserver-id=1 # Replication Slave(comment out master section to use this)? ? #? # To configure this hostasa replication slave,you can choosebetween# two methods:? ? #? #1)Use the CHANGE MASTER TO command(fully describedinour manual)-#? ? the syntaxis:? ? #? #? ? CHANGE MASTER TO MASTER_HOST=,MASTER_PORT=,#? ? MASTER_USER=,MASTER_PASSWORD=;? ? #? #whereyou replace,,byquoted stringsand#bythe master's port number (3306 by default).? ? ? #? ? ? #? ? Example:? ? ? #? ? ? #? ? CHANGE MASTER TO MASTER_HOST='125.564.12.1', MASTER_PORT=3306,? ? ? #? ? MASTER_USER='joe', MASTER_PASSWORD='secret';? ? ? #? ? ? # OR? ? ? #? ? ? # 2) Set the variables below. However, in case you choose this method, then? ? ? #? ? start replication for the first time (even unsuccessfully, for example? ? ? #? ? if you mistyped the password in master-password and the slave fails to? ? ? #? ? connect), the slave will create a master.info file, and any later? ? ? #? ? change in this file to the variables'valuesbelow will be ignoredand#? ? overriddenbythe content of the master.infofile,unless you shutdown#? ? the slave server,deletemaster.infoandrestart the slaver server.#? ? For that reason,you may want to leave the lines below untouched#(commented)andinstead use CHANGE MASTER TO(see above)? ? #? # required unique idbetween2and2^32-1#(anddifferentfromthe master)# defaults to2if master-hostisset# but willnotfunctionasa slave if omitted#server-id=2? ? #? # The replication master for this slave-required#master-host=? ? #? ? ? # The username the slave will use for authentication when connecting? # to the master-required#master-user=? ? #? ? ? # The password the slave will authenticate with when connecting to? # the master-required#master-password=? ? #? mysqld# The port the masterislisteningon.# optional-defaults to3306#master-port=? ? #? # binary logging-notrequired for slaves,but recommended#log-bin=mysql-bin? ? ? # Uncomment the following if you are using InnoDB tables? #innodb_data_home_dir=/usr/local/mysql/data#innodb_data_file_path=ibdata1:10M:autoextend#innodb_log_group_home_dir=/usr/local/mysql/data# You canset.._buffer_pool_size up to50-80%? ? ? # of RAM but beware of setting memory usage too high? #innodb_buffer_pool_size=16M#innodb_additional_mem_pool_size=2M#Set.._log_file_size to25%of buffer pool size#innodb_log_file_size=5M#innodb_log_buffer_size=8M#innodb_flush_log_at_trx_commit=1#innodb_lock_wait_timeout=50 [mysqldump]? ? ? ? quick? max_allowed_packet=16M [mysql]no-auto-rehash# Remove the next comment character if you arenotfamiliar with SQL#safe-updatesdefault-character-set=utf8 [myisamchk]key_buffer_size=20Msort_buffer_size=20Mread_buffer=2Mwrite_buffer=2M [mysqlhotcopy]interactive-timeout

此時再次查看間隙鎖是否開啟

唯一索引的間隙鎖

-- 創建test表并插入一些數據CREATETABLE`test`(`id`int(1)NOTNULLAUTO_INCREMENT,`name`varchar(8)DEFAULTNULL,PRIMARY KEY(`id`))ENGINE=InnoDB DEFAULT CHARSET=utf8;INSERTINTO`test`VALUES('1','小羅');INSERTINTO`test`VALUES('5','小黃');INSERTINTO`test`VALUES('7','小明');INSERTINTO`test`VALUES('11','小紅');

在進行測試之前,我們先來看看test表中存在的隱藏間隙:

(-∞, 1]

(1, 5]

(5, 7]

(7, 11]

(11, +∞]

-- 執行事物1如下START TRANSACTION;-- 查詢 id 在 5 - 11 范圍的數據并加記錄鎖 SELECT*FROM`test`WHERE`id`BETWEEN5AND11FORUPDATE;-- 事務B進行插入數據操作 --? 注意:以下的語句不是放在一個事務中執行,而是分開多次執行,每次事務中只有一條添加語句START TRANSACTION;-- 事務2插入一條 id = 3,name = '小張1' 的數據 INSERTINTO`test`(`id`,`name`)VALUES(3,'小張1');# 正常執行-- 事務3插入一條 id = 4,name = '小白' 的數據INSERTINTO`test`(`id`,`name`)VALUES(4,'小白');# 正常執行-- 事務4插入一條 id = 6,name = '小東' 的數據INSERTINTO`test`(`id`,`name`)VALUES(6,'小東');# 阻塞-- 事務5插入一條 id = 8, name = '大羅' 的數據INSERTINTO`test`(`id`,`name`)VALUES(8,'大羅');# 阻塞-- 事務6插入一條 id = 9, name = '大東' 的數據INSERTINTO`test`(`id`,`name`)VALUES(9,'大東');# 阻塞-- 事務7插入一條 id = 11, name = '李西' 的數據INSERTINTO`test`(`id`,`name`)VALUES(11,'李西');# 阻塞-- 事務8插入一條 id = 12, name = '張三' 的數據INSERTINTO`test`(`id`,`name`)VALUES(12,'張三');# 正常執行-- 提交事務1,釋放事務1的鎖 COMMIT;

從上面我們可以看到,(5, 7]、(7, 11] 這兩個區間,都不可插入數據,其它區間,都可以正常插入數據。所以我們可以得出結論:當我們給 (5, 7] 這個區間加鎖的時候,會鎖住 (5, 7]、(7, 11] 這兩個區間。

我們再來測試如果我們鎖住不存在的數據時,會怎樣:

-- 開啟事務1 START TRANSACTION;--? 查詢 id = 3 這一條不存在的數據并加記錄鎖 SELECT*FROM`test`WHERE`id`=3FORUPDATE;-- 延遲30秒執行,防止鎖釋放 SELECTSLEEP(30);-- # 注意:以下的語句不是放在一個事務中執行,而是分開多次執行,每次事務中只有一條添加語句--? 事務2插入一條 id = 3,name = '小張1' 的數據 INSERTINTO`test`(`id`,`name`)VALUES(2,'小張1');# 阻塞--? 事務3插入一條 id = 4,name = '小白' 的數據 INSERTINTO`test`(`id`,`name`)VALUES(4,'小白');# 阻塞-- 事務4插入一條 id = 6,name = '小東' 的數據 INSERTINTO`test`(`id`,`name`)VALUES(6,'小東');# 正常執行-- 事務5插入一條 id = 8, name = '大羅' 的數據 INSERTINTO`test`(`id`,`name`)VALUES(8,'大羅');# 正常執行-- 提交事務1,釋放事務1的鎖 COMMIT;

我們可以看出,指定查詢某一條記錄時,如果這條記錄不存在,會產生間隙鎖。

結論

對于指定查詢某一條記錄的加鎖語句,如果該記錄不存在,會產生記錄鎖和間隙鎖,如果記錄存在,則只會產生記錄鎖,如:WHERE `id` = 5 FOR UPDATE;

對于查找某一范圍內的查詢語句,會產生間隙鎖,如:WHERE `id` BETWEEN 5 AND 7 FOR UPDATE;

總結:

上述文檔是自己及其團隊小伙伴們一起討論的結果,自己做了一下總結,很感謝團隊成員劉昌力、劉明遠、朱文彬、祁世松、桑萌萌、石偉男以及彭紹翔等他們的討論支持。

此文檔是對行鎖和表鎖的一個粗略的認識,存在一定的不足、知識點的缺失、不完善等問題。希望大家能夠一起再完善一下,共同學習進步,掌握知識技能,更好的正確高效的運用到平時的工作當中去,才是我們分享技術文檔的意思所在。

總結

以上是生活随笔為你收集整理的mysql innodb 全表锁,Mysql InnoDB行锁及表锁分享的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。