MySQL - count(*)

count(*) 的實(shí)現(xiàn)方式

在沒(méi)有過(guò)濾條件的 count()下核偿,在不同的 MySQL 引擎中贤牛,count() 有不同的實(shí)現(xiàn)方式尘吗。

  • MyISAM 引擎把一個(gè)表的總行數(shù)存在了磁盤(pán)上鹉梨,因此執(zhí)行 count(*) 的時(shí)候會(huì)直接返回這個(gè)數(shù)婴程,效率很高;
  • 而 InnoDB 引擎就麻煩了渠脉,即使是在同一個(gè)時(shí)刻的多個(gè)查詢(xún),由于多版本并發(fā)控制(MVCC)的原因瓶佳,InnoDB 表“應(yīng)該返回多少行”也是不確定的芋膘。它執(zhí)行 count(*) 的時(shí)候,需要把數(shù)據(jù)一行一行地從引擎里面讀出來(lái)霸饲,然后累積計(jì)數(shù)为朋。

MyISAM 表雖然 count() 很快,但是不支持事務(wù)厚脉;
show table status 命令雖然返回很快习寸,但是不準(zhǔn)確;
InnoDB 表直接 count(
) 會(huì)遍歷全表傻工,雖然結(jié)果準(zhǔn)確霞溪,但會(huì)導(dǎo)致性能問(wèn)題。

InnDB對(duì)count(*)的優(yōu)化

InnoDB 是索引組織表中捆,主鍵索引樹(shù)的葉子節(jié)點(diǎn)是數(shù)據(jù)鸯匹,而普通索引樹(shù)的葉子節(jié)點(diǎn)是主鍵值。
所以泄伪,普通索引樹(shù)比主鍵索引樹(shù)小很多殴蓬。對(duì)于 count(*) 這樣的操作,遍歷哪個(gè)索引樹(shù)得到的結(jié)果邏輯上都是一樣的蟋滴。
因此染厅,MySQL 優(yōu)化器會(huì)找到最小的那棵樹(shù)來(lái)遍歷痘绎。
在保證邏輯正確的前提下,盡量減少掃描的數(shù)據(jù)量肖粮,是數(shù)據(jù)庫(kù)系統(tǒng)設(shè)計(jì)的通用法則之一孤页。

如果你現(xiàn)在有一個(gè)頁(yè)面經(jīng)常要顯示交易系統(tǒng)的操作記錄總數(shù),到底應(yīng)該怎么辦呢尿赚?
答案是散庶,我們只能自己計(jì)數(shù)。

用緩存系統(tǒng)保存計(jì)數(shù)

例如使用Redis string類(lèi)型進(jìn)行存儲(chǔ)表的行數(shù)凌净。
問(wèn)題是會(huì)有丟失數(shù)據(jù)和計(jì)數(shù)不精確的問(wèn)題悲龟。



seesionA插入一條數(shù)據(jù)后,sessionB馬上讀取Redis中該表行數(shù)冰寻,實(shí)際是舊數(shù)據(jù)须教。
只有sessionA把Redis更新了才生效。

在數(shù)據(jù)庫(kù)保存計(jì)數(shù)

可以利用“事務(wù)”這個(gè)特性斩芭,把問(wèn)題解決掉轻腺。



雖然會(huì)話(huà) B 的讀操作仍然是在 T3 執(zhí)行的,但是因?yàn)檫@時(shí)候更新事務(wù)還沒(méi)有提交划乖,所以計(jì)數(shù)值加 1 這個(gè)操作對(duì)會(huì)話(huà) B 還不可見(jiàn)贬养。
因此,會(huì)話(huà) B 看到的結(jié)果里琴庵, 查計(jì)數(shù)值和“最近 100 條記錄”看到的結(jié)果误算,邏輯上就是一致的。

不同的 count 用法

count含義:count() 是一個(gè)聚合函數(shù)迷殿,對(duì)于返回的結(jié)果集儿礼,一行行地判斷,如果 count 函數(shù)的參數(shù)不是 NULL庆寺,累計(jì)值就加 1蚊夫,否則不加。最后返回累計(jì)值懦尝。

所以知纷,count(*)、count(主鍵 id) 和 count(1) 都表示返回滿(mǎn)足條件的結(jié)果集的總行數(shù)导披;而 count(字段)屈扎,則表示返回滿(mǎn)足條件的數(shù)據(jù)行里面,參數(shù)“字段”不為 NULL 的總個(gè)數(shù)撩匕。

至于分析性能差別的時(shí)候鹰晨,可以記住這么幾個(gè)原則:

  1. server 層要什么就給什么;
  2. InnoDB 只給必要的值;
  3. 現(xiàn)在的優(yōu)化器只優(yōu)化了 count(*) 的語(yǔ)義為“取行數(shù)”其他“顯而易見(jiàn)”的優(yōu)化并沒(méi)有做模蜡。

