色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

count()的原理

jf_ro2CN3Fa ? 來源:小白debug ? 2023-01-30 11:30 ? 次閱讀


在我們平時查詢數據庫表記錄行數的時候,經常會使用到count()函數,比如使用count(*)、count(1)或者count(某個主鍵或索引列),今天我們來對比下這些用法中哪個性能最優秀!

創建短信表

比如說,你有一張短信表(sms) ,里面放了各種需要發送的短信信息

731da068-9fb9-11ed-bfe3-dac502259ad0.pngsms建表sql733abe5a-9fb9-11ed-bfe3-dac502259ad0.pngsms表

需要注意的是state字段,為0的時候說明這時候短信還未發送。

此時還會有一個異步線程 不斷的撈起未發送(state=0)** 的短信數據,執行發短信操作,發送成功之后state字段會被*置為1(已發送) 。也就是說未發送的數據會不斷變少* 。

734b5120-9fb9-11ed-bfe3-dac502259ad0.png異步線程發送短信

假設由于某些原因,你現在需要做一些監控,比如監控的內容是,你的sms數據表里還有沒有state=0(未發送)的短信,方便判斷一下堆積的未發送短信大概在什么樣的一個量級。

為了獲取滿足某些條件的行數是多少 ,我們一般會使用count()方法

這時候為了獲取未發送的短信數據,我們很自然就想到了使用下面的sql語句進行查詢。

selectcount(*)fromsmswherestate=0;

然后再把獲得數據作為打點發給監控服務。

當數據表小的時候,這是沒問題的,但當數據量大的時候,比如未發送的短信到了百萬量級 的時候,你就會發現,上面的sql查詢時間會變得很長,最后timeout報錯,查不出結果了

為什么?

我們先從count()方法的原理 聊起。

基于 Spring Boot + MyBatis Plus + Vue & Element 實現的后臺管理系統 + 用戶小程序,支持 RBAC 動態權限、多租戶、數據權限、工作流、三方登錄、支付、短信、商城等功能

  • 項目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 視頻教程:https://doc.iocoder.cn/video/

count()的原理

count()方法的目的是計算當前sql語句查詢得到的非NULL的行數

我們知道mysql是分為server層和存儲引擎層的

7357b2bc-9fb9-11ed-bfe3-dac502259ad0.pngMysql架構

存儲引擎層里可以選擇各種引擎進行存儲,最常見的是innodb、myisam。具體使用哪個存儲引擎,可以通過建表sql里的ENGINE字段進行指定。比如這篇文章開頭的建表sql里用了ENGINE=InnoDB,那這張表用的就是innodb引擎。

雖然在server層都叫count()方法,但在不同的存儲引擎下,它們的實現方式是有區別的。

比如同樣是讀全表數據 select count(*) from sms;語句。

使用 myisam引擎 的數據表里有個記錄當前表里有幾行數據的字段,直接讀這個字段返回就好了,因此速度快得飛起。

而使用innodb引擎 的數據表,則會選擇體積最小的索引樹 ,然后通過遍歷葉子節點的個數挨個加起來,這樣也能得到全表數據。

因此回到文章開頭的問題里,當數據表行數變大后,單次count就需要掃描大量的數據 ,因此很可能就會出現超時報錯。

那么問題就來了。

為什么innodb不能像myisam那樣實現count()方法

myisam和innodb這兩個引擎,有幾個比較明顯的區別,這個是八股文常考了。

其中最大的區別在于myisam不支持事務,而innodb支持事務。

而事務,有四層隔離級別,其中默認隔離級別就是可重復讀隔離級別(RR)

7365fa16-9fb9-11ed-bfe3-dac502259ad0.png四層隔離級別

innodb引擎通過MVCC實現了可重復隔離級別 ,事務開啟后,多次執行同樣的select快照讀 ,要能讀到同樣的數據。

于是我們看個例子。

73724384-9fb9-11ed-bfe3-dac502259ad0.png為什么innodb不單獨記錄表行數

對于兩個事務A和B,一開始sms表假設就2條 數據,那事務A一開始確實是讀到2條數據。事務B在這期間插入了1條數據,按道理數據庫其實有3條數據了,但由于可重復讀的隔離級別,事務A依然還是只能讀到2條數據。

因此由于事務隔離級別的存在,不同的事務在同一時間下,看到的表內數據行數是不一致的 ,因此innodb,沒辦法,也沒必要像myisam那樣單純的加個count字段信息在數據表上。

