Mysql性能優化:為什麼你的count(*)這麼慢?

来源:https://www.cnblogs.com/Chenjiabing/archive/2020/04/03/12625559.html
-Advertisement-
Play Games

導讀 在開發中一定會用到統計一張表的行數,比如一個交易系統,老闆會讓你每天生成一個報表,這些統計信息少不了 sql 中的count函數。 但是隨著記錄越來越多,查詢的速度會越來越慢,為什麼會這樣呢?Mysql內部到底是怎麼處理的? 今天這篇文章將從Mysql內部對於count函數是怎樣處理的? 本文 ...


導讀

  • 在開發中一定會用到統計一張表的行數,比如一個交易系統,老闆會讓你每天生成一個報表,這些統計信息少不了 sql 中的count函數。
  • 但是隨著記錄越來越多,查詢的速度會越來越慢,為什麼會這樣呢?Mysql內部到底是怎麼處理的?
  • 今天這篇文章將從Mysql內部對於count函數是怎樣處理的?
  • 本文首發於作者微信公眾號【碼猿技術專欄】Mysql性能優化:為什麼你的count(*)這麼慢?,原創不易,喜歡的請支持一下,謝謝!!!

count的實現方式

  • 在Mysql中的不同的存儲引擎對count函數有不同的實現方式。
  • MyISAM引擎把一個表的總行數存在了磁碟上,因此執行count(*)的時候會直接返回這個數,效率很高(沒有where查詢條件)。
  • InnoDB引擎並沒有直接將總數存在磁碟上,在執行count(*)函數的時候需要一行一行的將數據讀出來,然後累計總數。

為什麼InnoDB不將總數存起來?

  • 說到InnoDB相信讀者總會想到其支持事務的特性,事務具有隔離性,如果將總數存起來,怎麼保證各個事務之間的總數的一致性呢?不明白的看圖

  • 事務A事務B中的count(*)的執行結果是不同的,因此InnoDB引擎在每個事務中返回多少行是不確定的,只能一行一行的讀出來用來判斷總數。

如何提升count效率

  • InnoDB對於如何提升count(*)的查詢效率,網上有多種解決辦法,這裡主要介紹三種,並分析可行性。

show table status

  • show table status這個命令能夠很快的查詢出資料庫中每個表的行數,但是真的能夠替代count(*)嗎?
  • 答案是不能。原因很簡單,這個命令統計出來的值是一個「估值」,因此是不准確的,官方文檔說誤差大概在40%-50%
  • 因此這種方法直接pass,不准確還用它幹嘛。

緩存系統存儲總數

  • 這種方法也是最容易想到的,增加一行就+1,刪除一行就-1,並且緩存系統讀取也是很快,既簡單又方便的為什麼不用?

  • 緩存系統和Mysql是兩個系統,比如redisMysql這兩個是典型的比較。兩個系統最難的就是在高併發下無法保證數據的一致性。  

  • 通過上面兩張圖,無論是redis計數+1還是insert into user先執行,最終都會導致數據在邏輯上的不一致。第一張圖會出現redis計數少了,第二張圖雖然計數正確了但是並沒有查詢出插入的那一行數據。

  • 在併發系統裡面,我們是無法精確控制不同線程的執行時刻的,因為存在圖中的這種操作序列,所以,我們說即使Redis正常工作,這個計數值還是邏輯上不精確的。

在資料庫保存計數

  • 通過緩存系統保存的分析得知了使用緩存無法保證數據在邏輯上的一致性,因此我們想到了直接使用資料庫來保存,有了「事務」的支持,也就保證了數據的一致性了。

  • 如何使用呢?很簡單,直接將計數保存在一張表中(table_name,total)

  • 至於執行的邏輯只需要將緩存系統中redis計數+1改成total欄位+1即可,如下圖: 

  • 由於在同一個事務中,保證了數據在邏輯上的一致性。

