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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > 数据库 >内容正文

数据库

mysql 值为0 但却被认为null_MySQL介于普通读和锁定读的加锁方式

發(fā)布時(shí)間:2025/3/15 数据库 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mysql 值为0 但却被认为null_MySQL介于普通读和锁定读的加锁方式 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在閱讀本文前最好先看過(guò)三篇語(yǔ)句加鎖分析文章:

  • 超全面MySQL語(yǔ)句加鎖分析(上篇)(求轉(zhuǎn))
  • 超全面MySQL語(yǔ)句加鎖分析(中篇)(求轉(zhuǎn))
  • 超全面MySQL語(yǔ)句加鎖分析(下篇)(求轉(zhuǎn))

事前準(zhǔn)備

為了故事的順利發(fā)展,我們先建一個(gè)表,并向表中插入一些記錄,下邊是SQL語(yǔ)句:

CREATE TABLE hero (
number INT,
name VARCHAR(100),
country varchar(100),
PRIMARY KEY (number),
KEY idx_name (name)
) Engine=InnoDB CHARSET=utf8;

INSERT INTO hero VALUES
(1, 'l劉備', '蜀'),
(3, 'z諸葛亮', '蜀'),
(8, 'c曹操', '魏'),
(15, 'x荀彧', '魏'),
(20, 's孫權(quán)', '吳');

現(xiàn)在hero表中的記錄情況就如下所示:

mysql> SELECT * FROM hero;
+--------+------------+---------+
| number | name | country |
+--------+------------+---------+
| 1 | l劉備 | 蜀 |
| 3 | z諸葛亮 | 蜀 |
| 8 | c曹操 | 魏 |
| 15 | x荀彧 | 魏 |
| 20 | s孫權(quán) | 吳 |
+--------+------------+---------+
5 rows in set (0.01 sec)

現(xiàn)象

在小冊(cè)答疑群里有一位同學(xué)提了一個(gè)問(wèn)題:說(shuō)是在READ COMMITTED隔離級(jí)別下發(fā)生了一件百思不得其解的事兒。好的,首先構(gòu)造環(huán)境,將當(dāng)前會(huì)話默認(rèn)的隔離級(jí)別設(shè)置成READ COMMITTED:

mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
Query OK, 0 rows affected (0.00 sec)

事務(wù)T1先執(zhí)行:

# T1中,隔離級(jí)別為READ COMMITTED
mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM hero WHERE country = '魏' FOR UPDATE;
+--------+---------+---------+
| number | name | country |
+--------+---------+---------+
| 8 | c曹操 | 魏 |
| 15 | x荀彧 | 魏 |
+--------+---------+---------+
2 rows in set (0.01 sec)

country列并不是索引列,所以本條語(yǔ)句執(zhí)行時(shí)肯定是使用掃描聚簇索引的全表掃描方式來(lái)執(zhí)行,EXPLAIN語(yǔ)句也證明了我們的想法:

mysql> EXPLAIN SELECT * FROM hero WHERE country = '魏' FOR UPDATE;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | hero | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 20.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.02 sec)

我們之前學(xué)過(guò)MySQL語(yǔ)句的加鎖分析,知道在READ COMMITTED隔離級(jí)別下,如果采用全表掃描的方式執(zhí)行查詢語(yǔ)句時(shí),InnoDB存儲(chǔ)引擎將依次對(duì)每條記錄加正經(jīng)記錄鎖,在server層測(cè)試該記錄是否符合WHERE條件,如果不符合則將加在該記錄上的鎖釋放掉。本例中使用FOR UPDATE語(yǔ)句,肯定加的是X型正經(jīng)記錄鎖。只有兩條記錄符合WHERE條件,所以最終其實(shí)只對(duì)這兩條符合條件的記錄加了X型正經(jīng)記錄鎖(就是number列值為8和15的兩條記錄)。當(dāng)然,我們可以使用SHOW ENGINE INNODB STATUS命令證明我們的分析:

mysql> SHOW ENGINE INNODB STATUS\G
... 省略了很多內(nèi)容

