數(shù)據(jù)庫(kù)的使用你可能忽略了這些
數(shù)據(jù)庫(kù)的管理是一個(gè)非常專業(yè)的事情,對(duì)數(shù)據(jù)庫(kù)的調(diào)優(yōu)、監(jiān)控一般是由數(shù)據(jù)庫(kù)工程師完成,但是開發(fā)人員也經(jīng)常與數(shù)據(jù)庫(kù)打交道,即使是簡(jiǎn)單的增刪改查也是有很多竅門,這里,一起來(lái)聊聊數(shù)據(jù)庫(kù)中很容易忽略的問(wèn)題。
字段長(zhǎng)度省著點(diǎn)用
先說(shuō)說(shuō)我們常用的類型的存儲(chǔ)長(zhǎng)度:
很明顯,不同的類型存儲(chǔ)的長(zhǎng)度有很大區(qū)別的,對(duì)查詢的效率有影響,字段長(zhǎng)度對(duì)索引的影響是很大的。
- 字符串字段長(zhǎng)度都差不多的,可以預(yù)估長(zhǎng)度的,用char
- 字符串長(zhǎng)度差異大,用varchar,限制長(zhǎng)度,不要浪費(fèi)空間
- 整型根據(jù)大小,選擇合適的類型
- 時(shí)間建議用timestamp
- 建議使用decimal,不建議使用float,如果是價(jià)格,可以考慮用int或bigint,如1元,存儲(chǔ)的就是100
放棄uuid(guid)的使用
不管是uuid,還是guid,使用的時(shí)候都是為了避免同時(shí)生成重復(fù)的ID,但是建議考慮其他方案,原因如下:
- uuid沒(méi)有順序
- uuid太長(zhǎng)
- uuid規(guī)則完全不可控
推薦的方案用bigint(***),或者char來(lái)存儲(chǔ),生成方式參考snowflake的算法,有順序、長(zhǎng)度固定、比uuid更短,當(dāng)然,也幾乎不會(huì)重復(fù)。
大表減少聯(lián)表,***是單表查詢
單表查詢的優(yōu)勢(shì)很多,查詢效率極高,便于分表分庫(kù)擴(kuò)展,但是很多時(shí)候大家都覺得真正實(shí)現(xiàn)起來(lái)不太現(xiàn)實(shí),完全失去了關(guān)系數(shù)據(jù)庫(kù)的意義,但是單表的性能優(yōu)勢(shì)太明顯,一般總會(huì)有辦法解決的:
- 合理的冗余字段
- 配合內(nèi)存數(shù)據(jù)庫(kù)(redis\mongodb)使用
- 聯(lián)表變多次查詢(下文會(huì)有說(shuō)明)
如果考慮都后期數(shù)據(jù)量大,需要分表分庫(kù),就應(yīng)該盡早實(shí)時(shí)單表查詢,現(xiàn)在的數(shù)據(jù)庫(kù)分表分庫(kù)的中間件基本都無(wú)法支持聯(lián)表查詢。即使如mycat最多支持兩個(gè)表的聯(lián)表查詢,但是也有很明顯的性能損耗。
索引的正確處理方式
索引的優(yōu)勢(shì)這里就不多說(shuō)了,索引使用不當(dāng)會(huì)有反效果:
- 數(shù)據(jù)量很小的表,不需要索引
- 一個(gè)表的索引不宜過(guò)多,建議最多就5個(gè),索引不可能滿足所有的場(chǎng)景,但是了個(gè)滿足絕大部分的場(chǎng)景
- mysql 和 sqlserver的索引差別還挺大的,需要注意。例如:
mysql索引字段的順序?qū)π阅苡泻艽笥绊懀瑂qlserver優(yōu)化過(guò),影響很小
多查幾次比聯(lián)表可能要好
提出這個(gè)方案相信會(huì)得到很多人的反對(duì),但是我相信這個(gè)結(jié)論還是非常適合數(shù)據(jù)量大的場(chǎng)景。多查幾次數(shù)據(jù)庫(kù)有這么幾個(gè)弊端:
- 增加了網(wǎng)絡(luò)消耗
- 增加了數(shù)據(jù)庫(kù)的連接數(shù)
其實(shí),這兩個(gè)問(wèn)題在現(xiàn)在基本都可以忽略的,數(shù)據(jù)庫(kù)和應(yīng)用的連接基本都是內(nèi)網(wǎng),這個(gè)網(wǎng)絡(luò)連接的效率還是很高的。數(shù)據(jù)庫(kù)對(duì)連接池的優(yōu)化已經(jīng)比較成熟了,連接數(shù)只要不是太多,影響也不會(huì)太嚴(yán)重,但是多查幾次的優(yōu)勢(shì)卻很多:
- 單表效率更高
- 便于后期擴(kuò)展分表分庫(kù)庫(kù)
- 有效利用數(shù)據(jù)庫(kù)本身的結(jié)果緩存
- 減少鎖表,聯(lián)表會(huì)鎖多個(gè)表
當(dāng)然,多查幾次這個(gè)度一定要把握。千萬(wàn)不要在一個(gè)循環(huán)里面查詢數(shù)據(jù)庫(kù)。我們也應(yīng)該盡量減少查詢數(shù)據(jù)庫(kù)的次數(shù)。我們可以接受1次查詢變2次查詢,如果你變成10次查詢,那就要放棄了。
舉個(gè)例子:
查詢商品的時(shí)候,需要顯示分類表的分類名
- select category.name,product.name from product inner join category on p.categoryid=category.id
建議的方式:
- select categoryid,name from product
- select categoryname from category where categoryid in ('','','','')
當(dāng)然,你可以再優(yōu)化一下,查詢分類名之前,對(duì)product的categoryid排序一下,這樣速度更快。因?yàn)槲覀兦懊嬉呀?jīng)用snowflake生成了有順序的主鍵了。
補(bǔ)充一下,in的效率并不是你想象的那么慢,如果保持在100個(gè)節(jié)點(diǎn)(很多書籍介紹1000個(gè)節(jié)點(diǎn),我們保守一點(diǎn)),性能還是很高的。
盡量使用簡(jiǎn)單的數(shù)據(jù)庫(kù)腳本
很多用過(guò) .net Entity Framework 的人都說(shuō)這個(gè)框架太慢,其實(shí)慢主要是兩點(diǎn):錯(cuò)誤的使用延遲加載(外鍵關(guān)聯(lián))、生成SQL編譯太慢。Entity Framework生成的SQL腳本有太多沒(méi)用的東西,導(dǎo)致編譯太慢。
數(shù)據(jù)庫(kù)腳本盡量使用簡(jiǎn)單的,不要用太長(zhǎng)的一個(gè)SQL腳本,會(huì)導(dǎo)致初次執(zhí)行的時(shí)候,編譯SQL腳本花費(fèi)太多的時(shí)間。
盡量去避免聚合操作
聚合操作如count,group等,是數(shù)據(jù)庫(kù)性能的大殺手,經(jīng)常會(huì)出現(xiàn)大面積的表掃描和索表的情況,所以大家能看到很多平臺(tái)都把數(shù)量的計(jì)算給隱藏了,商品查詢不去實(shí)時(shí)顯示count的結(jié)果。如淘寶,就不顯示查詢結(jié)果的數(shù)量,只是顯示前100頁(yè)。
避免聚合操作的方法就是將實(shí)時(shí)的count計(jì)算結(jié)果用字段去存儲(chǔ),去累加這個(gè)結(jié)果。當(dāng)然,也可以考慮用spark等實(shí)時(shí)計(jì)算框架去處理,這種高深的技術(shù),不在此次討論范圍內(nèi)。(PS:主要是我也不懂)
總結(jié)
程序的優(yōu)化很多時(shí)候都是一些細(xì)節(jié)的問(wèn)題,更應(yīng)該注意平時(shí)的積累,阿里SQL的規(guī)范有很多可以吸取的地方,以上也是自己工作中的一些總結(jié),歡迎大家補(bǔ)充。