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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > 数据库 >内容正文

数据库

在MySQL的InnoDB存储引擎中count(*)函数的优化

發(fā)布時(shí)間:2023/12/3 数据库 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 在MySQL的InnoDB存储引擎中count(*)函数的优化 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

轉(zhuǎn)載自??在MySQL的InnoDB存儲(chǔ)引擎中count(*)函數(shù)的優(yōu)化

寫這篇文章之前已經(jīng)看過了很多數(shù)據(jù)庫(kù)方面的優(yōu)化內(nèi)容,大部分都是加索引、使用事務(wù)、要什么select什么等等。然而,只是停留在閱讀的層面上,很少有實(shí)踐,因?yàn)闆]有遇到真實(shí)的項(xiàng)目,一切都是紙上談兵。實(shí)踐是檢驗(yàn)真理的唯一標(biāo)準(zhǔn),于是就想在數(shù)據(jù)庫(kù)上測(cè)試一些性能優(yōu)化的方案,比如索引之類的,但是不想使用假的數(shù)據(jù),于是就想著能不能抓取網(wǎng)上的一些數(shù)據(jù)來作分析,后來自己通過PHP抓取了一些數(shù)據(jù)(爬取數(shù)據(jù)博文),抓了大約110W的用戶數(shù)據(jù)之后,當(dāng)然需要統(tǒng)計(jì)一下具體的數(shù)量,于是我使用了以下的SQL語(yǔ)句(我使用的存儲(chǔ)引擎是InnoDB):

SELECT?COUNT(*)?FROM?zh_user;

然而,發(fā)現(xiàn)需要運(yùn)行14-20s的時(shí)間才能看到結(jié)果。

這樣的時(shí)間開銷在真實(shí)的環(huán)境的用戶體驗(yàn)是十分差的,試想一下,打開一個(gè)頁(yè)面還要等接近20s才能看到數(shù)據(jù),別說20s,就算是3s也是十分差的,于是便想在這方面做優(yōu)化。

存儲(chǔ)引擎

在MySQL中,日常開發(fā)中比較常用的有MyISAM和InnoDB兩種存儲(chǔ)引擎。兩者之間的其中一個(gè)區(qū)別是使用count(*)函數(shù)計(jì)算表的具體行數(shù)。

因?yàn)镸yISAM會(huì)保存表的具體行數(shù),因此這段代碼在MyISAM存儲(chǔ)引擎中執(zhí)行,MyISAM只要簡(jiǎn)單地讀出保存好的行數(shù)即可。因此,如果表中沒有使用事務(wù)之類的操作,這是最好的優(yōu)化方案。然而,InnoDB存儲(chǔ)引擎不會(huì)保存表的具體行數(shù),因此,在InnoDB存儲(chǔ)引擎中執(zhí)行這段代碼,InnoDB要掃描一遍整個(gè)表來計(jì)算有多少行。

查詢優(yōu)化命令--Explain

要弄懂查詢性能在哪,首先,需要知道導(dǎo)致查詢緩慢的瓶頸在哪。explain命令顯示的rows是核心的性能指標(biāo),rows大,說明mysql需要掃描的行數(shù)就多,絕大部分rows大的語(yǔ)句執(zhí)行一定很快。所以優(yōu)化語(yǔ)句基本上都是在優(yōu)化rows。

首先,看看表的結(jié)構(gòu):

表的當(dāng)前索引:

再看看Explain的結(jié)果:

可以看到,mysql掃描了整個(gè)表來執(zhí)行本次查詢。

奇怪的地方

在數(shù)據(jù)表的設(shè)計(jì)中,我是添加了唯一索引的,但是后來有一個(gè)語(yǔ)句是根據(jù)其中一個(gè)字段統(tǒng)計(jì)數(shù)量,當(dāng)時(shí)添加了一個(gè)普通的索引,當(dāng)我再執(zhí)行了一遍上面的SQL語(yǔ)句,發(fā)現(xiàn)只需要0.2-0.3s的時(shí)間就能統(tǒng)計(jì)出表中的行數(shù)。

不禁嚇了一跳,誤打誤撞就發(fā)現(xiàn)了優(yōu)化的方法:在InnoDB中,除了唯一索引之外,在其他字段添加一個(gè)普通索引(稱為輔助索引)就能夠提升count(*)函數(shù)的性能。但是這是為什么呢?
加了索引之后的表結(jié)構(gòu):

