摘要:會(huì)話在插入一條新數(shù)據(jù),在查詢時(shí)的結(jié)果是時(shí)會(huì)話語句已經(jīng)提交,所以在會(huì)話的事務(wù)中能看到這個(gè)更新。由于會(huì)話在時(shí)事務(wù)還沒有提交,會(huì)話看不到會(huì)話的更新,所以會(huì)話在時(shí)的結(jié)果是。
相信每個(gè)人在寫代碼時(shí)都有遇到過要獲取MYSQL表里數(shù)據(jù)行數(shù)的情況,多數(shù)人獲取數(shù)據(jù)表行數(shù)時(shí)都用COUNT(*),但同時(shí)也流傳了不少其他方式,比如說COUNT(1)、COUNT(主鍵)、COUNT(字段)。到底哪種方式MYSQL執(zhí)行起來更快也是眾說紛紜,其實(shí)之前我也不知道到底哪個(gè)執(zhí)行起來快,到底誰說的對(duì)(笑哭)。好在最近在認(rèn)真學(xué)習(xí)極客時(shí)間的MySQL專欄,其中專門有一節(jié)是對(duì)這個(gè)問題的討論,看完后也是解除了長久以來的疑惑。
文章中都是針對(duì)MySQL的InnoDB引擎展開討論的,MyISAM引擎是把一個(gè)表的總行數(shù)記錄在了磁盤里,查詢時(shí)效率很高(如果加了where條件也不能直接從磁盤返回)。而InnoDB由于多版本并發(fā)控制(MVCC)的原因,即使時(shí)同一時(shí)刻的查詢InnoDB表應(yīng)該"返回多少行"也是不確定的,比如假設(shè)表t中有10000行數(shù)據(jù):
時(shí)刻 | 會(huì)話A | 會(huì)話B | 會(huì)話C |
---|---|---|---|
T1 | begin; | ||
T2 | select count(*) from t; | ||
T3 | insert into t (插入一行); | ||
T4 | begin; | ||
T5 | insert into t (插入一行); | ||
T6 | select count(*) from t; (返回10000) | select count(*) from t; (返回10002); | select count(*) from t; (返回10001) |
會(huì)話A在T1開啟事務(wù)拿到一致性視圖,可重復(fù)讀級(jí)別下在事務(wù)中任何時(shí)刻讀到數(shù)據(jù)都一樣,其他事務(wù)的更新對(duì)會(huì)話A沒影響所以count(*)的結(jié)果是10000,會(huì)話B在T4開啟事務(wù)拿到一致性視圖,T4之前會(huì)話C已經(jīng)新插入了一條語句并提交(多帶帶執(zhí)行一條更新語句,InnoDB會(huì)自己啟動(dòng)一個(gè)事務(wù),語句執(zhí)行完馬上提交)。會(huì)話B在T5插入一條新數(shù)據(jù),在T6查詢時(shí)count(*)的結(jié)果是10002(T4 begin時(shí)會(huì)話C insert語句已經(jīng)提交,所以在會(huì)話B的事務(wù)中能看到這個(gè)更新)。由于會(huì)話B在T6時(shí)事務(wù)還沒有提交,會(huì)話C看不到會(huì)話B的更新,所以會(huì)話C在T6時(shí)count(*)的結(jié)果是10001。
COUNT是一個(gè)聚合函數(shù),它的功能是對(duì)返回的結(jié)果集中每一行進(jìn)行判斷,如果COUNT函數(shù)的參數(shù)不是NULL則累加1,否則不累加,最后返回累計(jì)值。接下來看一下每個(gè)COUNT版本的執(zhí)行效率:
COUNT(主鍵ID) InnoDB遍歷全表,把每一行的主鍵值都取出來返回給MySQL的Server層,因?yàn)橹麈I不可能為NULL,Server層直接按行累加最后返回累計(jì)值給客戶端。
COUNT(1) 遍歷全表但不取值,Server層對(duì)返回的每一行放個(gè)數(shù)字"1"進(jìn)去,按行累加。COUNT(1)比COUNT(主鍵)快,因?yàn)椴恍枰≈担瑴p少了數(shù)據(jù)傳輸。
COUNT(字段) 遍歷全表,一行行從記錄中讀出字段值給Server層,Server層判斷值不為NULL了再累加。
COUNT(*) MySQL專門做了優(yōu)化,會(huì)找到表中最小的索引樹,InnoDB普通索引樹比主鍵索引小很多,對(duì)于COUNT(*)遍歷哪個(gè)樹是一樣的,count(*)時(shí)MySQL不取記錄值,count(*)也肯定不為NULL,Server層中直接按行累加。
所以這個(gè)版本COUNT的從低到高分別為:
COUNT(字段) < COUNT(主鍵) < COUNT(1) ≈ COUNT(*)
所以建議你盡量使用count(*)來獲取記錄行數(shù)。
另外要注意,很多人為了銷量會(huì)把表的行數(shù)記錄到Redis中,但這樣不能保證Redis里的計(jì)數(shù)和MySQL表里的數(shù)據(jù)保持精確一致,這是兩個(gè)不同的存儲(chǔ)系統(tǒng)不支持分布式事務(wù)所以就無法拿到精確的一致性視圖,如果為了效率把表行數(shù)多帶帶存儲(chǔ)那么最好存放在一個(gè)多帶帶的MySQL表里,這樣無法拿到一致性視圖的問題就能解決了。
關(guān)于MySQL更詳細(xì)的分析,推薦關(guān)注MySQL實(shí)戰(zhàn)45講
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/17983.html
閱讀 2709·2019-08-30 15:52
閱讀 3655·2019-08-29 17:02
閱讀 1905·2019-08-29 13:00
閱讀 978·2019-08-29 11:07
閱讀 3313·2019-08-27 10:53
閱讀 1822·2019-08-26 13:43
閱讀 1062·2019-08-26 10:22
閱讀 1401·2019-08-23 18:06