那如果不可避免要使用count(),有沒有辦法讓它快一點?

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現的后臺管理系統 + 用戶小程序,支持 RBAC 動態權限、多租戶、數據權限、工作流、三方登錄、支付、短信、商城等功能

  • 項目地址:https://github.com/YunaiV/yudao-cloud
  • 視頻教程:https://doc.iocoder.cn/video/

各種count()方法的原理

count()的括號里,可以放各種奇奇怪怪的東西,想必大家應該看過,比如放個星號*,放個1,放個索引列啥的。

我們來分析下他們的執行流程。

count方法的大原則是server層會從innodb存儲引擎里讀來一行行數據,并且只累計非null的值 。但這個過程,根據count()方法括號內的傳參,有略有不同。

count(*)

server層拿到innodb返回的行數據,不對里面的行數據做任何解析和判斷 ,默認取出的值肯定都不是null,直接行數+1。

count(1)

server層拿到innodb返回的行數據,每行放個1進去,默認不可能為null,直接行數+1.

count(某個列字段)

由于指明了要count某個字段,innodb在取數據的時候,會把這個字段解析出來 返回給server層,所以會比count(1)和count(*)多了個解析字段出來的流程。

  • 如果這個列字段是主鍵id ,主鍵是不可能為null的,所以server層也不用判斷是否為null,innodb每返回一行,行數結果就+1.
  • 如果這個列是普通索引字段 ,innodb一般會走普通索引 ,每返回一行數據,server層就會判斷這個字段是否為null,不是null的情況下+1。當然如果建表sql里字段定義為not null的話,那就不用做這一步判斷直接+1。
  • 如果這個列沒有加過索引 ,那innodb可能會全表掃描,返回的每一行數據,server層都會判斷這個字段是否為null,不是null的情況下+1。同上面的情況一樣,字段加了not null也就省下這一步判斷了。

理解了原理后我們大概可以知道他們的性能排序是

count(*)≈count(1)>count(主鍵id)>count(普通索引列)>count(未加索引列)

所以說count(*),已經是最快的了。

知道真相的我眼淚掉下來。

那有沒有其他更好的辦法?

允許粗略估計行數的場景

我們回過頭來細品下文章開頭的需求,我們只是希望知道數據庫里還有多少短信是堆積在那沒發的,具體是1k還是2k其實都是差不多量級,等到了百萬以上,具體數值已經不重要了,我們知道它現在堆積得很離譜,就夠了。因此這個場景,其實是允許使用比較粗略 的估計的。

那怎么樣才能獲得粗略的數值呢?

還記得我們平時為了查看sql執行計劃用的explain命令 不。

其中有個rows ,會用來估計 接下來執行這條sql需要掃描和檢查多少行。它是通過采樣的方式計算出來的,雖然會有一定的偏差,但它能反映一定的數量級。

737e3306-9fb9-11ed-bfe3-dac502259ad0.pngexplain里的rows

有些語言的orm里可能沒有專門的explain語法,但是肯定有執行raw sql的功能,你可以把explain語句當做raw sql傳入,從返回的結果里將rows那一列讀出來使用。

一般情況下,explain的sql如果能走索引,那會比不走索引的情況更準 。單個字段的索引會比多個字段組成的復合索引要準。索引區分度越高,rows的值也會越準。

這種情況幾乎滿足大部分的監控場景。但總有一些場景,它要求必須得到精確的行數,這種情況該怎么辦呢?

必須精確估計行數的場景

這種場景就比較頭疼了,但也不是不能做。

我們可以單獨拉一張新的數據庫表,只為保存各種場景下的count。

CREATETABLE`count_table`(
`id`intNOTNULLAUTO_INCREMENTCOMMENT'主鍵',
`cnt_what`char(20)NOTNULLDEFAULT''COMMENT'各種需要計算的指標',
`cnt`tinyintNOTNULLCOMMENT'cnt指標值',
PRIMARYKEY(`id`),
KEY`idx_cnt_what`(`cnt_what`)
)ENGINE=InnoDBDEFAULTCHARSET=utf8mb4;
738e0fce-9fb9-11ed-bfe3-dac502259ad0.pngcount_table表保存各種場景下的count

當需要獲取某個場景下的cout值時,可以使用下面的sql進行直接讀取,快得飛起

