偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

MySQL:如何才能實現(xiàn)高效數(shù)據(jù)統(tǒng)計

數(shù)據(jù)庫 MySQL
對于count(*)來說,InnoDB只好把數(shù)據(jù)一行行讀出來,對可見的行進(jìn)行統(tǒng)計。因此,InnoDB不能像MyISAM引擎一樣在磁盤保存數(shù)據(jù)行樹。

我們在業(yè)務(wù)中經(jīng)常遇到的一個場景就是統(tǒng)計當(dāng)前已有的業(yè)務(wù)數(shù)據(jù),比如說商品庫內(nèi)商品的數(shù)量、每天的用戶訂單數(shù)量等等。

這時候,我們一般就需要MySQL的統(tǒng)計功能實現(xiàn)。

1 count(*)實現(xiàn)方式

不同的引擎,count(*)實現(xiàn)邏輯也不一致:

  • MyISAM引擎將一個表的總數(shù)存在磁盤上,當(dāng)執(zhí)行count(*)沒有where條件時,直接從磁盤讀取數(shù)據(jù)返回即可,效率比較高;如果是有where條件,則和InnoDB實現(xiàn)邏輯類似;
  • InnoDB執(zhí)行count(*)需要將一行行數(shù)據(jù)從引擎中讀取出來后累積計數(shù);

InnoDB利用多版本控制機制支持事務(wù),一行記錄會記錄多個MVCC,統(tǒng)計行數(shù)這一行為和隔離級別直接相關(guān)。在RR級別下,每一行記錄都要判斷自己是否對這個會話可見,每個會話也會執(zhí)行增刪改操作,導(dǎo)致每個事務(wù)統(tǒng)計的行數(shù)不一致,因此,對于count(*)來說,InnoDB只好把數(shù)據(jù)一行行讀出來,對可見的行進(jìn)行統(tǒng)計。因此,InnoDB不能像MyISAM引擎一樣在磁盤保存數(shù)據(jù)行樹。

圖片

表中,會話C沒有顯示開始事務(wù),因此每條語句都是獨立事務(wù),由于AB會話都沒有提交事務(wù),因此,AB的修改對C不可見。

事實上,InnoDB對count(*)做了一定優(yōu)化,由于InnoDB是索引組織表,主鍵索引樹的葉子節(jié)點是數(shù)據(jù),普通索引樹的葉子階段是主鍵值,因此,普通索引樹比主鍵索引樹小很多。執(zhí)行count(*)的邏輯就是遍歷,因此,MySQL優(yōu)化器會選擇最小的索引樹用于遍歷,相對于每次都讀取所有的數(shù)據(jù)行,只是遍歷主鍵,自然IO開銷要小的多。

因此說:在保證邏輯正確的前提下,盡量減少掃描的數(shù)據(jù)量,是數(shù)據(jù)庫系統(tǒng)設(shè)計的通用法則之一。

另外,還有一個顯示行數(shù)的命令為:

show table status;

也會顯示表的行數(shù),但是這里的rows是預(yù)估值,這個預(yù)估值是根據(jù)隨機采集計算出來的,MySQL隨機取N頁數(shù)據(jù),計算出每頁中不同記錄數(shù),求取平均值后乘以總頁數(shù)得到的就是預(yù)估值。這個預(yù)估值是否接近真實值,取決于索引字段區(qū)分度、索引數(shù)據(jù)頁緊湊程度、是否存在頁分裂、索引空洞等元素。這個預(yù)估值也是造成MySQL選錯索引的原因。

2 如何實現(xiàn)計數(shù)邏輯

2.1 用緩存系統(tǒng)統(tǒng)計計數(shù)

如果是一般場景,使用緩存系統(tǒng)執(zhí)行計數(shù)是滿足需求的,即使說,由于redis服務(wù)集群異常重啟導(dǎo)致數(shù)據(jù)丟失,但是可以再次掃描一次表獲取表的總數(shù)。

但是如果是非常嚴(yán)謹(jǐn)?shù)膱鼍埃ㄣy行統(tǒng)計實際支付的訂單數(shù)據(jù)等),那可能有如下的問題。