------------
TRANSACTIONS
------------
Trx id counter 39764
Purge done for trx's n:o < 39763 undo n:o < 0 state: running but idle
History list length 36
Total number of lock structs in row lock hash table 1
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 281479653009568, not started
0 lock struct(s), heap size 1160, 0 row lock(s)
---TRANSACTION 281479653012832, not started
0 lock struct(s), heap size 1160, 0 row lock(s)
---TRANSACTION 39763, ACTIVE 468 sec
2 lock struct(s), heap size 1160, 2 row lock(s)
MySQL thread id 19, OS thread handle 123145470611456, query id 586 localhost 127.0.0.1 root
TABLE LOCK table `xiaohaizi`.`hero` trx id 39763 lock mode IX
RECORD LOCKS space id 287 page no 3 n bits 72 index PRIMARY of table `xiaohaizi`.`hero` trx id 39763 lock_mode X locks rec but not gap
Record lock, heap no 4 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
0: len 4; hex 80000008; asc ;;
1: len 6; hex 000000009b4a; asc J;;
2: len 7; hex 80000001d3012a; asc *;;
3: len 7; hex 63e69bb9e6938d; asc c ;;
4: len 3; hex e9ad8f; asc ;;
Record lock, heap no 5 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
0: len 4; hex 8000000f; asc ;;
1: len 6; hex 000000009b4a; asc J;;
2: len 7; hex 80000001d30137; asc 7;;
3: len 7; hex 78e88d80e5bda7; asc x ;;
4: len 3; hex e9ad8f; asc ;;
... 省略了很多內(nèi)容

其中id為39763的事務(wù)就是指T1,可以看出它為heap no值為4和5的兩條記錄加了X型正經(jīng)記錄鎖(lock_mode X locks rec but not gap)。

然后再開(kāi)啟一個(gè)隔離級(jí)別也為READ COMMITTED的事務(wù)T2,在其中執(zhí)行:

# T2中,隔離級(jí)別為READ COMMITTED
mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM hero WHERE country = '吳' FOR UPDATE;
(進(jìn)入阻塞狀態(tài))

很顯然,這條語(yǔ)句也會(huì)采用全表掃描的方式來(lái)執(zhí)行,會(huì)依次去獲取每一條聚簇索引記錄的鎖。不過(guò)因?yàn)閚umber值為8的記錄已經(jīng)被T1加了X型正經(jīng)記錄鎖,T2想得卻得不到,只能眼巴巴的進(jìn)行阻塞狀態(tài),此時(shí)的SHOW ENGINE INNODB STATUS也能證明我們的猜想(只截取了一部分):

---TRANSACTION 39764, ACTIVE 34 sec fetching rows
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1160, 1 row lock(s)
MySQL thread id 20, OS thread handle 123145471168512, query id 590 localhost 127.0.0.1 root Sending data
SELECT * FROM hero WHERE country = '吳' FOR UPDATE
------- TRX HAS BEEN WAITING 34 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 287 page no 3 n bits 72 index PRIMARY of table `xiaohaizi`.`hero` trx id 39764 lock_mode X locks rec but not gap waiting
Record lock, heap no 4 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
0: len 4; hex 80000008; asc ;;
1: len 6; hex 000000009b4a; asc J;;
2: len 7; hex 80000001d3012a; asc *;;
3: len 7; hex 63e69bb9e6938d; asc c ;;
4: len 3; hex e9ad8f; asc ;;

可以看到T2正在等待獲取heap no為4的記錄上的X型正經(jīng)記錄鎖(lock_mode X locks rec but not gap waiting)。

以上是很正常的阻塞邏輯,我們都可以分析出來(lái),不過(guò)如果在T2中執(zhí)行下邊的UPDATE語(yǔ)句:

# T2中,隔離級(jí)別為READ COMMITTED
mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> UPDATE hero SET name = 'xxx' WHERE country = '吳';
Query OK, 1 row affected (0.02 sec)
Rows matched: 1 Changed: 1 Warnings: 0

WTF? 竟然沒(méi)有阻塞,就這么隨意地執(zhí)行成功了?同樣的WHERE條件,同樣的執(zhí)行計(jì)劃,怎么SELECT ... FOR UPDATE和UPDATE語(yǔ)句的加鎖情況不一樣?

原因