selectcntfromcount_tablewherecnt_what="未發送的短信數量";

那這些count的結果值從哪來呢?

這里分成兩種情況。

實時性要求較高的場景

如果你對這個cnt計算結果的實時性要求很高,那你需要將更新cnt的sql加入到對應變更行數的事務中

比如我們有兩個事務A和B,分別是增加未發送短信和減少未發送短信。

73997e22-9fb9-11ed-bfe3-dac502259ad0.png將更改表行數的操作放入到事務里

這樣做的好處 是事務內的cnt行數依然符合隔離級別,事務回滾的時候,cnt的值也會跟著回滾。

壞處 也比較明顯,多個線程對同一個cnt進行寫操作,會觸發悲觀鎖,多個線程之間需要互相等待。對于高頻寫的場景 ,性能會有折損。

實時性沒那么高的場景

如果實時性要求不高的話,比如可以一天一次,那你可以通過全表掃描后做計算。

舉個例子,比如上面的短信表,可以按id排序 ,每次取出1w條數據,記下這一批里最大的id,然后下次從最大id開始再拿1w條數據出來,不斷循環。

對于未發送的短信,就只需要在撈出的那1w條數據里,篩選出state=0的條數。

73a48c36-9fb9-11ed-bfe3-dac502259ad0.pngbatch分批獲取短信表

當然如果有條件,這種場景最好的方式還是消費binlog將數據導入到hive里 ,然后在hive里做查詢,不少公司也已經有現成的組件可以做這種事情,不用自己寫腳本,豈不美哉。

73b1065a-9fb9-11ed-bfe3-dac502259ad0.pngmysql同步hive

總結

  • mysql用count方法查全表數據 ,在不同的存儲引擎里實現不同,myisam有專門字段記錄全表的行數,直接讀這個字段就好了。而innodb則需要一行行去算。
  • 性能方面 count(*) ≈ count(1) > count(主鍵id) > count(普通索引列) > count(未加索引列),但哪怕是性能最好的count(*),由于實現上就需要一行行去算,所以數據量大的時候就是不給力。
  • 如果確實需要獲取行數,且可以接受不那么精確的行數(只需要判斷大概的量級) 的話,那可以用explain里的rows,這可以滿足大部分的監控場景,實現簡單。
  • 如果要求行數準確 ,可以建個新表,里面專門放表行數的信息。
  • 如果對實時性要求比較高 的話,可以將更新行數的sql放入到對應事務里,這樣既能滿足事務隔離性,還能快速讀取到行數信息。
  • 如果對實時性要求不高 ,接受一小時或者一天的更新頻率,那既可以自己寫腳本遍歷全表后更新行數信息。也可以將通過監聽binlog將數據導入hive,需要數據時直接通過hive計算得出。


審核編輯 :李倩


聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 數據
    +關注

    關注

    8

    文章

    7134

    瀏覽量

    89410
  • SQL
    SQL
    +關注

    關注

    1

    文章

    773

    瀏覽量

    44219
  • 程序員
    +關注

    關注

    4

    文章

    953

    瀏覽量

    29833