第一個是緩存可能會丟失數(shù)據(jù),即使是開啟持久化,還是存在丟失數(shù)據(jù)可能性。redis持久化有RDB和AOF兩種方式;RDB按照備份策略,比如60秒1000個k-v被修改,備份過程中宕機,那么這個階段的所有更新都會丟失;AOF按照備份策略,比如 appendfsync always 策略,同步記錄所執(zhí)行的指令到日志文件,但是它的日志和mysql的WAL不同,它是寫后日志,可能指令執(zhí)行后寫日之前宕機,那這個數(shù)據(jù)就丟失了,雖然丟失數(shù)據(jù)較少且概率較低,但依然存在這個可能。

第二個是數(shù)據(jù)一致性保證問題,Redis和MySQL是兩個數(shù)據(jù)源,可以看成是一種分布式一致性的問題,而分布式一致性由于不能保證原子性,因此一般只能保證最終一致性,而不能保證實時一致性。

數(shù)據(jù)一致性問題目前可分為三類:

1.主從不一。解決辦法:半同步復(fù)制可以保證實時的一致性,因為寫時寫主和從之后才響應(yīng),只不顧這樣寫的并發(fā)上不去;其他訪問有強制讀主、消息中間件路由讀主和緩存是否失效讀主;

2.數(shù)據(jù)庫與緩存的不一。解決辦法:讀操作直接讀緩存,寫操作先更新到數(shù)據(jù)庫,淘汰緩存(程序需要保證兩個操作的原子性,如果淘汰失敗,則發(fā)一條小實現(xiàn)異步淘汰).由于該key的緩存已經(jīng)清理掉,那么下次讀的時候需要先讀數(shù)據(jù)庫,在重建緩存. 由于redis是單線程,保證了一個操作的原子性.可以通過設(shè)置appendfsync always來保證每次操作都把該操作記錄并落盤到aof文件里(不過一般redis該值為everysec),畢竟使用redis的目的不是為了保證acid.還是要根據(jù)業(yè)務(wù)來選擇 。

3.一個事務(wù)跨多個節(jié)點或者多種數(shù)據(jù)庫(分庫分表和銀行轉(zhuǎn)賬這種例子) 。目前好像都是通過2pc,3pc來保證的。 

2.2 用數(shù)據(jù)庫保存計數(shù)

在數(shù)據(jù)庫中設(shè)計單獨的計數(shù)表,將插入數(shù)據(jù)、刪除數(shù)據(jù)的SQL和更新計數(shù)表的SQL語句作為同一個事務(wù)執(zhí)行。

圖片圖片

在統(tǒng)計時,將讀取計數(shù)器和查詢最近數(shù)據(jù)也作為一個事務(wù)執(zhí)行,這樣拿到的就是理論上的實際值。

但是實際上,在高并發(fā)場景以及一般場景,這種統(tǒng)計的意義可能并不是很大,因為當(dāng)你剛剛統(tǒng)計的數(shù)據(jù),可能在返回的期間已經(jīng)有變化。

這時候再次有個問題:在并發(fā)系統(tǒng)性能的角度考慮,在事務(wù)序列里,是先插入操作記錄,還是應(yīng)該先更新計數(shù)表?

答案是:先插入新紀(jì)錄。

  • 因為插入新紀(jì)錄只會影響到行鎖和間隙鎖,但是更新計數(shù)表會占用計數(shù)表的寫鎖,而很多其他事務(wù)的插入操作就必須阻塞等待,即:并發(fā)度高的操作放在后面執(zhí)行,可以減少鎖等待;
  • 計數(shù)表是公用表,根據(jù)鎖的二階段協(xié)議,在需要的時候獲取,在事務(wù)提交的時候釋放,晚獲取可以減少并發(fā),提高吞吐量;

3 不同的count方法

count()方法是一個聚合函數(shù),對于返回的結(jié)果集,一行行判斷,如果count函數(shù)參數(shù)不是null,累計值+1,否則不加。最后返回累計值。

  • InnoDB存儲引擎查詢數(shù)據(jù)結(jié)果集;
  • Server層根據(jù)結(jié)果集進(jìn)行遍歷統(tǒng)計;