對(duì)于 count(主鍵 id) 來(lái)說(shuō)漠趁,InnoDB 引擎會(huì)遍歷整張表,把每一行的 id 值都取出來(lái)忍疾,返回給 server 層闯传。server 層拿到 id 后,判斷是不可能為空的卤妒,就按行累加甥绿。

對(duì)于 count(1) 來(lái)說(shuō),InnoDB 引擎遍歷整張表则披,但不取值共缕。server 層對(duì)于返回的每一行,放一個(gè)數(shù)字“1”進(jìn)去士复,判斷是不可能為空的图谷,按行累加。

單看這兩個(gè)用法的差別的話(huà)阱洪,你能對(duì)比出來(lái)便贵,count(1) 執(zhí)行得要比 count(主鍵 id) 快。因?yàn)閺囊娣祷?id 會(huì)涉及到解析數(shù)據(jù)行冗荸,以及拷貝字段值的操作承璃。

對(duì)于 count(字段) 來(lái)說(shuō):

  • 如果這個(gè)“字段”是定義為 not null 的話(huà),一行行地從記錄里面讀出這個(gè)字段蚌本,判斷不能為 null绸硕,按行累加;
  • 如果這個(gè)“字段”定義允許為 null魂毁,那么執(zhí)行的時(shí)候,判斷到有可能是 null出嘹,還要把值取出來(lái)再判斷一下席楚,不是 null 才累加。也就是前面的第一條原則税稼,server 層要什么字段烦秩,InnoDB 就返回什么字段。

但是 count() 是例外郎仆,并不會(huì)把全部字段取出來(lái)只祠,而是專(zhuān)門(mén)做了優(yōu)化,不取值扰肌。count() 肯定不是 null抛寝,按行累加。

看到這里,你一定會(huì)說(shuō)盗舰,優(yōu)化器就不能自己判斷一下嗎晶府,主鍵 id 肯定非空啊,為什么不能按照 count() 來(lái)處理钻趋,多么簡(jiǎn)單的優(yōu)化啊川陆。
當(dāng)然,MySQL 專(zhuān)門(mén)針對(duì)這個(gè)語(yǔ)句進(jìn)行優(yōu)化蛮位,也不是不可以较沪。但是這種需要專(zhuān)門(mén)優(yōu)化的情況太多了,而且 MySQL 已經(jīng)優(yōu)化過(guò) count(
) 了失仁,你直接使用這種用法就可以了尸曼。

結(jié)論是,按照效率排序:count(字段) < count(主鍵) < count(1) = count()陶因。 盡量使用count()骡苞。

總結(jié)

在不同引擎中 count(*) 的實(shí)現(xiàn)方式是不一樣的,用緩存系統(tǒng)來(lái)存儲(chǔ)計(jì)數(shù)值是存在的問(wèn)題楷扬。
把計(jì)數(shù)放在 Redis 里面解幽,不能夠保證計(jì)數(shù)和 MySQL 表里的數(shù)據(jù)精確一致的原因,是這兩個(gè)不同的存儲(chǔ)構(gòu)成的系統(tǒng)烘苹,不支持分布式事務(wù)躲株,無(wú)法拿到精確一致的視圖。
而把計(jì)數(shù)值也放在 MySQL 中镣衡,就解決了一致性視圖的問(wèn)題霜定。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市廊鸥,隨后出現(xiàn)的幾起案子望浩,更是在濱河造成了極大的恐慌,老刑警劉巖惰说,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件磨德,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡吆视,警方通過(guò)查閱死者的電腦和手機(jī)典挑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)啦吧,“玉大人您觉,你說(shuō)我怎么就攤上這事∈谧遥” “怎么了琳水?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵肆糕,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我炫刷,道長(zhǎng)擎宝,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任浑玛,我火速辦了婚禮绍申,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘顾彰。我一直安慰自己极阅,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布涨享。 她就那樣靜靜地躺著筋搏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪厕隧。 梳的紋絲不亂的頭發(fā)上奔脐,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音吁讨,去河邊找鬼髓迎。 笑死,一個(gè)胖子當(dāng)著我的面吹牛建丧,可吹牛的內(nèi)容都是我干的排龄。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼翎朱,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼橄维!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起拴曲,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤争舞,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后澈灼,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體兑障,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年蕉汪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片逞怨。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡者疤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出叠赦,到底是詐尸還是另有隱情驹马,我是刑警寧澤革砸,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站糯累,受9級(jí)特大地震影響算利,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜泳姐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一效拭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧胖秒,春花似錦缎患、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至风题,卻和暖如春判导,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背沛硅。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工眼刃, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人稽鞭。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓鸟整,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親朦蕴。 傳聞我的和親對(duì)象是個(gè)殘疾皇子篮条,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容