日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

mysql innodb count_MySQL下INNODB引擎的SELECT COUNT(*)性能优化及思考

發布時間:2025/4/16 数据库 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mysql innodb count_MySQL下INNODB引擎的SELECT COUNT(*)性能优化及思考 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

正 文:

MySQL下INNODB引擎的SELECT COUNT(*)性能優化及思考

最近有項目有高并發需求,服務器采用負載均衡,數據庫采用阿里云的RDS MYSQL,16核64G內存,連接數:16000 IOPS:14000,前幾場活動一切正常,RDS的cpu都維持在40%以內的使用率,但是后面幾場的時候發現RDS的cpu達到了100%,RDS幾乎掛掉了!

排查原因,發現了大量慢日志:

select count(*) from `roundmember`

該表的引擎原來是myisam,同樣的程序在其他服務器(如騰訊云數據庫)運行時并沒有出現異常。檢查該表,在阿里云的rds上是innodb引擎,阿里云的rds for mysql目前只支持innodb引擎,不支持myisam引擎,阿里云給出的解釋是:MyISAM引擎表不支持事務,讀寫操作會相互沖突,僅支持表級別鎖。當其上的查詢或者寫入操作時間比較長的時候,會阻塞其他操作,容易導致連接堆積,而且在crash?后存在數據丟失的風險,因此RDS?for?MySQL推薦使用?Innodb?引擎。

目前RDS?for?MySQL如果導入表、新建表是MyISAM引擎或調整表引擎為MyISAM,會自動修改為Innodb引擎。

雖然從MySQL 5.5版本開始,默認引擎變成了InnoDB,但是并不代表Myisam引擎就要被淘汰,對于一些非重要數據無需事務處理而又要求高讀取速度的表,飄易倒是更愿意使用myisam引擎。阿里云應該讓用戶自己選擇,而不是代替用戶選擇。

話說回來,innodb引擎的表在使用select count的時候,如果表的總行數在1-2萬條以內,速度應該不是瓶頸,但是一旦超過了這個值,隨著行數的增多,select count查詢效率會迅速的下降。

在本項目中,由于RDS性能較高,在roungmember表的總行數3萬條以內的時候,select count查詢速度還不錯,沒有成為性能瓶頸,但是超過了3萬條之后,大量的慢日志就出來了,RDS CPU使用率直線飆升到100%。

此時,RDS的并發連接數在400上下,并不算太高:

本文的關鍵點來了,InnoDB引擎并不適合使用select count(*)查詢總行數。

測試表大約4.3萬行

Myisam引擎:

SELECT?SQL_NO_CACHE?COUNT(*)?FROM?`roundmember2`

耗費105微秒

innodb引擎:

耗費10335微秒

可以看出innodb引擎耗時是myisam引擎的98倍!這還是僅僅是4萬多行的數據下測試的差距,隨著記錄行的增加,這個差距會越來越大。

MyISAM會保存表的總行數,這段代碼在MyISAM存儲引擎中執行,MyISAM只要簡單地讀出保存好的行數即可。因此,如果表中沒有使用事務之類的操作,這是最好的優化方案。然而,innodb表不像myisam有個內置的計數器,InnoDB存儲引擎不會保存表的具體行數,因此,在InnoDB存儲引擎中執行這段代碼,InnoDB要掃描一遍整個表來計算有多少行。

innodb引擎:

SELECT?SQL_NO_CACHE?COUNT(*)?FROM?`roundmember`?WHERE?id>0

采用where加主鍵id查詢,耗時達到16.2ms,性能比未帶id查詢反而降低了。

在uid列上建立索引:

SELECT?SQL_NO_CACHE?COUNT(*)?FROM?`roundmember`?WHERE?uid>0

采用where加普通索引(第二索引)查詢,耗時26.2ms,性能更低了。

而這樣的結果似乎和網上得到的結論有一些出入:

網上的結論1:采用?secondary?index?查詢要比用?primary?key?查詢來的快很多。那么,為什么用?secondary?index?掃描反而比?primary?key?掃描來的要快呢?我們就需要了解innodb的

clustered?index?和?secondary?index?之間的區別了。

innodb?的?clustered?index?是把?primary?key?以及?row?data?保存在一起的,而?secondary?index?則是單獨存放,然后有個指針指向?primary?key。因此,需要進行?count(*)?統計表記錄總數時,利用?secondary?index?掃描起來,顯然更快。

網上的結論2:在第一次使用了唯一索引(u_id)的時候,InnoDB使用了唯一索引作為表的聚簇索引。而在InnoDB存儲引擎中,count(*)函數是先從內存中讀取表中的數據到內存緩沖區,然后掃描全表獲得行記錄數的。因此,使用唯一索引作為聚簇索引的時候,InnoDB需要先讀取110W條的數據到數據緩沖區中,這里發生了很多次I/O,因此造成了主要的時間消耗。而添加了輔助索引后,mysql在執行查詢時會使用內部的優化機制:即使用輔助索引來統計數量。輔助索引保存的是index的值,此時只需要讀取一個字段,I/O減少了,性能就提高了。因此在InnoDB中,如果有統計整張表的數量的需求,可以考慮增加一個輔助索引。

InnoDB PitfallsHowever,?all?is?not?rosy?with?InnoDB.?Because?of?its?transactional?nature,?it?has?bottlenecks?of?its?own.?On?MyISAM,?doing?a?query?that?does?SELECT?COUNT(*)?FROM?{some_table},?is?very?fast,?since?MyISAM?keeps?the?information?in?the?index.