所以,count(*)、count(1)、count(主鍵)都表示返回滿足條件的結(jié)果集的總行數(shù);count(列)表示返回滿足條件的數(shù)據(jù)行里面,參數(shù)“字段”不為Null的總個數(shù)。

MySQL執(zhí)行統(tǒng)計的原則是:

  • server層要什么就給什么;
  • InnoDB只給必要的值;
  • 現(xiàn)在的優(yōu)化器只優(yōu)化了count(*)的語義為取行數(shù),其他的語句沒有做優(yōu)化。

count(主鍵):InnoDB引擎會遍歷整張表,把每一行的id取出來(存在數(shù)據(jù)行數(shù)據(jù)解析),把主鍵返回給server層(存在字段值拷貝)。判定id是否為空,不為空直接按行累加即可(這里應(yīng)該是可以優(yōu)化的,因為主鍵一定不允許為空);同時,count(id)可能會走最小的索引來遍歷,并不一定非要走主鍵索引;

count(1):InnoDB引擎遍歷整張表,但是無需取值。server層對返回的每一行放一個數(shù)字1,按行累加;

count(column):

  • 如果字段不允許為空,一行行從記錄里面讀取這個字段,按行累加;
  • 如果字段允許為空,一行行從記錄讀取字段后,先判定是否為null,如果為null,則過濾掉,不為Null,則累加;
  • 如果字段為索引,那么這里的統(tǒng)計就會走該索引;

count(*):count(*)不取值,按行累加即可;

MySQL文檔中有如下說明:

InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) operations in the same way. There is no performance difference。

因此,按照效率排序為:count(字段)<count(主鍵 id)<count(1)=count(*),所以我建議你,盡量使用 count(*)。

從獲取的數(shù)值來看,count(字段)也一定是最小的,因為列字段的值可能為null。

4 本章回顧問題

把該講內(nèi)容總結(jié)為幾個問題, 大家復(fù)習(xí)的時候可以先嘗試回答這些問題檢查自己的掌握程度:

  1. count(*)的實現(xiàn)方式在MySAM引擎和InnoDB引擎的實現(xiàn)方式各是怎么樣的? 為什么會有這種不同?
  2. 使用緩存保存count總數(shù)存在什么問題?
  3. 如果使用一場單獨的表來記錄其他各張表的記錄數(shù)的話,是怎么解決統(tǒng)計結(jié)果不精確的問題的?
  4. count(字段),count(id),count(1), count(*)各自是怎么樣的執(zhí)行機制, 效率排序是怎么樣的?
責(zé)任編輯:武曉燕 來源: 陸隊長
相關(guān)推薦

2015-02-12 16:05:51

微信SDK

2015-02-12 16:17:09

微信SDK

2015-02-12 15:45:05

微信SDK

2024-11-27 09:32:58

2015-02-12 16:53:22

微信SDK

2021-05-24 08:58:34

Redis Bitmap 數(shù)據(jù)統(tǒng)計

2021-06-08 08:51:50

Redis 數(shù)據(jù)類型數(shù)據(jù)統(tǒng)計

2016-12-16 12:43:38

大數(shù)據(jù)OLAP數(shù)據(jù)統(tǒng)計

2019-07-11 10:52:02

Python統(tǒng)計數(shù)據(jù)

2018-08-31 08:01:27

數(shù)據(jù)統(tǒng)計機器學(xué)習(xí)深度學(xué)習(xí)

2014-04-24 13:24:49

DevOpsDevOps戰(zhàn)略

2016-10-18 14:13:21

數(shù)據(jù)統(tǒng)計模型

2012-08-10 13:34:25

深信服應(yīng)用交付負(fù)載均衡

2022-06-24 09:58:35

大數(shù)據(jù)JavaPython

2023-08-03 18:05:26

人工智能

2014-10-28 14:59:42

手游付費行為數(shù)據(jù)統(tǒng)計分析

2009-09-07 18:43:52

LINQ查詢

2010-11-04 15:43:49

DB2數(shù)據(jù)統(tǒng)計與分析系

2013-04-08 10:31:38

微信公眾平臺數(shù)據(jù)統(tǒng)計
點贊
收藏

51CTO技術(shù)棧公眾號