真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

count(*)很慢的原因是什么

這篇文章主要介紹“count(*)很慢的原因是什么”的相關(guān)知識(shí),小編通過實(shí)際案例向大家展示操作過程,操作方法簡單快捷,實(shí)用性強(qiáng),希望這篇“count(*)很慢的原因是什么”文章能幫助大家解決問題。

創(chuàng)新互聯(lián)-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比蘇尼特右網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式蘇尼特右網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋蘇尼特右地區(qū)。費(fèi)用合理售后完善,十載實(shí)體公司更值得信賴。

以下所有的內(nèi)容均是基于,MySQL 5.7 + InnoDB引擎, 進(jìn)行的分析。

拓展:

MyISAM 如果沒有查詢條件,只是簡單的統(tǒng)計(jì)表中數(shù)據(jù)總數(shù),將會(huì)返回的超快,因?yàn)閟ervice層中獲取到表信息中的總行數(shù)是準(zhǔn)確的,而InnoDB只是一個(gè)估值。

實(shí)例

廢話不多說,先看一個(gè)例子。

以下是一張表數(shù)據(jù)量有100w,表中字段相對(duì)較短,整體數(shù)據(jù)量不算大。

CREATE TABLE `hospital_statistics_data` (
  `pk_id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `id` varchar(36) COLLATE utf8mb4_general_ci NOT NULL COMMENT '外鍵',
  `hospital_code` varchar(36) COLLATE utf8mb4_general_ci NOT NULL COMMENT '醫(yī)院編碼',
  `biz_type` tinyint NOT NULL COMMENT '1服務(wù)流程  2管理效果',
  `item_code` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '考核項(xiàng)目編碼',
  `item_name` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '考核項(xiàng)目名稱',
  `item_value` varchar(36) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '考核結(jié)果',
  `is_deleted` tinyint DEFAULT NULL COMMENT '是否刪除 0否 1是',
  `gmt_created` datetime DEFAULT NULL COMMENT '創(chuàng)建時(shí)間',
  `gmt_modified` datetime DEFAULT NULL COMMENT 'gmt_modified',
  `gmt_deleted` datetime(3) DEFAULT '9999-12-31 23:59:59.000' COMMENT '刪除時(shí)間',
  PRIMARY KEY (`pk_id`)
) DEFAULT CHARSET=utf8mb4  COMMENT='醫(yī)院統(tǒng)計(jì)數(shù)據(jù)';

此表初始狀態(tài)只有一個(gè)聚簇索引。

以下分不同索引情況,看一下COUNT(*)的執(zhí)行計(jì)劃。

1)在只有一個(gè)聚簇索引的情況下看一下執(zhí)行計(jì)劃。

EXPLAIN select COUNT(*) from hospital_statistics_data;

結(jié)果:

count(*)很慢的原因是什么

這里只關(guān)注以下幾個(gè)屬性。

  • type: 這里顯示index,說明使用了索引。

  • key:PRIMARY使用了主鍵索引。

  • key_len: 索引長度8字節(jié)。

這里有很關(guān)鍵的一點(diǎn):count(*)也會(huì)走索引,在當(dāng)前情況下使用了聚簇索引。

好,再往下看。

2)存在一個(gè)非聚簇索引(二級(jí)索引)

給表添加一個(gè)hospital_code索引。

alter table hospital_statistics_data add index idx_hospital_code(hospital_code)

此時(shí)表中存在2個(gè)索引,主鍵 hospital_code。

同樣的,再執(zhí)行一下:

EXPLAIN select COUNT(*) from hospital_statistics_data;

結(jié)果:

count(*)很慢的原因是什么

同樣的,看一下 type、key和key_len三個(gè)字段。

是不是覺得有點(diǎn)“神奇”。

為何索引變成剛添加的idx_hospital_code了。

先別急著想結(jié)論,再看下面一種情況。

3)存在兩個(gè)非聚簇索引(二級(jí)索引)

在上面的基礎(chǔ)上,再添加一個(gè)二級(jí)索引。

alter table hospital_statistics_data add index idx_biz_type(biz_type)

此時(shí)表中存在3個(gè)索引,主鍵 、hospital_code 和 biz_type。

同樣的,執(zhí)行一下:

EXPLAIN select COUNT(*) from hospital_statistics_data;

結(jié)果:

count(*)很慢的原因是什么

是不是更困惑了,索引又..又又...變了.

變成新添加的idx_biz_type。

先不說為何會(huì)產(chǎn)生以上的變化,繼續(xù)往下分析。

在以上3個(gè)索引的基礎(chǔ)上,分別看一下,count(1)count(id)、count(index)count(無索引)

這4種情況,與count(*)的執(zhí)行計(jì)劃有何區(qū)別。

  • count(1)

count(*)很慢的原因是什么

  • count(id) 對(duì)于樣例表來說是,主鍵是pk_id

count(*)很慢的原因是什么

  • count(index)

這里選取biz_type索引字段。

count(*)很慢的原因是什么

  • count(無索引)

count(*)很慢的原因是什么

小結(jié):

  • count(index) 會(huì)使用當(dāng)前index指定的索引。

  • count(無索引) 是全表掃描,未走索引。

  • count(1) , count(*), count(id) 一樣都會(huì)選擇idx_biz_type索引

必要知識(shí)點(diǎn)

  • mysql 分為service層引擎層。

  • 所有的sql在執(zhí)行前會(huì)經(jīng)過service層的優(yōu)化,優(yōu)化分為很多類型,簡單的來說可分為成本規(guī)則。

  • 執(zhí)行計(jì)劃所反映的是service層經(jīng)過sql優(yōu)化后,可能的執(zhí)行過程。并非絕對(duì)(免得有些人說我只看執(zhí)行計(jì)劃過于片面)。絕大多數(shù)情況執(zhí)行計(jì)劃是可信的。

  • 索引類型分為聚簇索引非聚簇索引(二級(jí)索引)。其中數(shù)據(jù)都是掛在聚簇索引上的,非聚簇索引上只是記錄的主鍵id。

  • 拋開數(shù)據(jù)內(nèi)存,只談數(shù)據(jù)量,都是扯淡。什么500w就是極限,什么2個(gè)表以上的join都需要優(yōu)化了,什么is null不會(huì)走索引等,純純的放屁。

  • 相信一點(diǎn),編寫mysql代碼的人比,看此文章的大部分人都要優(yōu)秀。他們會(huì)盡可能在執(zhí)行前,對(duì)我這樣菜逼寫的亂七八糟的sql進(jìn)行優(yōu)化。

原因分析

其實(shí)原因非常非常簡單,上面也說了,service層會(huì)基于成本進(jìn)行優(yōu)化。

并且,正常情況下,非聚簇索引所占有的內(nèi)存要遠(yuǎn)遠(yuǎn)小于聚簇索引。所以問題來了,如果你是mysql的開發(fā)人員,你在執(zhí)行count(*)查詢的時(shí)候會(huì)使用那個(gè)索引?

我相信正常人都會(huì)使用非聚簇索引。

那如果存在2個(gè)甚至多個(gè)非聚簇索引又該如何選擇呢?

那肯定選擇最短的,占用內(nèi)存最小的一個(gè)呀,在回頭看看上面的實(shí)例,還迷惑嗎。

同樣都是非聚簇索引。idx_hospital_codelen146字節(jié);而idx_biz_typelen只有1。那還要選嗎?

那為何count(*)走了索引,卻還是很慢呢?

這里要明確一點(diǎn),索引只是提升效率的一種方式,但不能完全的解決效率問題。count(*)有一個(gè)明顯的缺陷,就是它要計(jì)算總數(shù),那就意味著要遍歷所有符合條件的數(shù)據(jù),相當(dāng)于一個(gè)計(jì)數(shù)器,在數(shù)據(jù)量足夠大的情況下,即使使用非聚簇索引也無法優(yōu)化太多。

官方文檔:

InnoDBhandlesSELECT COUNT(*)andSELECT COUNT(1)operations in the same way. There is no performance difference.

簡單的來說就是,InnoDB下 count(*) 等價(jià)于 count(1)

既然會(huì)自動(dòng)走索引,那么上面那個(gè)所謂的速度排序還覺得對(duì)嗎? count(*)的性能跟數(shù)據(jù)量有很大的關(guān)系,此外最好有一個(gè)字段長度較短的二級(jí)索引。

拓展:

另外,多說一下,關(guān)于網(wǎng)上說的那些索引失效的情況,大多都是片面的,我這里只說一點(diǎn)。量變才能引起質(zhì)變,索引的失效取決于你圈定數(shù)據(jù)的范圍,若你圈定的數(shù)據(jù)量占整體數(shù)據(jù)量的比例過高,則會(huì)放棄使用索引,反之則會(huì)優(yōu)先使用索引。但是此規(guī)則并不是完美的,有時(shí)候可能與你預(yù)期的不同,也可以通過一些技巧強(qiáng)制使用索引,但這種方式少用。

舉個(gè)栗子:

通過上面這個(gè)表hospital_statistics_data,我進(jìn)行了如下查詢:

select * from hospital_statistics_data where hospital_code is not null;

此時(shí)這個(gè)sql會(huì)使用到hospital_code的索引嗎?

這里也不賣關(guān)子了,若hospital_code只有很少一部分?jǐn)?shù)據(jù)是null值,那么將不會(huì)走索引,反之則走索引。

原因就2個(gè)字:回表。

好比去買砂糖橘,如果你只買幾斤,那么你隨便挑筐里面好的就行。但是如果你要買一筐,我相信老板不會(huì)讓你在里面一個(gè)個(gè)挑,而是一次給你一整筐,當(dāng)然大家都不傻,都知道筐里里面肯定有那么幾個(gè)壞果子。但是這樣效率最高,而且對(duì)老板來說損失更小。

執(zhí)行過程

1.首先在server層維護(hù)一個(gè)count變量

2.server層向InnoDB引擎要第一條記錄

3.InnoDB找到第一條二級(jí)索引記錄,并返回給server層(注意:由于此時(shí)只是統(tǒng)計(jì)記錄數(shù)量,所以并不需要回表)

4.由于COUNT函數(shù)的參數(shù)是*,MySQL會(huì)將*當(dāng)作常數(shù)0處理。由于0并不是NULL,server層給count變量加1。

5.server層向InnoDB要下一條記錄。

6.InnoDB通過二級(jí)索引記錄的next_record屬性找到下一條二級(jí)索引記錄,并返回給server層。

7.server層繼續(xù)給count變量加1。

8.重復(fù)上述過程,直到InnoDB向server層返回沒記錄可查的消息。

9.server層將最終的count變量的值發(fā)送到客戶端。

關(guān)于“count(*)很慢的原因是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí),可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,小編每天都會(huì)為大家更新不同的知識(shí)點(diǎn)。


文章題目:count(*)很慢的原因是什么
分享路徑:http://weahome.cn/article/gpiijs.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部