原文標題:程序員新人頻繁使用count(*),被組長批評后怒懟:性能并不拉垮!

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    verilog計數器代碼為什么要使用這句話if (count===8\'bxxxxxxxx)count=8\'b0000_0000;

    and output variables. input reset, output reg [7:0] count ); /It also includes the definition of parameters
    發表于 12-21 14:49

    常用SQL函數及其用法

    的 SQL 函數及其用法: 一、聚合函數(Aggregate Functions) 聚合函數對一組值執行計算,并返回單個值。 COUNT() 用途 :返回匹配指定條件的行數。 示例 : SELECT
    的頭像 發表于 11-19 10:18 ?407次閱讀

    【BearPi-Pico H3863星閃開發板體驗連載】systick

    12:47:06:309 -> count_after_get_ms = 11355, count_before_get_ms = 10355 2024-10-18 12:47:06
    發表于 10-18 19:07

    【星閃派物聯網開發套件體驗連載】systick

    ,使用串口工具即可查看運行日志,日志打印波特率為115200。 此程序 com信息: 2024-10-18 12:47:06:309 -> count
    發表于 10-18 14:10

    在TLE987X UM中ADC2的CTRL2的SEL_TS_COUNT這個參數怎么理解?

    Hi 各位好! 咨詢下在TLE987X UM中ADC2的CTRL2的SEL_TS_COUNT這個參數怎么理解?是修改溫度傳感器的采樣周期?
    發表于 07-23 06:39

    調用esp_mesh_lite_try_sending_msg后free heap越來越少怎么解決?

    rssi: -14, free heap: 167752 I (604216) uart_port: uart1 recv, count: 0, len: 4 I (605076
    發表于 06-28 13:25

    請問ULP處理器數據如何與主控交互數據的?

    根據官方提供的ULP計數例程里,主控代碼里的ulp_edge_count 變量只在主控區域被引用,并沒有跟ULP關聯。而ULP代碼里的計數變量edge_count也是跟主控代碼沒有任何關聯。它們之間
    發表于 06-26 07:12

    CY8C4247AZI-M485 EEPROM相關的問題求解

    的位置 */ #define RESET_COUNT_LOCATION 13u #define RESET_COUNT_SIZE 2u /* ASCII"9"
    發表于 05-29 07:42

    STM32F103ZE IO口外部中斷計算PWM波形的占空比和頻率遇到的疑問求解

    無效。 TIMER_CNT Count[2]={0};//Count[0] is sample value, Count[1] is return value. u16 TITimer=0; void
    發表于 05-10 06:41

    為什么STM8S003F3P6的uart1發送數據是亂碼的?

    (u32 ms){ u32 count =1000; for(;ms>0;ms--){ for(;count>0;count--){ _asm(\"nop
    發表于 05-09 07:49

    STM8L052寫EEPROM偶爾會死機的原因?怎么解決?

    EEPROM的字函數 uint8_t EEPROM_Buff_Write (uint16_t addr, uint8_t *buff, uint16_t len) { uint16_t i, count
    發表于 04-30 07:49

    用STM9S003F3做IAP升級,寫APP程序到APP地址區時有字節沒有寫成功的原因 ?

    )) { uint16_t Count; /* Point to the first block address */ /* Standard programming mode
    發表于 04-23 06:49

    求助,關于STM32F030定時器通道設置問題求解

    |= TIM_IT_CC1;/* 使能CC1中斷 */ Set_T1M3_CC1_Count = _uiTimeOut; } else if (_CC == 2
    發表于 04-09 07:17

    鴻蒙OS應用 示例:【數字滾動計時】

    HarmonyOS 數字滾動計時 類似 vue-count-to
    的頭像 發表于 03-27 15:24 ?405次閱讀
    鴻蒙OS應用 示例:【數字滾動計時】

    新手入門的簡單小例子-05-01

    ); parameter SEC_1S_MAX = 26\'d50_000_000; reg [25:0] count; //板載時鐘轉化為sec計數標志 always @(posedge clk
    發表于 03-26 11:41
    主站蜘蛛池模板: 欧美亚洲国产激情一区二区 | 久久电影精品久久99久久 | 大香交伊人 | 国产精品日韩欧美一区二区三区 | 国产女人乱人伦精品一区二区 | 折磨比基尼美女挠肚子 | 亚洲AV天堂无码麻豆电影 | 99精品视频免费在线观看 | 亚洲日韩在线观看 | 久久精品黄色 | 国产午夜人成在线视频麻豆 | 综合人妻久久一区二区精品 | 免费一级特黄欧美大片久久网 | 午夜A级理论片左线播放 | 欧美人与善交大片 | 国产精品久久久久久久久齐齐 | 波多野结衣教师系列6 | YELLOW免费观看2019 | 亚洲精品中文字幕一二三四区 | 色综合久久网女同蕾丝边 | 男女亲吻摸下面吃奶视频 | 国产精品免费观看视频播放 | 撅高 自己扒开 调教 | 国产爱豆剧果冻传媒在线 | 国内精品免费久久影院 | 精品爽爽久久久久久蜜臀 | 午夜在线视频国产极品片 | 亚洲欧美中文在线一区 | 67194成网页发布在线观看 | 亚洲精品国产国语 | 成人欧美尽粗二区三区AV | 色网址在线观看 | 久久精品在现线观看免费15 | 久久精品AV无码亚洲色欲 | 91九色网址 | 漂亮的保姆5电影免费观看完整版中文 | 伦理片qvod | 日韩欧美视频一区二区在线观看 | 国产精品无码麻豆放荡AV | 亚洲风情无码免费视频 | a视频在线观看免费 |