哈哈,是的,的確不一樣。其實(shí)MySQL支持3種類(lèi)型的讀語(yǔ)句:

  • 普通讀(也稱(chēng)一致性讀,英文名:Consistent Read)。

    這個(gè)就是指普通的SELECT語(yǔ)句,在末尾不加FOR UPDATE或者LOCK IN SHARE MODE的SELECT語(yǔ)句。普通讀的執(zhí)行方式是生成ReadView直接利用MVCC機(jī)制來(lái)進(jìn)行讀取,并不會(huì)對(duì)記錄進(jìn)行加鎖。

    小貼士:對(duì)于SERIALIZABLE隔離級(jí)別來(lái)說(shuō),如果autocommit系統(tǒng)變量被設(shè)置為OFF,那普通讀的語(yǔ)句會(huì)轉(zhuǎn)變?yōu)殒i定讀,和在普通的SELECT語(yǔ)句后邊加LOCK IN SHARE MODE達(dá)成的效果一樣。

  • 鎖定讀(英文名:Locking Read)。

    這個(gè)就是事務(wù)在讀取記錄之前,需要先獲取該記錄對(duì)應(yīng)的鎖。當(dāng)然,獲取什么類(lèi)型的鎖取決于當(dāng)前事務(wù)的隔離級(jí)別、語(yǔ)句的執(zhí)行計(jì)劃、查詢條件等因素,具體可參見(jiàn):超全面MySQL語(yǔ)句加鎖分析(上篇)(求轉(zhuǎn))

  • 半一致性讀(英文名:Semi-Consistent Read)。

    這是一種夾在普通讀和鎖定讀之間的一種讀取方式。它只在READ COMMITTED隔離級(jí)別下(或者在開(kāi)啟了innodb_locks_unsafe_for_binlog系統(tǒng)變量的情況下)使用UPDATE語(yǔ)句時(shí)才會(huì)使用。具體的含義就是當(dāng)UPDATE語(yǔ)句讀取已經(jīng)被其他事務(wù)加了鎖的記錄時(shí),InnoDB會(huì)將該記錄的最新提交的版本讀出來(lái),然后判斷該版本是否與UPDATE語(yǔ)句中的WHERE條件相匹配,如果不匹配則不對(duì)該記錄加鎖,從而跳到下一條記錄;如果匹配則再次讀取該記錄并對(duì)其進(jìn)行加鎖。這樣子處理只是為了讓UPDATE語(yǔ)句盡量少被別的語(yǔ)句阻塞。

    小貼士:半一致性讀只適用于對(duì)聚簇索引記錄加鎖的情況,并不適用于對(duì)二級(jí)索引記錄加鎖的情況。

很顯然,我們上邊所嘮叨的例子中是因?yàn)槭聞?wù)T2執(zhí)行UPDATE語(yǔ)句時(shí)使用了半一致性讀,判斷number列值為8和15這兩條記錄的最新提交版本的country列值均不為UPDATE語(yǔ)句中WHERE條件中的'吳',所以直接就跳過(guò)它們,不對(duì)它們加鎖了。

本知識(shí)點(diǎn)容易被忽略,各位同學(xué)在工作過(guò)程中分析的時(shí)候別忘記考慮一下Semi-Consistent Read喔,碼字不易,有幫助幫著轉(zhuǎn)發(fā)喔,么么噠~

小青蛙歷史文章(歷史文章,不容錯(cuò)過(guò)):

關(guān)于事務(wù)和鎖的一些細(xì)節(jié)個(gè)人所得稅漲了,日子又拮據(jù)了一點(diǎn)補(bǔ)數(shù)到底是個(gè)什么玩意兒?從根兒上理解一下MySQL小冊(cè)創(chuàng)作之心路歷程虛擬內(nèi)存是個(gè)啥 | 一分鐘系列MySQL中IS NULL、IS NOT NULL、!=不能用索引?胡扯!

長(zhǎng)按關(guān)注小青蛙,都是干貨喔

原文鏈接為《MySQL是怎樣運(yùn)行的:從根兒上理解MySQL》小冊(cè)鏈接

總結(jié)

以上是生活随笔為你收集整理的mysql 值为0 但却被认为null_MySQL介于普通读和锁定读的加锁方式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。