當(dāng)前的索引:

Explain一下:

同樣是掃描一樣的行數(shù),為什么添加一個(gè)普通索引就可以提高這么多的性能?于是便開始查找資料和閱讀文檔弄懂這個(gè)問題。

count(*)函數(shù)執(zhí)行原理

正如在不同的存儲(chǔ)引擎中,count()函數(shù)的執(zhí)行是不同的。在MyISAM存儲(chǔ)引擎中,count()函數(shù)是直接讀取數(shù)據(jù)表保存的行記錄數(shù)并返回,而在InnoDB存儲(chǔ)引擎中,count(*)函數(shù)是先從內(nèi)存中讀取表中的數(shù)據(jù)到內(nèi)存緩沖區(qū),然后掃描全表獲得行記錄數(shù)的。在使用count函數(shù)中加上where條件時(shí),在兩個(gè)存儲(chǔ)引擎中的效果是一樣的,都會(huì)掃描全表計(jì)算某字段有值項(xiàng)的次數(shù)。

索引原理

因?yàn)槭翘砑恿怂饕蟛诺玫叫阅苌系奶嵘?#xff0c;于是便想到從索引的角度來探索。

根據(jù)官方文檔上的定義:索引是幫助MySQL高效獲取數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)。可以得知,索引的本質(zhì)就是數(shù)據(jù)結(jié)構(gòu),添加索引的目的就是為了提高查詢的效率。

使用索引的查詢可以類比到字典,如果要查”mysql“這個(gè)單詞,我們首先會(huì)定位到m字母,然后在m字母下面的單詞中找y字母,以此類推,直到找到mysql這個(gè)單詞,就能看到它在第幾頁(yè),然后就去該頁(yè)獲取該單詞更多的信息。想象一下,如果沒有索引,那你就要在字典里一頁(yè)一頁(yè)的翻閱,效率十分低下。使用索引就是通過這樣不斷地縮小查詢的范圍來篩選出最終的結(jié)果。

那么在數(shù)據(jù)庫(kù)也是一樣的,但顯然在數(shù)據(jù)庫(kù)里使用索引要復(fù)雜許多。

磁盤存取與預(yù)讀

一般來說,索引本身也很大,不可能全部存儲(chǔ)在內(nèi)存中,因此索引往往以索引文件的形式存儲(chǔ)在磁盤上。那么數(shù)據(jù)庫(kù)在構(gòu)建索引的時(shí)候就需要先從磁盤讀取數(shù)據(jù)了,此時(shí)就要產(chǎn)生磁盤I/O消耗。而每次的數(shù)據(jù)讀取,都要經(jīng)歷尋道時(shí)間、旋轉(zhuǎn)延遲、傳輸時(shí)間三個(gè)部分。尋道時(shí)間是指磁臂移動(dòng)到指定磁道所需要的時(shí)間,一般在5ms以內(nèi);旋轉(zhuǎn)延遲就是磁盤轉(zhuǎn)速;傳輸時(shí)間指的是將數(shù)據(jù)從磁盤讀出并寫入到內(nèi)存的時(shí)間,這個(gè)時(shí)間較短,可以忽略不計(jì)。相對(duì)于內(nèi)存存取,I/O存取的消耗要高幾個(gè)數(shù)量級(jí)。因此,評(píng)價(jià)一個(gè)數(shù)據(jù)結(jié)構(gòu)作為索引的優(yōu)劣最重要的指標(biāo)就是查找過程中磁盤I/O操作次數(shù)的漸進(jìn)復(fù)雜度。換句話說,索引的結(jié)構(gòu)組織要盡量減少查找過程中磁盤I/O的存取次數(shù)。

從上面的描述可以得知磁盤I/O是非常高昂的操作,根據(jù)操作系統(tǒng)的局部性原理:

當(dāng)一個(gè)數(shù)據(jù)被用到時(shí),其附近的數(shù)據(jù)也通常會(huì)馬上被使用。

計(jì)算機(jī)操作系統(tǒng)在這方面做了一些優(yōu)化,當(dāng)一次I/O時(shí),不光把當(dāng)前磁盤地址的數(shù)據(jù)讀取到內(nèi)存緩沖區(qū)內(nèi),而且把相鄰的數(shù)據(jù)也都讀取到內(nèi)存緩沖區(qū)內(nèi)。這樣一來,在讀取數(shù)據(jù)時(shí)產(chǎn)生的I/O就少了很多了。因?yàn)樵跀?shù)據(jù)庫(kù)中,每一次I/O讀取的數(shù)據(jù)我們稱之為一頁(yè)(page),一般為4k或8k,也就是說,我們讀取一頁(yè)內(nèi)的數(shù)據(jù)時(shí),實(shí)際上才發(fā)生了一次I/O。