不同count的用法

  • count()是一個聚合函數,對於返回的結果集,一行行地判斷,如果count函數的參數不是NULL,累計值就加1,否則不加。最後返回累計值。
  • count的用法有多種,分別是count(*)count(欄位)count(1)count(主鍵id)。那麼多種用法,到底有什麼差別呢?當然,「前提是沒有where條件語句」
  • count(id):InnoDB引擎會遍歷整張表,把每一行的id值都取出來,返回給server層。server層拿到id後,判斷是不可能為空的,就按行累加。
  • count(1):InnoDB引擎遍歷整張表,但不取值。server層對於返回的每一行,放一個數字1進去,判斷是不可能為空的,按行累加。
  • count(欄位)count(*):不會把全部欄位取出來,而是專門做了優化,不取值。count(*)肯定不是null,按行累加。
    • 如果這個“欄位”是定義為not null的話,一行行地從記錄裡面讀出這個欄位,判斷不能為null,按行累加;
    • 如果這個欄位定義允許為null,那麼執行的時候,判斷到有可能是null,還要把值取出來再判斷一下,不是null才累加。
  • 所以結論很簡單:「按照效率排序的話,count(欄位)<count(主鍵id)<count(1)count(*),所以建議讀者,儘量使用count(*)。」
  • 「註意」:這裡肯定有人會問,count(id)不是走的索引嗎,為什麼查詢效率和其他的差不多呢?陳某在這裡解釋一下,雖然走的索引,但是還是要一行一行的掃描才能統計出來總數。

總結

  • MyISAM表雖然count(*)很快,但是不支持事務;
  • show table status命令雖然返回很快,但是不准確;
  • InnoDB直接count(*)會遍歷全表(沒有where條件),雖然結果準確,但會導致性能問題。
  • 緩存系統的存儲計數雖然簡單效率高,但是無法保證數據的一致性。
  • 資料庫保存計數很簡單,也能保證數據的一致性,建議使用。
  • 「思考題,讀者留言區討論」:在系統高併發的情況下,使用資料庫保存計數,是先更新計數+1,還是先插入數據。即是先update total+=1還是先insert into

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Cannot connect to the Docker daemon. Is the docker daemon running on this host? 原因: 沒有啟動docker服務 service docker start 效果: docker stop showdoc # 停止容器 d ...
  • 在目錄下創建新文件時出現 ,原因是用戶許可權不足。 解決方法是給用戶添加修改許可權,在根目錄執行以下命令: Linux/Unix 的文件調用許可權分為三級 : 文件擁有者、群組、其他。利用 chmod 可以藉以控制文件如何被他人所調用。 語法為: R : 對目前目錄下的所有文件與子目錄進行相同的許可權變更( ...
  • MySQL是一個開放源碼的小型關聯式資料庫管理系統,開發者為瑞典MySQL AB公司, 目前屬於Oracle公司,MySQL被廣泛地應用在Internet上的中小型網站中。由於其體積小、速度快、總體擁有成本低,尤其是開放源碼這一特點,許多中小型網站為了降低網站總體擁有成本而選擇了MySQL作為網站數 ...
  • 1、 Linux操作系統簡介 Linux具有如下優點: 穩定、免費或者花費少 安全性高 多任務,多用戶 耗資源少 由於內核小,所以它可以支持多種電子產品,如:Android手機、PDA等。 2、 Linux發展趨勢 隨著IT產業的不斷發展,用戶對網站體驗要求也越來越高,目前主流網站後端承載系統都是L ...
  • https://www.cnblogs.com/wt7018/p/11929359.html MongoDB聚合(aggregate) 一、基礎 1、什麼是聚合? 聚合是基於數據處理的聚合管道,每個文檔通過一個有多個階段(stage)組成的管道可以對每個階段的管道進行分組、過濾等功能,然後經過一系列 ...
  • 1. 業務說:“…… bulabula……,這個需求很簡單,怎麼實現我不管?” 面對霸氣側漏的業務需求,由於沒有大數據知識儲備,咱心裡沒底,咱也不敢問,咱也不敢說,只能靜下來默默儲備、默默尋覓解決方案。 關註“一猿小講”公眾號的小伙伴們,今天有福啦,因為今天我們將一起跳出系統之外,共同邁入大數據之 ...
  • 環境 MySQL5.6 表結構及數據 sql DROP TABLE IF EXISTS ; CREATE TABLE ( int(11) NOT NULL AUTO_INCREMENT, varchar(20) NOT NULL, varchar(20) NOT NULL, double(10, 3 ...
  • SQL語句還是多去用才能掌握,再多理論,白搭 SQL概述 SQL的產生與發展 SQL的特點 SQL功能十分強大,針對數據的操作,核心功能只用9個動詞就可以完成。而且其語句有點類似英語口語一看基本就能明白它要表達的意思。 SQL語言對資料庫三級模式的支持 SQL語言作為大多數資料庫使用的共同數據存取語 ...
一周排行
    -Advertisement-
    Play Games
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...