十年網(wǎng)站開(kāi)發(fā)經(jīng)驗(yàn) + 多家企業(yè)客戶 + 靠譜的建站團(tuán)隊(duì)
量身定制 + 運(yùn)營(yíng)維護(hù)+專業(yè)推廣+無(wú)憂售后,網(wǎng)站問(wèn)題一站解決
數(shù)據(jù)庫(kù)優(yōu)化有很多可以講,按照支撐的數(shù)據(jù)量來(lái)分可以分為兩個(gè)階段:?jiǎn)螜C(jī)數(shù)據(jù)庫(kù)和分庫(kù)分表,前者一般可以支撐500W或者10G以內(nèi)的數(shù)據(jù),超過(guò)這個(gè)值則需要考慮分庫(kù)分表。另外,一般大企業(yè)面試往往會(huì)從單機(jī)數(shù)據(jù)庫(kù)問(wèn)起,一步一步問(wèn)到分庫(kù)分表,中間會(huì)穿插很多數(shù)據(jù)庫(kù)優(yōu)化的問(wèn)題。本文試圖描述單機(jī)數(shù)據(jù)庫(kù)優(yōu)化的一些實(shí)踐,數(shù)據(jù)庫(kù)基于mysql,如有不合理的地方,歡迎指正。
成都創(chuàng)新互聯(lián)公司專注于貴溪網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠(chéng)為您提供貴溪營(yíng)銷型網(wǎng)站建設(shè),貴溪網(wǎng)站制作、貴溪網(wǎng)頁(yè)設(shè)計(jì)、貴溪網(wǎng)站官網(wǎng)定制、小程序開(kāi)發(fā)服務(wù),打造貴溪網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供貴溪網(wǎng)站排名全網(wǎng)營(yíng)銷落地服務(wù)。
1、表結(jié)構(gòu)優(yōu)化
在開(kāi)始做一個(gè)應(yīng)用的時(shí)候,數(shù)據(jù)庫(kù)的表結(jié)構(gòu)設(shè)計(jì)往往會(huì)影響應(yīng)用后期的性能,特別是用戶量上來(lái)了以后的性能。因此,表結(jié)構(gòu)優(yōu)化是一個(gè)很重要的步驟。
1.1、字符集
一般來(lái)說(shuō)盡量選擇UTF-8,雖然在存中午的時(shí)候GBK比UTF-8使用的存儲(chǔ)空間少,但是UTF-8兼容各國(guó)語(yǔ)言,其實(shí)我們不必為了這點(diǎn)存儲(chǔ)空間而犧牲了擴(kuò)展性。事實(shí)上,后期如果要從GBK轉(zhuǎn)為UTF-8所要付出的代價(jià)是很高的,需要進(jìn)行數(shù)據(jù)遷移,而存儲(chǔ)空間完全可以用花錢擴(kuò)充硬盤來(lái)解決。
1.2、主鍵
在使用mysql的innodb的時(shí)候,innodb的底層存儲(chǔ)模型是B+樹,它使用主鍵作為聚簇索引,使用插入的數(shù)據(jù)作為葉子節(jié)點(diǎn),通過(guò)主鍵可以很快找到葉子節(jié)點(diǎn),從而快速獲取記錄。因此在設(shè)計(jì)表的時(shí)候需要增加一個(gè)主鍵,而且最好要自增。因?yàn)樽栽鲋麈I可以讓插入的數(shù)據(jù)按主鍵順序插入到底層的B+樹的葉子節(jié)點(diǎn)中,由于是按序的,這種插入幾乎不需要去移動(dòng)已有的其它數(shù)據(jù),所以插入效率很高。如果主鍵不是自增的,那么每次主鍵的值近似隨機(jī),這時(shí)候就有可能需要移動(dòng)大量數(shù)據(jù)來(lái)保證B+樹的特性,增加了不必要的開(kāi)銷。
1.3、字段
1.3.1、建了索引的字段必須加上not null約束,并且設(shè)置default值
1.3.2、不建議使用float、double來(lái)存小數(shù),防止精度損失,建議使用decimal
1.3.3、不建議使用Text/blob來(lái)保存大量數(shù)據(jù),因?yàn)閷?duì)大文本的讀寫會(huì)造成比較大的I/O開(kāi)銷,同時(shí)占用mysql的緩存,高并發(fā)下會(huì)極大的降低數(shù)據(jù)庫(kù)的吞吐量,建議將大文本數(shù)據(jù)保存在專門的文件存儲(chǔ)系統(tǒng)中,mysql中只保存這個(gè)文件的訪問(wèn)地址,比如博客文章可以保存在文件中,mysql中只保存文件的相對(duì)地址。
1.3.4、varchar類型長(zhǎng)度建議不要超過(guò)8K。
1.3.5、時(shí)間類型建議使用Datetime,不要使用timestamp,雖然Datetime占用8個(gè)字節(jié),而timestamp只占用4個(gè)字節(jié),但是后者要保證非空,而且后者是對(duì)時(shí)區(qū)敏感的。
1.3.6、建議表中增加gmt_create和gmt_modified兩個(gè)字段,用來(lái)記錄數(shù)據(jù)創(chuàng)建的修改時(shí)間。這兩個(gè)字段建立的原因是方便查問(wèn)題。
1.4、索引創(chuàng)建
1.4.1、這個(gè)階段由于對(duì)業(yè)務(wù)并不了解,所以盡量不要盲目加索引,只為一些一定會(huì)用到索引的字段加普通索引。
1.4.2、創(chuàng)建innodb單列索引的長(zhǎng)度不要超過(guò)767bytes,如果超過(guò)會(huì)用前255bytes作為前綴索引
1.4.3、創(chuàng)建innodb組合索引的各列索引長(zhǎng)度不要超過(guò)767bytes,一共加起來(lái)不要超過(guò)3072bytes
2、SQL優(yōu)化
一般來(lái)說(shuō)sql就那么幾種:基本的增刪改查,分頁(yè)查詢,范圍查詢,模糊搜索,多表連接
2.1、基本查詢
一般查詢需要走索引,如果沒(méi)有索引建議修改查詢,把有索引的那個(gè)字段加上,如果由于業(yè)務(wù)場(chǎng)景沒(méi)法使用這個(gè)字段,那么需要看這個(gè)查詢調(diào)用量大不大,如果大,比如每天調(diào)用10W+,這就需要新增索引,如果不大,比如每天調(diào)用100+,則可以考慮保持原樣。另外,select * 盡量少用,用到什么字段就在sql語(yǔ)句中加什么,不必要的字段就別查了,浪費(fèi)I/O和內(nèi)存空間。
2.2、高效分頁(yè)
limit m,n其實(shí)質(zhì)就是先執(zhí)行l(wèi)imit m+n,然后從第m行取n行,這樣當(dāng)limit翻頁(yè)越往后翻m越大,性能越低。比如
select * from A limit 100000,10,這種sql語(yǔ)句的性能是很差的,建議改成下面的版本:
selec id,name,age from A where id =(select id from A limit 100000,1) limit 10
2.3、范圍查詢
范圍查詢包括between、大于、小于以及in。Mysql中的in查詢的條件有數(shù)量的限制,若數(shù)量較小可以走索引查詢,若數(shù)量較大,就成了全表掃描了。而between、大于、小于等,這些查詢不會(huì)走索引,所以盡量放在走索引的查詢條件之后。
2.4、模糊查詢like
使用 like %name%這樣的語(yǔ)句是不會(huì)走索引的,相當(dāng)于全表掃描,數(shù)據(jù)量小的時(shí)候不會(huì)有太大的問(wèn)題,數(shù)據(jù)量大了以后性能會(huì)下降的很厲害,建議數(shù)據(jù)量大了以后使用搜索引擎來(lái)代替這種模糊搜索,實(shí)在不行也要在模糊查詢前加個(gè)能走索引的條件。
2.5、多表連接
子查詢和join都可以實(shí)現(xiàn)在多張表之間取數(shù)據(jù),但是子查詢性能較差,建議將子查詢改成join。對(duì)于mysql的join,它用的是Nested Loop Join算法,也就是通過(guò)前一個(gè)表查詢的結(jié)果集去后一個(gè)表中查詢,比如前一個(gè)表的結(jié)果集是100條數(shù)據(jù),后一個(gè)表有10W數(shù)據(jù),那么就需要在100*10W的數(shù)據(jù)集合中去過(guò)濾得到最終的結(jié)果集。因此,盡量用小結(jié)果集的表去和大表做join,同時(shí)在join的字段上建立索引,如果建不了索引,就需要設(shè)置足夠大的join buffer size。如果以上的技巧都無(wú)法解決join所帶來(lái)的性能下降的問(wèn)題,那干脆就別用join了,將一次join查詢拆分成兩次簡(jiǎn)單查詢。另外,多表連接盡量不要超過(guò)三張表,超過(guò)三張表一般來(lái)說(shuō)性能會(huì)很差,建議拆分sql。
3、數(shù)據(jù)庫(kù)連接池優(yōu)化
數(shù)據(jù)庫(kù)連接池本質(zhì)上是一種緩存,它是一種抗高并發(fā)的手段。數(shù)據(jù)庫(kù)連接池優(yōu)化主要是對(duì)參數(shù)進(jìn)行優(yōu)化,一般我們使用DBCP連接池,它的具體參數(shù)如下:
3.1 initialSize
初始連接數(shù),這里的初始指的是第一次getConnection的時(shí)候,而不是應(yīng)用啟動(dòng)的時(shí)候。初始值可以設(shè)置為并發(fā)量的歷史平均值
3.2、minIdle
最小保留的空閑連接數(shù)。DBCP會(huì)在后臺(tái)開(kāi)啟一個(gè)回收空閑連接的線程,當(dāng)該線程進(jìn)行空閑連接回收的時(shí)候,會(huì)保留minIdle個(gè)連接數(shù)。一般設(shè)置為5,并發(fā)量實(shí)在很小可以設(shè)置為1.
3.3、maxIdle
最大保留的空閑連接數(shù),按照業(yè)務(wù)并發(fā)高峰設(shè)置。比如并發(fā)高峰為20,那么當(dāng)高峰過(guò)去后,這些連接不會(huì)馬上被回收,如果過(guò)一小段時(shí)間又來(lái)一個(gè)高峰,那么連接池就可以復(fù)用這些空閑連接而不需要頻繁創(chuàng)建和關(guān)閉連接。
3.4、maxActive
最大活躍連接數(shù),按照可以接受的并發(fā)極值設(shè)置。比如單機(jī)并發(fā)量可接受的極值是100,那么這個(gè)maxActive設(shè)置成100后,就只能同時(shí)為100個(gè)請(qǐng)求服務(wù),多余的請(qǐng)求會(huì)在最大等待時(shí)間之后被拋棄。這個(gè)值必須設(shè)置,可以防止惡意的并發(fā)攻擊,保護(hù)數(shù)據(jù)庫(kù)。
3.5、maxWait
獲取連接的最大等待時(shí)間,建議設(shè)置的短一點(diǎn),比如3s,這樣可以讓請(qǐng)求快速失敗,因?yàn)橐粋€(gè)請(qǐng)求在等待獲取連接的時(shí)候,線程是不可以被釋放的,而單機(jī)的線程并發(fā)量是有限的,如果這個(gè)時(shí)間設(shè)置的過(guò)長(zhǎng),比如網(wǎng)上建議的60s,那么這個(gè)線程在這60s內(nèi)是無(wú)法被釋放的,只要這種請(qǐng)求一多,應(yīng)用的可用線程就少了,服務(wù)就變得不可用了。
3.6、minEvictableIdleTimeMillis
連接保持空閑而不被回收的時(shí)間,默認(rèn)30分鐘。
3.7、validationQuery
用于檢測(cè)連接是否有效的sql語(yǔ)句,一般是一條簡(jiǎn)單的sql,建議設(shè)置
3.8、testOnBorrow
申請(qǐng)連接的時(shí)候?qū)B接進(jìn)行檢測(cè),不建議開(kāi)啟,嚴(yán)重影響性能
3.9、testOnReturn
歸還連接的時(shí)候?qū)B接進(jìn)行檢測(cè),不建議開(kāi)啟,嚴(yán)重影響性能
3.10、testWhileIdle
開(kāi)啟了以后,后臺(tái)清理連接的線程會(huì)沒(méi)隔一段時(shí)間對(duì)空閑連接進(jìn)行validateObject,如果連接失效則會(huì)進(jìn)行清除,不影響性能,建議開(kāi)啟
3.11、numTestsPerEvictionRun
代表每次檢查鏈接的數(shù)量,建議設(shè)置和maxActive一樣大,這樣每次可以有效檢查所有的鏈接。
3.12、預(yù)熱連接池
對(duì)于連接池,建議在啟動(dòng)應(yīng)用的時(shí)候進(jìn)行預(yù)熱,在還未對(duì)外提供訪問(wèn)之前進(jìn)行簡(jiǎn)單的sql查詢,讓連接池充滿必要的連接數(shù)。
4、索引優(yōu)化
當(dāng)數(shù)據(jù)量增加到一定程度后,靠sql優(yōu)化已經(jīng)無(wú)法提升性能了,這時(shí)候就需要祭出大招:索引。索引有三級(jí),一般來(lái)說(shuō)掌握這三級(jí)就足夠了,另外,對(duì)于建立索引的字段,需要考慮其選擇性。
4.1、一級(jí)索引
在where后面的條件上建立索引,單列可以建立普通索引,多列則建立組合索引。組合索引需要注意最左前綴原則。
4.2、二級(jí)索引
如果有被order by或者group by用到的字段,則可以考慮在這個(gè)字段上建索引,這樣一來(lái),由于索引天然有序,可以避免order by以及group by所帶來(lái)的排序,從而提高性能。
4.3、三級(jí)索引
如果上面兩招還不行,那么就把所查詢的字段也加上索引,這時(shí)候就形成了所謂的索引覆蓋,這樣做可以減少一次I/O操作,因?yàn)閙ysql在查詢數(shù)據(jù)的時(shí)候,是先查主鍵索引,然后根據(jù)主鍵索引去查普通索引,然后根據(jù)普通索引去查相對(duì)應(yīng)的記錄。如果我們所需要的記錄在普通索引里都有,那就不需要第三步了。當(dāng)然,這種建索引的方式比較極端,不適合一般場(chǎng)景。
4.4、索引的選擇性
在建立索引的時(shí)候,盡量在選擇性高的字段上建立。什么是選擇性高呢?所謂選擇性高就是通過(guò)這個(gè)字段查出來(lái)的數(shù)據(jù)量少,比如按照名字查一個(gè)人的信息,查出來(lái)的數(shù)據(jù)量一般會(huì)很少,而按照性別查則可能會(huì)把數(shù)據(jù)庫(kù)一半的數(shù)據(jù)都查出來(lái),所以,名字是一個(gè)選擇性高的字段,而性別是個(gè)選擇性低的字段。
5、歷史數(shù)據(jù)歸檔
當(dāng)數(shù)據(jù)量到了一年增加500W條的時(shí)候,索引也無(wú)能為力,這時(shí)候一般的思路都是考慮分庫(kù)分表。如果業(yè)務(wù)沒(méi)有爆發(fā)式增長(zhǎng),但是數(shù)據(jù)的確在緩慢增加,則可以不考慮分庫(kù)分表這種復(fù)雜的技術(shù)手段,而是進(jìn)行歷史數(shù)據(jù)歸檔。我們針對(duì)生命周期已經(jīng)完結(jié)的歷史數(shù)據(jù),比如6個(gè)月之前的數(shù)據(jù),進(jìn)行歸檔。我們可以使用quartz的調(diào)度任務(wù)在凌晨定時(shí)將6個(gè)月之前的數(shù)據(jù)查出來(lái),然后存入遠(yuǎn)程的hbase服務(wù)器。當(dāng)然,我們也需要提供歷史數(shù)據(jù)的查詢接口,以備不時(shí)之需。
以上就是對(duì)mysql 單機(jī)數(shù)據(jù)庫(kù)的優(yōu)化資料整理,后續(xù)繼續(xù)補(bǔ)充相關(guān)資料,謝謝大家對(duì)本站的支持!
select * from table order by id limit 1,1;
// 先按 id 從小到大倒敘排列,然后從第二條開(kāi)始取數(shù)據(jù),只取一條,那么取出的這一條數(shù)據(jù) id 就是倒數(shù)第二大的
// limit m,n ;第一個(gè)參數(shù) m 表示從第幾條開(kāi)始取數(shù)據(jù),m 從 0 開(kāi)始,依次為第一條、第二條。。。,第二個(gè)參數(shù) n 表示要取出多少條數(shù)據(jù)
1、開(kāi)啟電腦,并打開(kāi)mysql的數(shù)據(jù)庫(kù),在數(shù)據(jù)庫(kù)中點(diǎn)擊并進(jìn)入右上的查詢,在點(diǎn)擊查詢下面的新建查詢。如圖,
2、接下來(lái)就可以輸入SELECT * from crew_1 order by gmt_create這個(gè)語(yǔ)句,然后表中的所有記錄就會(huì)按時(shí)間排序查詢。如圖。
3、如果需要獲取表中按時(shí)間排序的第一條記錄,就輸入SELECT * from crew_1 order by gmt_create desc limit 0,1,如圖
4、如果需要獲取第5條記錄,就輸入SELECT * from crew_1 order by gmt_create desc limit 4,1如圖:
5、如果需要獲取第1001條記錄,只需要將limit 4,1改成limit 1000,1,如果需要獲取第n條記錄,查詢語(yǔ)句加入limit n-1,1就可以了,如圖:
6、如果需要獲取表中前n條記錄,改成limit n,如圖;
哎……這其實(shí)和你上次問(wèn)的差不多……
SELECT id, name FROM mytable
改成
SELECT id, name FROM mytable where 1 order by COLUMN asc limit N1,N2
COLUMN是你要排序的欄,比如id
asc 是ascend 升序的意思,如果你要降序排列就用DESC 是descend 降序意思
limit N1,N2的意思是取第N1個(gè)開(kāi)始的N2個(gè)數(shù)據(jù)
建議你去自學(xué)下mysql的基本語(yǔ)法知識(shí)
不然這么寫會(huì)很累的