<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Mysql性能優(yōu)化:為什么count(*)這么慢?

          共 1829字,需瀏覽 4分鐘

           ·

          2020-09-07 10:26


          導(dǎo)讀

          • 在開發(fā)中一定會用到統(tǒng)計一張表的行數(shù),比如一個交易系統(tǒng),老板會讓你每天生成一個報表,這些統(tǒng)計信息少不了sql中的count函數(shù)。
          • 但是隨著記錄越來越多,查詢的速度會越來越慢,為什么會這樣呢?Mysql內(nèi)部到底是怎么處理的?
          • 今天這篇文章將從Mysql內(nèi)部對于count函數(shù)是怎樣處理的來展開詳細的講述。

          count的實現(xiàn)方式

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

          為什么InnoDB不將總數(shù)存起來?

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

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

          如何提升count效率

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

          show table status

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

          緩存系統(tǒng)存儲總數(shù)

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

          • 緩存系統(tǒng)和Mysql是兩個系統(tǒng),比如redisMysql這兩個是典型的比較。兩個系統(tǒng)最難的就是在高并發(fā)下無法保證數(shù)據(jù)的一致性。通過以下兩圖我們來理解一下:

          • 通過上面兩張圖,無論是redis計數(shù)+1還是insert into user先執(zhí)行,最終都會導(dǎo)致數(shù)據(jù)在邏輯上的不一致。第一張圖會出現(xiàn)redis計數(shù)少了,第二張圖雖然計數(shù)正確了但是并沒有查詢出插入的那一行數(shù)據(jù)。

          • 在并發(fā)系統(tǒng)里面,我們是無法精確控制不同線程的執(zhí)行時刻的,因為存在圖中的這種操作序列,所以,我們說即使Redis正常工作,這個計數(shù)值還是邏輯上不精確的。

          在數(shù)據(jù)庫保存計數(shù)

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

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

          • 至于執(zhí)行的邏輯只需要將緩存系統(tǒng)中redis計數(shù)+1改成total字段+1即可,如下圖:

          • 由于在同一個事務(wù)中,保證了數(shù)據(jù)在邏輯上的一致性。

          不同count的用法

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

          總結(jié)

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



          瀏覽 32
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  免费一级片 | 少妇性受XXXX黑人XYX性爽 | 大香蕉网在线视频 | 美女操鸡巴视频 | 欧美色图 在线视频 |