中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

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

發布時間:2023-01-06 09:45:12 來源:億速云 閱讀:142 作者:iii 欄目:MySQL數據庫

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

以下所有的內容均是基于,mysql 5.7 + InnoDB引擎, 進行的分析。

拓展:

MyISAM 如果沒有查詢條件,只是簡單的統計表中數據總數,將會返回的超快,因為service層中獲取到表信息中的總行數是準確的,而InnoDB只是一個估值。

實例

廢話不多說,先看一個例子。

以下是一張表數據量有100w,表中字段相對較短,整體數據量不算大。

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 '醫院編碼',
  `biz_type` tinyint NOT NULL COMMENT '1服務流程  2管理效果',
  `item_code` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '考核項目編碼',
  `item_name` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '考核項目名稱',
  `item_value` varchar(36) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '考核結果',
  `is_deleted` tinyint DEFAULT NULL COMMENT '是否刪除 0否 1是',
  `gmt_created` datetime DEFAULT NULL COMMENT '創建時間',
  `gmt_modified` datetime DEFAULT NULL COMMENT 'gmt_modified',
  `gmt_deleted` datetime(3) DEFAULT '9999-12-31 23:59:59.000' COMMENT '刪除時間',
  PRIMARY KEY (`pk_id`)
) DEFAULT CHARSET=utf8mb4  COMMENT='醫院統計數據';

此表初始狀態只有一個聚簇索引

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

1)在只有一個聚簇索引的情況下看一下執行計劃。

EXPLAIN select COUNT(*) from hospital_statistics_data;

結果:

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

這里只關注以下幾個屬性。

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

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

  • key_len: 索引長度8字節。

這里有很關鍵的一點:count(*)也會走索引,在當前情況下使用了聚簇索引。

好,再往下看。

2)存在一個非聚簇索引(二級索引)

給表添加一個hospital_code索引。

alter table hospital_statistics_data add index idx_hospital_code(hospital_code)

此時表中存在2個索引,主鍵 hospital_code

同樣的,再執行一下:

EXPLAIN select COUNT(*) from hospital_statistics_data;

結果:

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

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

是不是覺得有點“神奇”。

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

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

3)存在兩個非聚簇索引(二級索引)

在上面的基礎上,再添加一個二級索引。

alter table hospital_statistics_data add index idx_biz_type(biz_type)

此時表中存在3個索引,主鍵 、hospital_code 和 biz_type。

同樣的,執行一下:

EXPLAIN select COUNT(*) from hospital_statistics_data;

結果:

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

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

變成新添加的idx_biz_type。

先不說為何會產生以上的變化,繼續往下分析。

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

這4種情況,與count(*)的執行計劃有何區別。

  • count(1)

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

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

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

  • count(index)

這里選取biz_type索引字段。

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

  • count(無索引)

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

小結:

  • count(index) 會使用當前index指定的索引。

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

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

必要知識點

  • mysql 分為service層引擎層

  • 所有的sql在執行前會經過service層的優化,優化分為很多類型,簡單的來說可分為成本規則

  • 執行計劃所反映的是service層經過sql優化后,可能的執行過程。并非絕對(免得有些人說我只看執行計劃過于片面)。絕大多數情況執行計劃是可信的

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

  • 拋開數據內存,只談數據量,都是扯淡。什么500w就是極限,什么2個表以上的join都需要優化了,什么is null不會走索引等,純純的放屁。

  • 相信一點,編寫mysql代碼的人比,看此文章的大部分人都要優秀。他們會盡可能在執行前,對我這樣菜逼寫的亂七八糟的sql進行優化。

原因分析

其實原因非常非常簡單,上面也說了,service層會基于成本進行優化

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

我相信正常人都會使用非聚簇索引

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

那肯定選擇最短的,占用內存最小的一個呀,在回頭看看上面的實例,還迷惑嗎。

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

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

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

官方文檔:

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

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

既然會自動走索引,那么上面那個所謂的速度排序還覺得對嗎? count(*)的性能跟數據量有很大的關系,此外最好有一個字段長度較短的二級索引。

拓展:

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

舉個栗子:

通過上面這個表hospital_statistics_data,我進行了如下查詢:

select * from hospital_statistics_data where hospital_code is not null;

此時這個sql會使用到hospital_code的索引嗎?

這里也不賣關子了,若hospital_code只有很少一部分數據是null值,那么將不會走索引,反之則走索引。

原因就2個字:回表

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

執行過程

1.首先在server層維護一個count變量

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

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

4.由于COUNT函數的參數是*,MySQL會將*當作常數0處理。由于0并不是NULL,server層給count變量加1。

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

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

7.server層繼續給count變量加1。

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

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

關于“count(*)很慢的原因是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

峨眉山市| 镇康县| 苍山县| 保亭| 九龙城区| 文登市| 繁昌县| 二连浩特市| 巴青县| 罗定市| 鞍山市| 吐鲁番市| 定陶县| 兴城市| 扎囊县| 东宁县| 工布江达县| 龙川县| 竹北市| 上虞市| 襄樊市| 兰州市| 永福县| 九龙坡区| 日照市| 漯河市| 福建省| 东兴市| 平顶山市| 仁布县| 曲阜市| 安龙县| 虞城县| 定南县| 西乌珠穆沁旗| 安福县| 巴彦淖尔市| 南皮县| 桃园县| 涟源市| 冀州市|