根據(jù)以上的描述,我們可以初步得出結(jié)論,增加索引前后的性能差距體現(xiàn)在磁盤讀取過程。但是在添加新的索引之前,我是添加了一個(gè)唯一索引的,后來發(fā)現(xiàn)在mysql中,我添加的唯一索引被稱為聚簇索引,而后面添加的索引稱為輔助索引,因此,讓我們?cè)賮砜纯淳鄞厮饕洼o助索引的區(qū)別。

聚簇索引(clustered index)和輔助索引(secondary index)

聚簇索引(clustered index)

每一個(gè)InnoDB存儲(chǔ)引擎下的表都有一個(gè)特殊的索引用來保存每一行的數(shù)據(jù),稱為聚簇索引。通常情況下,聚簇索引是主鍵的同義詞。

這里講到,在InnoDB中,mysql是這樣選擇聚簇索引的:

  • 如果表中定義了PRIMARY KEY,那么InnoDB就會(huì)使用它作為聚簇索引;

  • 否則,如果沒有定義PRIMARY KEY,InnoDB會(huì)選擇第一個(gè)有NOT NULL約束的唯一索引作為PRIMARY KEY,然后InnoDB會(huì)使用它作為聚簇索引;

  • 如果表中沒有定義PRIMARY KEY或者合適的唯一索引。InnoDB內(nèi)部會(huì)在含有行ID值的合成列生成隱藏的聚簇索引。這些行使用InnoDB賦予這些表的ID進(jìn)行排序。行ID是6個(gè)字節(jié)的字段,且作為新行單一地自增。因此,根據(jù)行ID排序的行數(shù)據(jù)在物理上是根據(jù)插入的順序進(jìn)行排序。

聚簇索引如何加速查詢

因?yàn)樗械男袛?shù)據(jù)都跟聚簇索引存放在同一個(gè)地方,因此,通過聚簇索引訪問數(shù)據(jù)行會(huì)更快。如果表十分大,跟使用不同地方保存數(shù)據(jù)和索引的存儲(chǔ)組織來說,聚簇索引的結(jié)構(gòu)會(huì)節(jié)省很多的I/O操作。(比如說,MyISAM使用了一個(gè)文件來保存數(shù)據(jù)以及另一個(gè)文件保存索引記錄)。

輔助索引(secondary index)

除了聚簇索引之外的所有索引都被稱為輔助索引。在InnoDB里,輔助索引的每一行記錄都包含每一行的主鍵列,輔助索引指向主鍵。InnoDB使用這個(gè)主鍵來查找在聚簇索引中的行。如果主鍵很長(zhǎng),輔助索引會(huì)使用更多的空間,因此輔助索引有利于存儲(chǔ)引擎擁有長(zhǎng)度更短的主鍵。

結(jié)論

在第一次使用了唯一索引(u_id)的時(shí)候,InnoDB使用了唯一索引作為表的聚簇索引。而在InnoDB存儲(chǔ)引擎中,count(*)函數(shù)是先從磁盤中讀取表中的數(shù)據(jù)到內(nèi)存緩沖區(qū),然后掃描全表獲得行記錄數(shù)的。因此,使用唯一索引作為聚簇索引的時(shí)候,InnoDB需要先讀取110W條的數(shù)據(jù)到數(shù)據(jù)緩沖區(qū)中,這里發(fā)生了很多次I/O,因此造成了主要的時(shí)間消耗。而添加了輔助索引后,mysql在執(zhí)行查詢時(shí)會(huì)使用內(nèi)部的優(yōu)化機(jī)制:即使用輔助索引來統(tǒng)計(jì)數(shù)量。輔助索引保存的是index的值,此時(shí)只需要讀取一個(gè)字段,I/O減少了,性能就提高了。因此在InnoDB中,如果有統(tǒng)計(jì)整張表的數(shù)量的需求,可以考慮增加一個(gè)輔助索引。

?

總結(jié)

以上是生活随笔為你收集整理的在MySQL的InnoDB存储引擎中count(*)函数的优化的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。