On?InnoDB,?this?info?is?not?stored?in?an?index,?and?even?the?index?and?the?data?are?kept?in?the?same?file.?So,?doing?the?same?query?on?a?table?can?incur?a?significant?performance?penalty.

To?check?what?overhead?this?has,?I?wrote?a?simple?test?benchmark?code.?I?duplicated?a?client?node?table?that?has?20,243?rows?from?MyISAM?to?InnoDB.

On?a?quiescent?AMD?64?machine?with?MySQL?server?5.0.24,?doing?a?SELECT?COUNT(*)?FROM?node?takes?0.835?milliseconds?on?MyISAM,?while?on?InnoDB?it?takes?12.292?milliseconds!

既然我們知道innodb引擎并不適合做 select count(*)查詢,那么我們回過頭來看看實際業務的需求是怎樣的?

select count(*) from `roundmember`

采用這樣的查詢是因為需要查出實時參與活動的總人數,但是這里的“實時”是否一定要實時,采用準實時,用戶會反感嗎?或者即使有一定的偏差,普通用戶能感知嗎?如果我們得出答案,這個實時的參與數只要準實時,允許一定的偏差,那么就好辦了:

select id from roundmember order by id desc limit 1

我們只要查最新的一條記錄,id是自增字段,取當前的這個id值就可以大約知道總參與人數了(注意我們的項目里并不會刪除參與記錄)。

關于innodb引擎的一些知識點:

知識點一:innodb存儲引擎的默認的行格式為compact(redundant為兼容以前的版本),對于blob,text,varchar(8099)這樣的大字段,innodb只會存放前768字節在數據頁中,而剩余的數據則會存儲在溢出段中(發生溢出情況的時候適用);

知識點二:innodb的塊大小默認為16kb,由于innodb存儲引擎表為索引組織表,樹底層的葉子節點為一雙向鏈表,因此每個頁中至少應該有兩行記錄,這就決定了innodb在存儲一行數據的時候不能夠超過8k(8098字節);

知識點三:使用了blob數據類型,是不是一定就會存放在溢出段中?通常我們認為blob,clob這類的大對象的存儲會把數據存放在數據頁之外,其實不然,關鍵點還是要看一個page中到底能否存放兩行數據,blob可以完全存放在數據頁中(單行長度沒有超過8098字節),而varchar類型的也有可能存放在溢出頁中(單行長度超過8098字節,前768字節存放在數據頁中);

知識點四:5.1中的innodb_plugin引入了新的文件格式:barracuda(將compact和redundant合稱為antelope),該文件格式擁有新的兩種行格式:compressed和dynamic,兩種格式對blob字段采用完全溢出的方式,數據頁中只存放20字節,其余的都存放在溢出段中:

知識點五:mysql在操作數據的時候,以page為單位,不管是更新,插入,刪除一行數據,都需要將那行數據所在的page讀到內存中,然后在進行操作,這樣就存在一個命中率的問題,如果一個page中能夠相對的存放足夠多的行,那么命中率就會相對高一些,性能就會有提升;

關于innodb引擎的一些優化建議:

1、每個字段的長度控制在768字節以內

原因:當Innodb的存儲格式是 ROW_FORMAT=COMPACT (or ROW_FORMAT=REDUNDANT)的時候,Innodb只會存儲前768字節的長度,剩余的數據存放到“溢出頁”中。一旦采用了這種“溢出存儲”,返回數據的時候本來是順序讀取的數據,就變成了隨機讀取了,會導致性能急劇下降。并且Innodb并沒有將溢出頁(overflow page)緩存到內存里面。

2、InnoDB 中不保存表的具體行數,也就是說,執行select count(*) from table時,InnoDB要掃描一遍整個表來計算有多少行,但是MyISAM只要簡單的讀出保存好的行數即可。注意的是,當count(*)語句包含 where條件時,兩種表的操作有些不同,InnoDB類型的表用count(*)或者count(主鍵),加上where col 條件。其中col列是表的主鍵之外的其他具有唯一約束索引的列。這樣查詢時速度會很快,就是可以避免全表掃描。

3、對于AUTO_INCREMENT類型的字段,InnoDB中必須包含只有該字段的索引,但是在MyISAM表中,可以和其他字段一起建立聯合索引。

4、DELETE FROM table時,InnoDB不會重新建立表,而是一行一行的刪除。

5、LOAD TABLE FROM MASTER操作對InnoDB是不起作用的,解決方法是首先把InnoDB表改成MyISAM表,導入數據后再改成InnoDB表,但是對于使用的額外的InnoDB特性(例如外鍵)的表不適用。

6、innodb要熟練使用覆蓋索引技術(建立合適的聯合索引)

select a from table_name where b 這樣的一個查詢,都知道索引應該加在b上面,查詢的處理過程:首先去檢索b索引找到與其對應的索引,然后根據索引去檢索正確的數據行。這樣一來一去就是兩次檢索,能不能通過一次檢索而得到數據呢?

如果希望通過一次檢索得到數據,那么索引上面就應該包含其索引相對的數據,這樣可能嗎?

alter table_name add index (b,a);

添加一個這樣的索引就能實現了, 查看是否使用了覆蓋索引; 使用 explain select ...如果 extra:?use index就表示使用了覆蓋索引。

【參考】:

總結

以上是生活随笔為你收集整理的mysql innodb count_MySQL下INNODB引擎的SELECT COUNT(*)性能优化及思考的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。