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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...