mysql 之 优化 (收集于网络)
(以下內(nèi)容均來(lái)自于網(wǎng)絡(luò),如果有版權(quán)限制,請(qǐng)聯(lián)系我0.0)
Mysql存儲(chǔ)千億級(jí)的數(shù)據(jù),是一項(xiàng)非常大的挑戰(zhàn)。Mysql單表可以存儲(chǔ)10億級(jí)的數(shù)據(jù),只是這個(gè)時(shí)候性能非常差,項(xiàng)目中大量的實(shí)驗(yàn)證明,Mysql單表容量在500萬(wàn)左右,性能處于最佳狀態(tài)。
優(yōu)化的順序是:
第一優(yōu)化你的sql和索引;
第二加緩存 memcached,redis;
第三以上都做了后,還是慢,就做主從復(fù)制或主主復(fù)制,讀寫(xiě)分離,可以在應(yīng)用層做,效率高,也可以用三方工具,第三方工具推薦360的atlas,其它的要么效率不高,要么沒(méi)人維護(hù);
第四如果以上都做了還是慢,不要想著去做切分,mysql自帶分區(qū)表,先試試這個(gè),對(duì)你的應(yīng)用是透明的,無(wú)需更改代碼,但是sql語(yǔ)句是需要針對(duì)分區(qū)表做優(yōu)化的,sql條件中要帶上分區(qū)條件的列,從而使查詢定位到少量的分區(qū)上,否則就會(huì)掃描全部分區(qū),另外分區(qū)表還有一些坑,在這里就不多說(shuō)了;
第五如果以上都做了,那就先做垂直拆分,其實(shí)就是根據(jù)你模塊的耦合度,將一個(gè)大的系統(tǒng)分為多個(gè)小的系統(tǒng),也就是分布式系統(tǒng);
第六才是水平切分,針對(duì)數(shù)據(jù)量大的表,這一步最麻煩,最能考驗(yàn)技術(shù)水平,要選擇一個(gè)合理的sharding key,為了有好的查詢效率,表結(jié)構(gòu)也要改動(dòng),做一定的冗余,應(yīng)用也要改,sql中盡量帶sharding key,將數(shù)據(jù)定位到限定的表上去查,而不是掃描全部的表;
mysql數(shù)據(jù)庫(kù)一般都是按照這個(gè)步驟去演化的,成本也是由低到高;
一、查詢優(yōu)化
1、創(chuàng)建索引
有關(guān)索引的博客:https://blog.csdn.net/fenglepeng/article/details/103141756
最簡(jiǎn)單也是最常用的優(yōu)化就是查詢。因?yàn)閷?duì)于CRUD操作,read操作是占據(jù)了絕大部分的比例,所以read的性能基本上決定了應(yīng)用的性能。對(duì)于查詢性能最常用的就是創(chuàng)建索引。經(jīng)過(guò)測(cè)試,2000萬(wàn)條記錄,每條記錄200字節(jié)兩列varchar類型的。當(dāng)不使用索引的時(shí)候查詢一條記錄需要一分鐘,而當(dāng)創(chuàng)建了索引的時(shí)候查詢時(shí)間可以忽略。但是,當(dāng)你在已有數(shù)據(jù)上添加索引的時(shí)候,則需要耗費(fèi)非常大的時(shí)間。我插入2000萬(wàn)條記錄之后,再創(chuàng)建索引大約話費(fèi)了幾十分鐘的樣子。
創(chuàng)建索引的弊端和場(chǎng)合。雖然創(chuàng)建索引可以很大程度上優(yōu)化查詢的速度,但是弊端也是很明顯的。一個(gè)是在插入數(shù)據(jù)的時(shí)候,創(chuàng)建索引也需要消耗部分的時(shí)間,這就使得插入性能在一定程度上降低;另一個(gè)很明顯的是數(shù)據(jù)文件變的更大。在列上創(chuàng)建索引的時(shí)候,每條索引的長(zhǎng)度是和你創(chuàng)建列的時(shí)候制定的長(zhǎng)度相同的。比如你創(chuàng)建varchar(100),當(dāng)你在該列上創(chuàng)建索引,那么索引的長(zhǎng)度則是102字節(jié),因?yàn)殚L(zhǎng)度超過(guò)64字節(jié)則會(huì)額外增加2字節(jié)記錄索引的長(zhǎng)度。
從上圖可以看到我在YCSB_KEY這一列(長(zhǎng)度100)上創(chuàng)建了一個(gè)名字為index_ycsb_key的索引,每條索引長(zhǎng)度都為102,想象一下當(dāng)數(shù)據(jù)變的巨大無(wú)比的時(shí)候,索引的大小也是不可以小覷的。而且從這也可以看出,索引的長(zhǎng)度和列類型的長(zhǎng)度還不同,比如varchar它是變長(zhǎng)的字符類型(請(qǐng)看MySQL數(shù)據(jù)類型分析),實(shí)際存儲(chǔ)長(zhǎng)度是是實(shí)際字符的大小,但是索引卻是你聲明的長(zhǎng)度的大小。你創(chuàng)建列的時(shí)候聲明100字節(jié),那么索引長(zhǎng)度就是這個(gè)字節(jié)再加上2,它不管你實(shí)際存儲(chǔ)是多大。
除了創(chuàng)建索引需要消耗時(shí)間,索引文件體積會(huì)變的越來(lái)越大之外,創(chuàng)建索引也需要看的你存儲(chǔ)數(shù)據(jù)的特征。當(dāng)你存儲(chǔ)數(shù)據(jù)很大一部分都是重復(fù)記錄,那這個(gè)時(shí)候創(chuàng)建索引是百害而無(wú)一利。所以,當(dāng)很多數(shù)據(jù)重復(fù)的時(shí)候,索引帶來(lái)的查詢提升的效果是可以直接忽略的,但是這個(gè)時(shí)候你還要承受插入數(shù)據(jù)的時(shí)候創(chuàng)建索引帶來(lái)的性能消耗。
2、緩存的配置。
在MySQL中有多種多樣的緩存,有的緩存負(fù)責(zé)緩存查詢語(yǔ)句,也有的負(fù)責(zé)緩存查詢數(shù)據(jù)。這些緩存內(nèi)容客戶端無(wú)法操作,是由server端來(lái)維護(hù)的。它會(huì)隨著你查詢與修改等相應(yīng)不同操作進(jìn)行不斷更新。通過(guò)其配置文件我們可以看到在MySQL中的緩存:
在這里主要分析query cache,它是主要用來(lái)緩存查詢數(shù)據(jù)。當(dāng)你想使用該cache,必須把query_cache_size大小設(shè)置為非0。當(dāng)設(shè)置大小為非0的時(shí)候,server就會(huì)緩存每次查詢返回的結(jié)果,到下次相同查詢server就直接從緩存獲取數(shù)據(jù),而不是再執(zhí)行查詢。能緩存的數(shù)據(jù)量就和你的size大小設(shè)置有關(guān),所以當(dāng)你設(shè)置的足夠大,數(shù)據(jù)可以完全緩存到內(nèi)存,速度就會(huì)非常之快。
但是,query cache也有它的弊端。當(dāng)你對(duì)數(shù)據(jù)表做任何的更新操作(update/insert/delete)等操作,server為了保證緩存與數(shù)據(jù)庫(kù)的一致性,會(huì)強(qiáng)制刷新緩存數(shù)據(jù),導(dǎo)致緩存數(shù)據(jù)全部失效。所以,當(dāng)一個(gè)表格的更新數(shù)據(jù)表操作非常多的話,query cache是不會(huì)起到查詢提升的性能,還會(huì)影響其他操作的性能。
3、slow_query_log分析。
其實(shí)對(duì)于查詢性能提升,最重要也是最根本的手段也是slow_query的設(shè)置。
當(dāng)你設(shè)置slow_query_log為on的時(shí)候,server端會(huì)對(duì)每次的查詢進(jìn)行記錄,當(dāng)超過(guò)你設(shè)置的慢查詢時(shí)間 (long_query_time)的時(shí)候就把該條查詢記錄到日志。而你對(duì)性能進(jìn)行優(yōu)化的時(shí)候,就可以分析慢查詢?nèi)罩?#xff0c;對(duì)慢查詢的查詢語(yǔ)句進(jìn)行有目的的優(yōu)化。可以通過(guò)創(chuàng)建各種索引,可以通過(guò)分表等操作。
4、分庫(kù)分表
分庫(kù)分表應(yīng)該算是查詢優(yōu)化的殺手锏了。上述各種措施在數(shù)據(jù)量達(dá)到一定等級(jí)之后,能起到優(yōu)化的作用已經(jīng)不明顯了。這個(gè)時(shí)候就必須對(duì)數(shù)據(jù)量進(jìn)行分流。分流一般有分庫(kù)與分表兩種措施。而分表又有垂直切分與水平切分兩種方式。
對(duì)于mysql,其數(shù)據(jù)文件是以文件形式存儲(chǔ)在磁盤(pán)上的。當(dāng)一個(gè)數(shù)據(jù)文件過(guò)大的時(shí)候,操作系統(tǒng)對(duì)大文件的操作就會(huì)比較麻煩與耗時(shí),而且有的操作系統(tǒng)就不支持大文件,所以這個(gè)時(shí)候就必須分表了。另外對(duì)于mysql常用的存儲(chǔ)引擎是Innodb,它的底層數(shù)據(jù)結(jié)構(gòu)是B+樹(shù)。當(dāng)其數(shù)據(jù)文件過(guò)大的時(shí)候,B+樹(shù)就會(huì)從層次和節(jié)點(diǎn)上比較多,當(dāng)查詢一個(gè)節(jié)點(diǎn)的時(shí)候可能會(huì)查詢很多層次,而這必定會(huì)導(dǎo)致多次IO操作進(jìn)行裝載進(jìn)內(nèi)存,肯定會(huì)耗時(shí)的。除此之外還有Innodb對(duì)于B+樹(shù)的鎖機(jī)制。對(duì)每個(gè)節(jié)點(diǎn)進(jìn)行加鎖,那么當(dāng)更改表結(jié)構(gòu)的時(shí)候,這時(shí)候就會(huì)樹(shù)進(jìn)行加鎖,當(dāng)表文件大的時(shí)候,這可以認(rèn)為是不可實(shí)現(xiàn)的。?
所以綜上我們就必須進(jìn)行分表與分庫(kù)的操作。
5、子查詢優(yōu)化
在查詢中經(jīng)常會(huì)用到子查詢,在子查詢的時(shí)候一般使用in或者exist關(guān)鍵詞。針對(duì)in和exist在查詢的時(shí)候當(dāng)數(shù)據(jù)量大到一定程度以后,查詢執(zhí)行時(shí)間就差別比較大。但是,為了避免此類情況出現(xiàn),最好的方式是使用join查詢。因?yàn)樵诮^大多數(shù)情況下,服務(wù)器對(duì)join的查詢優(yōu)化要遠(yuǎn)遠(yuǎn)高于子查詢優(yōu)化。在比較高的版本5.6,mysql查詢會(huì)自動(dòng)把in查詢優(yōu)化成join查詢,就不會(huì)出現(xiàn)子查詢比較慢的問(wèn)題。有時(shí)候也可以采用distinct關(guān)鍵詞來(lái)限制子查詢的數(shù)量,但是需要注意的是distinct很多時(shí)候會(huì)轉(zhuǎn)化為group by,這個(gè)時(shí)候就會(huì)出現(xiàn)一個(gè)?臨時(shí)表,就會(huì)出現(xiàn)copy數(shù)據(jù)到臨時(shí)表的時(shí)延。
更多的子查詢優(yōu)化?請(qǐng)點(diǎn)擊。
分表
如何進(jìn)行分庫(kù)分表,目前互聯(lián)網(wǎng)上有許多的版本,比較知名的一些方案:阿里的TDDL,DRDS和cobar,京東金融的sharding-jdbc;民間組織的MyCAT;360的Atlas;美團(tuán)的zebra;其他比如網(wǎng)易,58,京東等公司都有自研的中間件。
這么多的分庫(kù)分表中間件方案歸總起來(lái),就兩類:client模式和proxy模式。
client模式
proxy模式
無(wú)論是client模式,還是proxy模式。幾個(gè)核心的步驟是一樣的:SQL解析,重寫(xiě),路由,執(zhí)行,結(jié)果歸并。個(gè)人比較傾向于采用client模式,它架構(gòu)簡(jiǎn)單,性能損耗也比較小,運(yùn)維成本低。
如何對(duì)業(yè)務(wù)類型進(jìn)行分庫(kù)分表。分庫(kù)分表最重要的一步,即sharding column的選取,sharding column選擇的好壞將直接決定整個(gè)分庫(kù)分表方案最終是否成功。而sharding column的選取跟業(yè)務(wù)強(qiáng)相關(guān)。在我們的項(xiàng)目場(chǎng)景中,sharding column無(wú)疑最好的選擇是業(yè)務(wù)編號(hào)。通過(guò)業(yè)務(wù)編號(hào),將客戶不同的綁定簽約業(yè)務(wù)保存到不同的表里面去,根據(jù)業(yè)務(wù)編號(hào)路由到相應(yīng)的表中進(jìn)行查詢,達(dá)到進(jìn)一步優(yōu)化sql的目的。
記錄一次MySQL兩千萬(wàn)數(shù)據(jù)的大表優(yōu)化解決過(guò)程,提供三種解決方案(轉(zhuǎn))
問(wèn)題概述
使用阿里云rds for MySQL數(shù)據(jù)庫(kù)(就是MySQL5.6版本),有個(gè)用戶上網(wǎng)記錄表6個(gè)月的數(shù)據(jù)量近2000萬(wàn),保留最近一年的數(shù)據(jù)量達(dá)到4000萬(wàn),查詢速度極慢,日常卡死。嚴(yán)重影響業(yè)務(wù)。
問(wèn)題前提:老系統(tǒng),當(dāng)時(shí)設(shè)計(jì)系統(tǒng)的人大概是大學(xué)沒(méi)畢業(yè),表設(shè)計(jì)和sql語(yǔ)句寫(xiě)的不僅僅是垃圾,簡(jiǎn)直無(wú)法直視。原開(kāi)發(fā)人員都已離職,到我來(lái)維護(hù)。
方案概述
方案一:優(yōu)化現(xiàn)有mysql數(shù)據(jù)庫(kù)。優(yōu)點(diǎn):不影響現(xiàn)有業(yè)務(wù),源程序不需要修改代碼,成本最低。缺點(diǎn):有優(yōu)化瓶頸,數(shù)據(jù)量過(guò)億就玩完了。
方案二:升級(jí)數(shù)據(jù)庫(kù)類型,換一種100%兼容mysql的數(shù)據(jù)庫(kù)。優(yōu)點(diǎn):不影響現(xiàn)有業(yè)務(wù),源程序不需要修改代碼,你幾乎不需要做任何操作就能提升數(shù)據(jù)庫(kù)性能,缺點(diǎn):多花錢(qián)
方案三:一步到位,大數(shù)據(jù)解決方案,更換newsql/nosql數(shù)據(jù)庫(kù)。優(yōu)點(diǎn):擴(kuò)展性強(qiáng),成本低,沒(méi)有數(shù)據(jù)容量瓶頸,缺點(diǎn):需要修改源程序代碼
以上三種方案,按順序使用即可,數(shù)據(jù)量在億級(jí)別一下的沒(méi)必要換nosql,開(kāi)發(fā)成本太高。三種方案我都試了一遍,而且都形成了落地解決方案
方案一詳細(xì)說(shuō)明:優(yōu)化現(xiàn)有mysql數(shù)據(jù)庫(kù)
跟阿里云數(shù)據(jù)庫(kù)大佬電話溝通 and Google解決方案 and 問(wèn)群里大佬,總結(jié)如下(都是精華):
1.數(shù)據(jù)庫(kù)設(shè)計(jì)和表創(chuàng)建時(shí)就要考慮性能
mysql數(shù)據(jù)庫(kù)本身高度靈活,造成性能不足,嚴(yán)重依賴開(kāi)發(fā)人員能力。也就是說(shuō)開(kāi)發(fā)人員能力高,則mysql性能高。這也是很多關(guān)系型數(shù)據(jù)庫(kù)的通病,所以公司的dba通常工資巨高。
設(shè)計(jì)表時(shí)要注意:
- 表字段避免null值出現(xiàn),null值很難查詢優(yōu)化且占用額外的索引空間,推薦默認(rèn)數(shù)字0代替null。
- 盡量使用INT而非BIGINT,如果非負(fù)則加上UNSIGNED(這樣數(shù)值容量會(huì)擴(kuò)大一倍),當(dāng)然能使用TINYINT、SMALLINT、MEDIUM_INT更好。
- 使用枚舉或整數(shù)代替字符串類型
- 盡量使用TIMESTAMP而非DATETIME
- 單表不要有太多字段,建議在20以內(nèi)
- 用整型來(lái)存IP
索引
- 索引并不是越多越好,要根據(jù)查詢有針對(duì)性的創(chuàng)建,考慮在WHERE和ORDER BY命令上涉及的列建立索引,可根據(jù)EXPLAIN來(lái)查看是否用了索引還是全表掃描
- 應(yīng)盡量避免在WHERE子句中對(duì)字段進(jìn)行NULL值判斷,否則將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描
- 值分布很稀少的字段不適合建索引,例如"性別"這種只有兩三個(gè)值的字段
- 字符字段只建前綴索引
- 字符字段最好不要做主鍵
- 不用外鍵,由程序保證約束
- 盡量不用UNIQUE,由程序保證約束
- 使用多列索引時(shí)主意順序和查詢條件保持一致,同時(shí)刪除不必要的單列索引
簡(jiǎn)言之就是使用合適的數(shù)據(jù)類型,選擇合適的索引
2.sql的編寫(xiě)需要注意優(yōu)化
- 使用limit對(duì)查詢結(jié)果的記錄進(jìn)行限定
- 避免select *,將需要查找的字段列出來(lái)
- 使用連接(join)來(lái)代替子查詢
- 拆分大的delete或insert語(yǔ)句
- 可通過(guò)開(kāi)啟慢查詢?nèi)罩緛?lái)找出較慢的SQL
- 不做列運(yùn)算:SELECT id WHERE age + 1 = 10,任何對(duì)列的操作都將導(dǎo)致表掃描,它包括數(shù)據(jù)庫(kù)教程函數(shù)、計(jì)算表達(dá)式等等,查詢時(shí)要盡可能將操作移至等號(hào)右邊
- sql語(yǔ)句盡可能簡(jiǎn)單:一條sql只能在一個(gè)cpu運(yùn)算;大語(yǔ)句拆小語(yǔ)句,減少鎖時(shí)間;一條大sql可以堵死整個(gè)庫(kù)
- OR改寫(xiě)成IN:OR的效率是n級(jí)別,IN的效率是log(n)級(jí)別,in的個(gè)數(shù)建議控制在200以內(nèi)
- 不用函數(shù)和觸發(fā)器,在應(yīng)用程序?qū)崿F(xiàn)
- 避免%xxx式查詢
- 少用JOIN
- 使用同類型進(jìn)行比較,比如用'123'和'123'比,123和123比
- 盡量避免在WHERE子句中使用!=或<>操作符,否則將引擎放棄使用索引而進(jìn)行全表掃描
- 對(duì)于連續(xù)數(shù)值,使用BETWEEN不用IN:SELECT id FROM t WHERE num BETWEEN 1 AND 5
- 列表數(shù)據(jù)不要拿全表,要使用LIMIT來(lái)分頁(yè),每頁(yè)數(shù)量也不要太大
引擎
目前廣泛使用的是MyISAM和InnoDB兩種引擎:
MyISAM引擎是MySQL 5.1及之前版本的默認(rèn)引擎,它的特點(diǎn)是:
- 不支持行鎖,讀取時(shí)對(duì)需要讀到的所有表加鎖,寫(xiě)入時(shí)則對(duì)表加排它鎖
- 不支持事務(wù)
- 不支持外鍵
- 不支持崩潰后的安全恢復(fù)
- 在表有讀取查詢的同時(shí),支持往表中插入新紀(jì)錄
- 支持BLOB和TEXT的前500個(gè)字符索引,支持全文索引
- 支持延遲更新索引,極大提升寫(xiě)入性能
- 對(duì)于不會(huì)進(jìn)行修改的表,支持壓縮表,極大減少磁盤(pán)空間占用
InnoDB在MySQL 5.5后成為默認(rèn)索引,它的特點(diǎn)是:
- 支持行鎖,采用MVCC來(lái)支持高并發(fā)
- 支持事務(wù)
- 支持外鍵
- 支持崩潰后的安全恢復(fù)
- 不支持全文索引
總體來(lái)講,MyISAM適合SELECT密集型的表,而InnoDB適合INSERT和UPDATE密集型的表
MyISAM速度可能超快,占用存儲(chǔ)空間也小,但是程序要求事務(wù)支持,故InnoDB是必須的,故該方案無(wú)法執(zhí)行,放棄!
3.分區(qū)
MySQL在5.1版引入的分區(qū)是一種簡(jiǎn)單的水平拆分,用戶需要在建表的時(shí)候加上分區(qū)參數(shù),對(duì)應(yīng)用是透明的無(wú)需修改代碼
對(duì)用戶來(lái)說(shuō),分區(qū)表是一個(gè)獨(dú)立的邏輯表,但是底層由多個(gè)物理子表組成,實(shí)現(xiàn)分區(qū)的代碼實(shí)際上是通過(guò)對(duì)一組底層表的對(duì)象封裝,但對(duì)SQL層來(lái)說(shuō)是一個(gè)完全封裝底層的黑盒子。MySQL實(shí)現(xiàn)分區(qū)的方式也意味著索引也是按照分區(qū)的子表定義,沒(méi)有全局索引
用戶的SQL語(yǔ)句是需要針對(duì)分區(qū)表做優(yōu)化,SQL條件中要帶上分區(qū)條件的列,從而使查詢定位到少量的分區(qū)上,否則就會(huì)掃描全部分區(qū),可以通過(guò)EXPLAIN PARTITIONS來(lái)查看某條SQL語(yǔ)句會(huì)落在那些分區(qū)上,從而進(jìn)行SQL優(yōu)化,我測(cè)試,查詢時(shí)不帶分區(qū)條件的列,也會(huì)提高速度,故該措施值得一試。
分區(qū)的好處是:
- 可以讓單表存儲(chǔ)更多的數(shù)據(jù)
- 分區(qū)表的數(shù)據(jù)更容易維護(hù),可以通過(guò)清楚整個(gè)分區(qū)批量刪除大量數(shù)據(jù),也可以增加新的分區(qū)來(lái)支持新插入的數(shù)據(jù)。另外,還可以對(duì)一個(gè)獨(dú)立分區(qū)進(jìn)行優(yōu)化、檢查、修復(fù)等操作
- 部分查詢能夠從查詢條件確定只落在少數(shù)分區(qū)上,速度會(huì)很快
- 分區(qū)表的數(shù)據(jù)還可以分布在不同的物理設(shè)備上,從而搞笑利用多個(gè)硬件設(shè)備
- 可以使用分區(qū)表賴避免某些特殊瓶頸,例如InnoDB單個(gè)索引的互斥訪問(wèn)、ext3文件系統(tǒng)的inode鎖競(jìng)爭(zhēng)
- 可以備份和恢復(fù)單個(gè)分區(qū)
分區(qū)的限制和缺點(diǎn):
- 一個(gè)表最多只能有1024個(gè)分區(qū)
- 如果分區(qū)字段中有主鍵或者唯一索引的列,那么所有主鍵列和唯一索引列都必須包含進(jìn)來(lái)
- 分區(qū)表無(wú)法使用外鍵約束
- NULL值會(huì)使分區(qū)過(guò)濾無(wú)效
- 所有分區(qū)必須使用相同的存儲(chǔ)引擎
分區(qū)的類型:
- RANGE分區(qū):基于屬于一個(gè)給定連續(xù)區(qū)間的列值,把多行分配給分區(qū)
- LIST分區(qū):類似于按RANGE分區(qū),區(qū)別在于LIST分區(qū)是基于列值匹配一個(gè)離散值集合中的某個(gè)值來(lái)進(jìn)行選擇
- HASH分區(qū):基于用戶定義的表達(dá)式的返回值來(lái)進(jìn)行選擇的分區(qū),該表達(dá)式使用將要插入到表中的這些行的列值進(jìn)行計(jì)算。這個(gè)函數(shù)可以包含MySQL中有效的、產(chǎn)生非負(fù)整數(shù)值的任何表達(dá)式
- KEY分區(qū):類似于按HASH分區(qū),區(qū)別在于KEY分區(qū)只支持計(jì)算一列或多列,且MySQL服務(wù)器提供其自身的哈希函數(shù)。必須有一列或多列包含整數(shù)值
我首先根據(jù)月份把上網(wǎng)記錄表RANGE分區(qū)了12份,查詢效率提高6倍左右,效果不明顯,故:換id為HASH分區(qū),分了64個(gè)分區(qū),查詢速度提升顯著。問(wèn)題解決!
4.分表
分表就是把一張大表,按照如上過(guò)程都優(yōu)化了,還是查詢卡死,那就把這個(gè)表分成多張表,把一次查詢分成多次查詢,然后把結(jié)果組合返回給用戶。
分表分為垂直拆分和水平拆分,通常以某個(gè)字段做拆分項(xiàng)。比如以id字段拆分為100張表: 表名為 tableName_id%100
但:分表需要修改源程序代碼,會(huì)給開(kāi)發(fā)帶來(lái)大量工作,極大的增加了開(kāi)發(fā)成本,故:只適合在開(kāi)發(fā)初期就考慮到了大量數(shù)據(jù)存在,做好了分表處理,不適合應(yīng)用上線了再做修改,成本太高!!!而且選擇這個(gè)方案,都不如選擇我提供的第二第三個(gè)方案的成本低!故不建議采用。
5.分庫(kù)
把一個(gè)數(shù)據(jù)庫(kù)分成多個(gè),建議做個(gè)讀寫(xiě)分離就行了,真正的做分庫(kù)也會(huì)帶來(lái)大量的開(kāi)發(fā)成本,得不償失!不推薦使用。
方案二詳細(xì)說(shuō)明:升級(jí)數(shù)據(jù)庫(kù),換一個(gè)100%兼容mysql的數(shù)據(jù)庫(kù)
mysql性能不行,那就換個(gè)。為保證源程序代碼不修改,保證現(xiàn)有業(yè)務(wù)平穩(wěn)遷移,故需要換一個(gè)100%兼容mysql的數(shù)據(jù)庫(kù)。
開(kāi)源選擇
1.tiDB https://github.com/pingcap/tidb
2.Cubrid https://www.cubrid.org/
3.開(kāi)源數(shù)據(jù)庫(kù)會(huì)帶來(lái)大量的運(yùn)維成本且其工業(yè)品質(zhì)和MySQL尚有差距,有很多坑要踩,如果你公司要求必須自建數(shù)據(jù)庫(kù),那么選擇該類型產(chǎn)品。
云數(shù)據(jù)選擇
阿里云POLARDB
https://www.aliyun.com/product/polardb?spm=a2c4g.11174283.cloudEssentials.47.7a984b5cS7h4wH
官方介紹語(yǔ):POLARDB 是阿里云自研的下一代關(guān)系型分布式云原生數(shù)據(jù)庫(kù),100%兼容MySQL,存儲(chǔ)容量最高可達(dá) 100T,性能最高提升至 MySQL 的 6 倍。POLARDB 既融合了商業(yè)數(shù)據(jù)庫(kù)穩(wěn)定、可靠、高性能的特征,又具有開(kāi)源數(shù)據(jù)庫(kù)簡(jiǎn)單、可擴(kuò)展、持續(xù)迭代的優(yōu)勢(shì),而成本只需商用數(shù)據(jù)庫(kù)的 1/10。
我開(kāi)通測(cè)試了一下,支持免費(fèi)mysql的數(shù)據(jù)遷移,無(wú)操作成本,性能提升在10倍左右,價(jià)格跟rds相差不多,是個(gè)很好的備選解決方案!
阿里云OcenanBase
淘寶使用的,扛得住雙十一,性能卓著,但是在公測(cè)中,我無(wú)法嘗試,但值得期待
阿里云HybridDB for MySQL (原PetaData)
https://www.aliyun.com/product/petadata?spm=a2c4g.11174283.cloudEssentials.54.7a984b5cS7h4wH
官方介紹:云數(shù)據(jù)庫(kù)HybridDB for MySQL (原名PetaData)是同時(shí)支持海量數(shù)據(jù)在線事務(wù)(OLTP)和在線分析(OLAP)的HTAP(Hybrid Transaction/Analytical Processing)關(guān)系型數(shù)據(jù)庫(kù)。
我也測(cè)試了一下,是一個(gè)olap和oltp兼容的解決方案,但是價(jià)格太高,每小時(shí)高達(dá)10塊錢(qián),用來(lái)做存儲(chǔ)太浪費(fèi)了,適合存儲(chǔ)和分析一起用的業(yè)務(wù)。
騰訊云DCDB
https://cloud.tencent.com/product/dcdb_for_tdsql
官方介紹:DCDB又名TDSQL,一種兼容MySQL協(xié)議和語(yǔ)法,支持自動(dòng)水平拆分的高性能分布式數(shù)據(jù)庫(kù)——即業(yè)務(wù)顯示為完整的邏輯表,數(shù)據(jù)卻均勻的拆分到多個(gè)分片中;每個(gè)分片默認(rèn)采用主備架構(gòu),提供災(zāi)備、恢復(fù)、監(jiān)控、不停機(jī)擴(kuò)容等全套解決方案,適用于TB或PB級(jí)的海量數(shù)據(jù)場(chǎng)景。
騰訊的我不喜歡用,不多說(shuō)。原因是出了問(wèn)題找不到人,線上問(wèn)題無(wú)法解決頭疼!但是他價(jià)格便宜,適合超小公司,玩玩。
方案三詳細(xì)說(shuō)明:去掉mysql,換大數(shù)據(jù)引擎處理數(shù)據(jù)
開(kāi)源解決方案
hadoop家族。hbase/hive懟上就是了。但是有很高的運(yùn)維成本,一般公司是玩不起的,沒(méi)十萬(wàn)投入是不會(huì)有很好的產(chǎn)出的!
云解決方案
這個(gè)就比較多了,也是一種未來(lái)趨勢(shì),大數(shù)據(jù)由專業(yè)的公司提供專業(yè)的服務(wù),小公司或個(gè)人購(gòu)買(mǎi)服務(wù),大數(shù)據(jù)就像水/電等公共設(shè)施一樣,存在于社會(huì)的方方面面。國(guó)內(nèi)做的最好的當(dāng)屬阿里云。
我選擇了阿里云的MaxCompute配合DataWorks,使用超級(jí)舒服,按量付費(fèi),成本極低。
MaxCompute可以理解為開(kāi)源的Hive,提供sql/mapreduce/ai算法/python腳本/shell腳本等方式操作數(shù)據(jù),數(shù)據(jù)以表格的形式展現(xiàn),以分布式方式存儲(chǔ),采用定時(shí)任務(wù)和批處理的方式處理數(shù)據(jù)。DataWorks提供了一種工作流的方式管理你的數(shù)據(jù)處理任務(wù)和調(diào)度監(jiān)控。
當(dāng)然你也可以選擇阿里云hbase等其他產(chǎn)品,我這里主要是離線處理,故選擇MaxCompute,基本都是圖形界面操作,大概寫(xiě)了300行sql,費(fèi)用不超過(guò)100塊錢(qián)就解決了數(shù)據(jù)處理問(wèn)題。
千萬(wàn)條數(shù)據(jù)優(yōu)化建議30條
1、對(duì)查詢進(jìn)行優(yōu)化、應(yīng)盡量避免全表掃描、首先應(yīng)考慮在 where 及 order by 涉及的列上建立索引。
2、應(yīng)盡量避免在 where 子句中對(duì)字段進(jìn)行 null 值判斷、否則將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描、如:
select id from t where num is null;
--可以在num上設(shè)置默認(rèn)值0、確保表中num列沒(méi)有null值、然后這樣查詢:
select id from t where num=0;
3、應(yīng)盡量避免在 where 子句中使用!=或<>操作符、否則將引擎放棄使用索引而進(jìn)行全表掃描。
4、應(yīng)盡量避免在 where 子句中使用 or 來(lái)連接條件、否則將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描、如:
select id from t where num=10 or num=20
--可以這樣查詢:
select id from t where num=10 union all select id from t where num=20;
5、in 和 not in 也要慎用、否則會(huì)導(dǎo)致全表掃描、如:
select id from t where num in(1,2,3);
對(duì)于連續(xù)的數(shù)值、能用 between 就不要用 in 了:
select id from t where num between 1 and 3;
6、下面的查詢也將導(dǎo)致全表掃描:
select id from t where name like '%abc%';
--若要提高效率、可以考慮全文檢索。
7、如果在 where 子句中使用參數(shù)、也會(huì)導(dǎo)致全表掃描。因?yàn)镾QL只有在運(yùn)行時(shí)才會(huì)解析局部變量、但優(yōu)化程序不能將訪問(wèn)計(jì)劃的選擇推遲到運(yùn)行時(shí);它必須在編譯時(shí)進(jìn)行選擇。然而、如果在編譯時(shí)建立訪問(wèn)計(jì)劃、變量的值還是未知的、因而無(wú)法作為索引選擇的輸入項(xiàng)。如下面語(yǔ)句將進(jìn)行全表掃描:
select id from t where num=@num
--可以改為強(qiáng)制查詢使用索引:
select id from t with(index(索引名)) where num=?@num?;
8、應(yīng)盡量避免在 where 子句中對(duì)字段進(jìn)行表達(dá)式操作、這將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描。如:
select id from t where num/2=100;
--應(yīng)改為:
select id from t where num=100*2;
9、應(yīng)盡量避免在where子句中對(duì)字段進(jìn)行函數(shù)操作、這將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描。如:
select id from t where substring(name,1,3)='abc';? --name以abc開(kāi)頭的id
select id from t where datediff(day,createdate,'2005-11-30')=0;? --‘2005-11-30’生成的id
--應(yīng)改為:
select id from t where name like 'abc%';
select id from t where createdate>='2005-11-30' and createdate<'2005-12-1';
10、不要在 where 子句中的“=”左邊進(jìn)行函數(shù)、算術(shù)運(yùn)算或其他表達(dá)式運(yùn)算、否則系統(tǒng)將可能無(wú)法正確使用索引。
11、在使用索引字段作為條件時(shí)、如果該索引是復(fù)合索引、那么必須使用到該索引中的第一個(gè)字段作為條件時(shí)才能保證系統(tǒng)使用該索引、否則該索引將不會(huì)被使用、并且應(yīng)盡可能的讓字段順序與索引順序相一致。
12、不要寫(xiě)一些沒(méi)有意義的查詢、如需要生成一個(gè)空表結(jié)構(gòu):
select col1,col2 into #t from t where 1=0;? --這類代碼不會(huì)返回任何結(jié)果集、但是會(huì)消耗系統(tǒng)資源的、應(yīng)改成這樣:
create table #t(...);
13、很多時(shí)候用 exists 代替 in 是一個(gè)好的選擇:
select num from a where num in(select num from b);
--用下面的語(yǔ)句替換:
select num from a where exists(select 1 from b where num=a.num);
14、并不是所有索引對(duì)查詢都有效、SQL是根據(jù)表中數(shù)據(jù)來(lái)進(jìn)行查詢優(yōu)化的、當(dāng)索引列有大量數(shù)據(jù)重復(fù)時(shí)、SQL查詢可能不會(huì)去利用索引、如一表中有字段sex、male、female幾乎各一半、那么即使在sex上建了索引也對(duì)查詢效率起不了作用。
15、索引并不是越多越好、索引固然可以提高相應(yīng)的 select 的效率、但同時(shí)也降低了 insert 及 update 的效率、因?yàn)?insert 或 update 時(shí)有可能會(huì)重建索引、所以怎樣建索引需要慎重考慮、視具體情況而定。一個(gè)表的索引數(shù)最好不要超過(guò)6個(gè)、若太多則應(yīng)考慮一些不常使用到的列上建的索引是否有必要。
16、應(yīng)盡可能的避免更新 clustered 索引數(shù)據(jù)列、因?yàn)?clustered 索引數(shù)據(jù)列的順序就是表記錄的物理存儲(chǔ)順序、一旦該列值改變將導(dǎo)致整個(gè)表記錄的順序的調(diào)整、會(huì)耗費(fèi)相當(dāng)大的資源。若應(yīng)用系統(tǒng)需要頻繁更新 clustered 索引數(shù)據(jù)列、那么需要考慮是否應(yīng)將該索引建為 clustered 索引。
17、盡量使用數(shù)字型字段、若只含數(shù)值信息的字段盡量不要設(shè)計(jì)為字符型、這會(huì)降低查詢和連接的性能、并會(huì)增加存儲(chǔ)開(kāi)銷。這是因?yàn)橐嬖谔幚聿樵兒瓦B接時(shí)會(huì)逐個(gè)比較字符串中每一個(gè)字符、而對(duì)于數(shù)字型而言只需要比較一次就夠了。
18、盡可能的使用 varchar/nvarchar 代替 char/nchar 、因?yàn)槭紫茸冮L(zhǎng)字段存儲(chǔ)空間小、可以節(jié)省存儲(chǔ)空間、其次對(duì)于查詢來(lái)說(shuō)、在一個(gè)相對(duì)較小的字段內(nèi)搜索效率顯然要高些。
19、任何地方都不要使用 select * from t 、用具體的字段列表代替“*”、不要返回用不到的任何字段。
20、盡量使用表變量來(lái)代替臨時(shí)表。如果表變量包含大量數(shù)據(jù)、請(qǐng)注意索引非常有限(只有主鍵索引)。
21、避免頻繁創(chuàng)建和刪除臨時(shí)表、以減少系統(tǒng)表資源的消耗。
22、臨時(shí)表并不是不可使用、適當(dāng)?shù)厥褂盟鼈兛梢允鼓承├谈行А⒗纭?dāng)需要重復(fù)引用大型表或常用表中的某個(gè)數(shù)據(jù)集時(shí)。但是、對(duì)于一次性事件、最好使用導(dǎo)出表。
23、在新建臨時(shí)表時(shí)、如果一次性插入數(shù)據(jù)量很大、那么可以使用 select into 代替 create table、避免造成大量 log 、以提高速度;如果數(shù)據(jù)量不大、為了緩和系統(tǒng)表的資源、應(yīng)先create table、然后insert。
24、如果使用到了臨時(shí)表、在存儲(chǔ)過(guò)程的最后務(wù)必將所有的臨時(shí)表顯式刪除、先 truncate table 、然后 drop table 、這樣可以避免系統(tǒng)表的較長(zhǎng)時(shí)間鎖定。
25、盡量避免使用游標(biāo)、因?yàn)橛螛?biāo)的效率較差、如果游標(biāo)操作的數(shù)據(jù)超過(guò)1萬(wàn)行、那么就應(yīng)該考慮改寫(xiě)。
26、使用基于游標(biāo)的方法或臨時(shí)表方法之前、應(yīng)先尋找基于集的解決方案來(lái)解決問(wèn)題、基于集的方法通常更有效。
27、與臨時(shí)表一樣、游標(biāo)并不是不可使用。對(duì)小型數(shù)據(jù)集使用 FAST_FORWARD 游標(biāo)通常要優(yōu)于其他逐行處理方法、尤其是在必須引用幾個(gè)表才能獲得所需的數(shù)據(jù)時(shí)。在結(jié)果集中包括“合計(jì)”的例程通常要比使用游標(biāo)執(zhí)行的速度快。如果開(kāi)發(fā)時(shí)間允許、基于游標(biāo)的方法和基于集的方法都可以嘗試一下、看哪一種方法的效果更好。
28、在所有的存儲(chǔ)過(guò)程和觸發(fā)器的開(kāi)始處設(shè)置 SET NOCOUNT ON 、在結(jié)束時(shí)設(shè)置 SET NOCOUNT OFF 。無(wú)需在執(zhí)行存儲(chǔ)過(guò)程和觸發(fā)器的每個(gè)語(yǔ)句后向客戶端發(fā)送 DONE_IN_PROC 消息。
29、盡量避免大事務(wù)操作、提高系統(tǒng)并發(fā)能力。
30、盡量避免向客戶端返回大數(shù)據(jù)量、若數(shù)據(jù)量過(guò)大、應(yīng)該考慮相應(yīng)需求是否合理。
MySQL 性能優(yōu)化的建議
1. 為查詢緩存優(yōu)化你的查詢
大多數(shù)的MySQL服務(wù)器都開(kāi)啟了查詢緩存。這是提高性最有效的方法之一,而且這是被MySQL的數(shù)據(jù)庫(kù)引擎處理的。當(dāng)有很多相同的查詢被執(zhí)行了多次的時(shí)候,這些查詢結(jié)果會(huì)被放到一個(gè)緩存中,這樣后續(xù)的相同的查詢就不用操作表而直接訪問(wèn)緩存結(jié)果了。這里最主要的問(wèn)題是,對(duì)于程序員來(lái)說(shuō),這個(gè)事情是很容易被忽略的。因?yàn)?#xff0c;我們某些查詢語(yǔ)句會(huì)讓MySQL不使用緩存。請(qǐng)看下面的示例:
// 查詢緩存不開(kāi)啟 $r?= mysql_query(?"SELECT username FROM user WHERE signup_date >= CURDATE()"?);// 開(kāi)啟查詢緩存 $today?=?date?(?"Y-m-d"?); $r?= mysql_query(?"SELECT username FROM user WHERE signup_date >= '$today'"?);上面兩條SQL語(yǔ)句的差別就是 CURDATE() ,MySQL的查詢緩存對(duì)這個(gè)函數(shù)不起作用。所以,像 NOW() 和 RAND() 或是其它的諸如此類的SQL函數(shù)都不會(huì)開(kāi)啟查詢緩存,因?yàn)檫@些函數(shù)的返回是會(huì)不定的易變的。所以,你所需要的就是用一個(gè)變量來(lái)代替MySQL的函數(shù),從而開(kāi)啟緩存。
2. EXPLAIN 你的 SELECT 查詢
EXPLAIN 博客https://blog.csdn.net/fenglepeng/article/details/103392319
使用?EXPLAIN?關(guān)鍵字可以讓你知道MySQL是如何處理你的SQL語(yǔ)句的。這可以幫你分析你的查詢語(yǔ)句或是表結(jié)構(gòu)的性能瓶頸。
EXPLAIN 的查詢結(jié)果還會(huì)告訴你你的索引主鍵被如何利用的,你的數(shù)據(jù)表是如何被搜索和排序的……等等。
3. 當(dāng)只要一行數(shù)據(jù)時(shí)使用 LIMIT 1
當(dāng)你查詢表的有些時(shí)候,你已經(jīng)知道結(jié)果只會(huì)有一條結(jié)果,但因?yàn)槟憧赡苄枰etch游標(biāo),或是你也許會(huì)去檢查返回的記錄數(shù)。在這種情況下,加上 LIMIT 1 可以增加性能。這樣一樣,MySQL數(shù)據(jù)庫(kù)引擎會(huì)在找到一條數(shù)據(jù)后停止搜索,而不是繼續(xù)往后查少下一條符合記錄的數(shù)據(jù)。下面的示例,只是為了找一下是否有“中國(guó)”的用戶,很明顯,后面的會(huì)比前面的更有效率。(第一條中是Select *,第二條是Select 1)
// 沒(méi)有效率的: $r = mysql_query( "SELECT * FROM user WHERE country = 'China'" ); if (mysql_num_rows( $r ) > 0) {// ... }// 有效率的: $r = mysql_query( "SELECT 1 FROM user WHERE country = 'China' LIMIT 1" ); if (mysql_num_rows( $r ) > 0) {// ... }4. 為搜索字段建索引
索引并不一定就是給主鍵或是唯一的字段。如果在你的表中,有某個(gè)字段你總要會(huì)經(jīng)常用來(lái)做搜索,那么,請(qǐng)為其建立索引吧。
從上圖你可以看到那個(gè)搜索字串 “l(fā)ast_name LIKE ‘a(chǎn)%’”,一個(gè)是建了索引,一個(gè)是沒(méi)有索引,性能差了4倍左右。另外,你應(yīng)該也需要知道什么樣的搜索是不能使用正常的索引的。例如,當(dāng)你需要在一篇大的文章中搜索一個(gè)詞時(shí),如: “WHERE post_content LIKE ‘%apple%’”,索引可能是沒(méi)有意義的。你可能需要使用MySQL 全文索引?或是自己做一個(gè)索引(比如說(shuō):搜索關(guān)鍵詞或是Tag什么的)
5. 在Join表的時(shí)候使用相當(dāng)類型的列,并將其索引
如果你的應(yīng)用程序有很多 JOIN 查詢,你應(yīng)該確認(rèn)兩個(gè)表中Join的字段是被建過(guò)索引的。這樣,MySQL內(nèi)部會(huì)啟動(dòng)為你優(yōu)化Join的SQL語(yǔ)句的機(jī)制。而且,這些被用來(lái)Join的字段,應(yīng)該是相同的類型的。例如:如果你要把 DECIMAL 字段和一個(gè) INT 字段Join在一起,MySQL就無(wú)法使用它們的索引。對(duì)于那些STRING類型,還需要有相同的字符集才行。(兩個(gè)表的字符集有可能不一樣)
// 在state中查找company $r?= mysql_query("SELECT company_name FROM usersLEFT JOIN companies ON (users.state = companies.state)WHERE users.id =?$user_id?");// 兩個(gè) state 字段應(yīng)該是被建過(guò)索引的,而且應(yīng)該是相當(dāng)?shù)念愋?#xff0c;相同的字符集。千萬(wàn)不要 ORDER BY RAND()
想打亂返回的數(shù)據(jù)行?隨機(jī)挑一個(gè)數(shù)據(jù)?真不知道誰(shuí)發(fā)明了這種用法,但很多新手很喜歡這樣用。但你確不了解這樣做有多么可怕的性能問(wèn)題。如果你真的想把返回的數(shù)據(jù)行打亂了,你有N種方法可以達(dá)到這個(gè)目的。這樣使用只讓你的數(shù)據(jù)庫(kù)的性能呈指數(shù)級(jí)的下降。這里的問(wèn)題是:MySQL會(huì)不得 不去執(zhí)行RAND()函數(shù)(很耗CPU時(shí)間),而且這是為了每一行記錄去記行,然后再對(duì)其排序。就算是你用了Limit 1也無(wú)濟(jì)于事(因?yàn)橐判?#xff09;
下面的示例是隨機(jī)挑一條記錄
// 千萬(wàn)不要這樣做: $r?= mysql_query(?"SELECT username FROM user ORDER BY RAND() LIMIT 1"?);// 這要會(huì)更好: $r?= mysql_query(?"SELECT count(*) FROM user"?); $d?= mysql_fetch_row(?$r?); $rand?= mt_rand(0,?$d?[0] - 1); $r?= mysql_query(?"SELECT username FROM user LIMIT $rand, 1"?);7. 避免 SELECT *。
從數(shù)據(jù)庫(kù)里讀出越多的數(shù)據(jù),那么查詢就會(huì)變得越慢。并且,如果你的數(shù)據(jù)庫(kù)服務(wù)器和WEB服務(wù)器是兩臺(tái)獨(dú)立的服務(wù)器的話,這還會(huì)增加網(wǎng)絡(luò)傳輸?shù)呢?fù)載。所以,你應(yīng)該養(yǎng)成一個(gè)需要什么就取什么的好的習(xí)慣。
// 不推薦 $r?= mysql_query(?"SELECT * FROM user WHERE user_id = 1"?); $d?= mysql_fetch_assoc(?$r?); echo?"Welcome {$d['username']}"?;// 推薦 $r?= mysql_query(?"SELECT username FROM user WHERE user_id = 1"?); $d?= mysql_fetch_assoc(?$r?); echo?"Welcome {$d['username']}"?;8.永遠(yuǎn)為每張表設(shè)置一個(gè)ID
我們應(yīng)該為數(shù)據(jù)庫(kù)里的每張表都設(shè)置一個(gè)ID做為其主鍵,而且最好的是一個(gè)INT型的(推薦使用UNSIGNED),并設(shè)置上自動(dòng)增加的 AUTO_INCREMENT標(biāo)志。就算是你 users 表有一個(gè)主鍵叫 “email”的字段,你也別讓它成為主鍵。使用 VARCHAR 類型來(lái)當(dāng)主鍵會(huì)使用得性能下降。另外,在你的程序中,你應(yīng)該使用表的ID來(lái)構(gòu)造你的數(shù)據(jù)結(jié)構(gòu)。而且,在MySQL數(shù)據(jù)引擎下,還有一些操作需要使用主鍵,在這些情況下,主鍵的性能和設(shè)置變得非常重要,比如,集群,分區(qū)……在這里,只有一個(gè)情況是例外,那就是“關(guān)聯(lián)表”的“外鍵”,也就是說(shuō),這個(gè)表的主鍵,通過(guò)若干個(gè)別的表的主鍵構(gòu)成。我們把這個(gè)情況叫做“外鍵”。比 如:有一個(gè)“學(xué)生表”有學(xué)生的ID,有一個(gè)“課程表”有課程ID,那么,“成績(jī)表”就是“關(guān)聯(lián)表”了,其關(guān)聯(lián)了學(xué)生表和課程表,在成績(jī)表中,學(xué)生ID和課 程ID叫“外鍵”其共同組成主鍵。
9. 使用 ENUM 而不是 VARCHAR
ENUM?類型是非常快和緊湊的。在實(shí)際上,其保存的是 TINYINT,但其外表上顯示為字符串。這樣一來(lái),用這個(gè)字段來(lái)做一些選項(xiàng)列表變得相當(dāng)?shù)耐昝馈?/p>
如果你有一個(gè)字段,比如“性別”,“國(guó)家”,“民族”,“狀態(tài)”或“部門(mén)”,你知道這些字段的取值是有限而且固定的,那么,你應(yīng)該使用 ENUM 而不是 VARCHAR。
MySQL也有一個(gè)“建議”(見(jiàn)第十條)告訴你怎么去重新組織你的表結(jié)構(gòu)。當(dāng)你有一個(gè) VARCHAR 字段時(shí),這個(gè)建議會(huì)告訴你把其改成 ENUM 類型。使用 PROCEDURE ANALYSE() 你可以得到相關(guān)的建議。
10. 從 PROCEDURE ANALYSE() 取得建議
PROCEDURE ANALYSE()?會(huì)讓 MySQL 幫你去分析你的字段和其實(shí)際的數(shù)據(jù),并會(huì)給你一些有用的建議。只有表中有實(shí)際的數(shù)據(jù),這些建議才會(huì)變得有用,因?yàn)橐鲆恍┐蟮臎Q定是需要有數(shù)據(jù)作為基礎(chǔ) 的。
例如,如果你創(chuàng)建了一個(gè) INT 字段作為你的主鍵,然而并沒(méi)有太多的數(shù)據(jù),那么,PROCEDURE ANALYSE()會(huì)建議你把這個(gè)字段的類型改成 MEDIUMINT 。或是你使用了一個(gè) VARCHAR 字段,因?yàn)閿?shù)據(jù)不多,你可能會(huì)得到一個(gè)讓你把它改成 ENUM 的建議。這些建議,都是可能因?yàn)閿?shù)據(jù)不夠多,所以決策做得就不夠準(zhǔn)。
在phpmyadmin里,你可以在查看表時(shí),點(diǎn)擊 “Propose table structure” 來(lái)查看這些建議
一定要注意,這些只是建議,只有當(dāng)你的表里的數(shù)據(jù)越來(lái)越多時(shí),這些建議才會(huì)變得準(zhǔn)確。一定要記住,你才是最終做決定的人。
11. 盡可能的使用 NOT NULL
除非你有一個(gè)很特別的原因去使用 NULL 值,你應(yīng)該總是讓你的字段保持 NOT NULL。這看起來(lái)好像有點(diǎn)爭(zhēng)議,請(qǐng)往下看。
首先,問(wèn)問(wèn)你自己“Empty”和“NULL”有多大的區(qū)別(如果是INT,那就是0和NULL)?如果你覺(jué)得它們之間沒(méi)有什么區(qū)別,那么你就不要 使用NULL。(你知道嗎?在 Oracle 里,NULL 和 Empty 的字符串是一樣的!)
不要以為 NULL 不需要空間,其需要額外的空間,并且,在你進(jìn)行比較的時(shí)候,你的程序會(huì)更復(fù)雜。 當(dāng)然,這里并不是說(shuō)你就不能使用NULL了,現(xiàn)實(shí)情況是很復(fù)雜的,依然會(huì)有些情況下,你需要使用NULL值。
下面摘自MySQL自己的文檔:
“NULL columns require additional space in the row to record whether their values are NULL. For MyISAM tables, each NULL column takes one bit extra, rounded up to the nearest byte.”
12. Prepared Statements
Prepared Statements很像存儲(chǔ)過(guò)程,是一種運(yùn)行在后臺(tái)的SQL語(yǔ)句集合,我們可以從使用 prepared statements 獲得很多好處,無(wú)論是性能問(wèn)題還是安全問(wèn)題。
Prepared Statements 可以檢查一些你綁定好的變量,這樣可以保護(hù)你的程序不會(huì)受到“SQL注入式”攻擊。當(dāng)然,你也可以手動(dòng)地檢查你的這些變量,然而,手動(dòng)的檢查容易出問(wèn)題, 而且很經(jīng)常會(huì)被程序員忘了。當(dāng)我們使用一些framework或是ORM的時(shí)候,這樣的問(wèn)題會(huì)好一些。
在性能方面,當(dāng)一個(gè)相同的查詢被使用多次的時(shí)候,這會(huì)為你帶來(lái)可觀的性能優(yōu)勢(shì)。你可以給這些Prepared Statements定義一些參數(shù),而MySQL只會(huì)解析一次。
雖然最新版本的MySQL在傳輸Prepared Statements是使用二進(jìn)制形勢(shì),所以這會(huì)使得網(wǎng)絡(luò)傳輸非常有效率。
當(dāng)然,也有一些情況下,我們需要避免使用Prepared Statements,因?yàn)槠洳恢С植樵兙彺妗5珦?jù)說(shuō)版本5.1后支持了。
在PHP中要使用prepared statements,你可以查看其使用手冊(cè):mysqli 擴(kuò)展?或是使用數(shù)據(jù)庫(kù)抽象層,如:?PDO?.
// 創(chuàng)建 prepared statement if?(?$stmt?=?$mysqli?->prepare(?"SELECT username FROM user WHERE state=?"?)) {// 綁定參數(shù)$stmt?->bind_param(?"s"?,?$state?);// 執(zhí)行$stmt?->execute();// 綁定結(jié)果$stmt?->bind_result(?$username?);// 移動(dòng)游標(biāo)$stmt?->fetch();printf(?"%s is from %s\n"?,?$username?,?$state?);$stmt?->close(); }正常的情況下,當(dāng)你在當(dāng)你在你的腳本中執(zhí)行一個(gè)SQL語(yǔ)句的時(shí)候,你的程序會(huì)停在那里直到?jīng)]這個(gè)SQL語(yǔ)句返回,然后你的程序再往下繼續(xù)執(zhí)行。你可 以使用無(wú)緩沖查詢來(lái)改變這個(gè)行為。13. 無(wú)緩沖的查詢
關(guān)于這個(gè)事情,在PHP的文檔中有一個(gè)非常不錯(cuò)的說(shuō)明:?mysql_unbuffered_query()?函數(shù):
“mysql_unbuffered_query() sends the SQL query query to MySQL without automatically fetching and buffering the result rows as mysql_query() does. This saves a considerable amount of memory with SQL queries that produce large result sets, and you can start working on the result set immediately after the first row has been retrieved as you don’t have to wait until the complete SQL query has been performed.”
上面那句話翻譯過(guò)來(lái)是說(shuō),mysql_unbuffered_query() 發(fā)送一個(gè)SQL語(yǔ)句到MySQL而并不像mysql_query()一樣去自動(dòng)fethch和緩存結(jié)果。這會(huì)相當(dāng)節(jié)約很多可觀的內(nèi)存,尤其是那些會(huì)產(chǎn)生大 量結(jié)果的查詢語(yǔ)句,并且,你不需要等到所有的結(jié)果都返回,只需要第一行數(shù)據(jù)返回的時(shí)候,你就可以開(kāi)始馬上開(kāi)始工作于查詢結(jié)果了。
然而,這會(huì)有一些限制。因?yàn)槟阋窗阉行卸甲x走,或是你要在進(jìn)行下一次的查詢前調(diào)用?mysql_free_result()清除結(jié)果。而且,?mysql_num_rows()?或?mysql_data_seek()?將無(wú)法使用。所以,是否使用無(wú)緩沖的查詢你需要仔細(xì)考慮。
14. 把IP地址存成 UNSIGNED INT
很多程序員都會(huì)創(chuàng)建一個(gè) VARCHAR(15) 字段來(lái)存放字符串形式的IP而不是整形的IP。如果你用整形來(lái)存放,只需要4個(gè)字節(jié),并且你可以有定長(zhǎng)的字段。而且,這會(huì)為你帶來(lái)查詢上的優(yōu)勢(shì),尤其是當(dāng) 你需要使用這樣的WHERE條件:IP between ip1 and ip2。
我們必需要使用UNSIGNED INT,因?yàn)?IP地址會(huì)使用整個(gè)32位的無(wú)符號(hào)整形。
而你的查詢,你可以使用?INET_ATON()?來(lái)把一個(gè)字符串IP轉(zhuǎn)成一個(gè)整形,并使用?INET_NTOA()?把一個(gè)整形轉(zhuǎn)成一個(gè)字符串IP。在PHP中,也有這樣的函數(shù)?ip2long()?和?long2ip()?。
$r?=?"UPDATE users SET ip = INET_ATON('{$_SERVER['REMOTE_ADDR']}') WHERE user_id = $user_id"?;15. 固定長(zhǎng)度的表會(huì)更快
如果表中的所有字段都是“固定長(zhǎng)度”的,整個(gè)表會(huì)被認(rèn)為是?“static” 或 “fixed-length”?。 例如,表中沒(méi)有如下類型的字段: VARCHAR,TEXT,BLOB。只要你包括了其中一個(gè)這些字段,那么這個(gè)表就不是“固定長(zhǎng)度靜態(tài)表”了,這樣,MySQL 引擎會(huì)用另一種方法來(lái)處理。
固定長(zhǎng)度的表會(huì)提高性能,因?yàn)镸ySQL搜尋得會(huì)更快一些,因?yàn)檫@些固定的長(zhǎng)度是很容易計(jì)算下一個(gè)數(shù)據(jù)的偏移量的,所以讀取的自然也會(huì)很快。而如果 字段不是定長(zhǎng)的,那么,每一次要找下一條的話,需要程序找到主鍵。
并且,固定長(zhǎng)度的表也更容易被緩存和重建。不過(guò),唯一的副作用是,固定長(zhǎng)度的字段會(huì)浪費(fèi)一些空間,因?yàn)槎ㄩL(zhǎng)的字段無(wú)論你用不用,他都是要分配那么多 的空間。
使用“垂直分割”技術(shù)(見(jiàn)下一條),你可以分割你的表成為兩個(gè)一個(gè)是定長(zhǎng)的,一個(gè)則是不定長(zhǎng)的。
16. 垂直分割
“垂直分割”是一種把數(shù)據(jù)庫(kù)中的表按列變成幾張表的方法,這樣可以降低表的復(fù)雜度和字段的數(shù)目,從而達(dá)到優(yōu)化的目的。(以前,在銀行做過(guò)項(xiàng)目,見(jiàn)過(guò) 一張表有100多個(gè)字段,很恐怖)
示例一?:在Users表中有一個(gè)字段是家庭地址,這個(gè)字段是可選字段,相比起,而且你在數(shù)據(jù)庫(kù)操作的時(shí)候除了個(gè) 人信息外,你并不需要經(jīng)常讀取或是改寫(xiě)這個(gè)字段。那么,為什么不把他放到另外一張表中呢? 這樣會(huì)讓你的表有更好的性能,大家想想是不是,大量的時(shí)候,我對(duì)于用戶表來(lái)說(shuō),只有用戶ID,用戶名,口令,用戶角色等會(huì)被經(jīng)常使用。小一點(diǎn)的表總是會(huì)有 好的性能。
示例二?: 你有一個(gè)叫 “l(fā)ast_login” 的字段,它會(huì)在每次用戶登錄時(shí)被更新。但是,每次更新時(shí)會(huì)導(dǎo)致該表的查詢緩存被清空。所以,你可以把這個(gè)字段放到另一個(gè)表中,這樣就不會(huì)影響你對(duì)用戶 ID,用戶名,用戶角色的不停地讀取了,因?yàn)椴樵兙彺鏁?huì)幫你增加很多性能。
另外,你需要注意的是,這些被分出去的字段所形成的表,你不會(huì)經(jīng)常性地去Join他們,不然的話,這樣的性能會(huì)比不分割時(shí)還要差,而且,會(huì)是極數(shù)級(jí) 的下降。
17. 拆分大的 DELETE 或 INSERT 語(yǔ)句
如果你需要在一個(gè)在線的網(wǎng)站上去執(zhí)行一個(gè)大的 DELETE 或 INSERT 查詢,你需要非常小心,要避免你的操作讓你的整個(gè)網(wǎng)站停止相應(yīng)。因?yàn)檫@兩個(gè)操作是會(huì)鎖表的,表一鎖住了,別的操作都進(jìn)不來(lái)了。
Apache 會(huì)有很多的子進(jìn)程或線程。所以,其工作起來(lái)相當(dāng)有效率,而我們的服務(wù)器也不希望有太多的子進(jìn)程,線程和數(shù)據(jù)庫(kù)鏈接,這是極大的占服務(wù)器資源的事情,尤其是 內(nèi)存。
如果你把你的表鎖上一段時(shí)間,比如30秒鐘,那么對(duì)于一個(gè)有很高訪問(wèn)量的站點(diǎn)來(lái)說(shuō),這30秒所積累的訪問(wèn)進(jìn)程/線程,數(shù)據(jù)庫(kù)鏈接,打開(kāi)的文件數(shù),可 能不僅僅會(huì)讓你泊WEB服務(wù)Crash,還可能會(huì)讓你的整臺(tái)服務(wù)器馬上掛了。
所以,如果你有一個(gè)大的處理,你定你一定把其拆分,使用 LIMIT 條件是一個(gè)好的方法。下面是一個(gè)示例:
while?(1) {//每次只做1000條mysql_query(?"DELETE FROM logs WHERE log_date <= '2009-11-01' LIMIT 1000"?);if?(mysql_affected_rows() == 0) {// 沒(méi)得可刪了,退出!break?;}// 每次都要休息一會(huì)兒usleep(50000); }18. 越小的列會(huì)越快
對(duì)于大多數(shù)的數(shù)據(jù)庫(kù)引擎來(lái)說(shuō),硬盤(pán)操作可能是最重大的瓶頸。所以,把你的數(shù)據(jù)變得緊湊會(huì)對(duì)這種情況非常有幫助,因?yàn)檫@減少了對(duì)硬盤(pán)的訪問(wèn)。
參看 MySQL 的文檔?Storage Requirements?查看所有的數(shù)據(jù)類型。
如果一個(gè)表只會(huì)有幾列罷了(比如說(shuō)字典表,配置表),那么,我們就沒(méi)有理由使用 INT 來(lái)做主鍵,使用 MEDIUMINT, SMALLINT 或是更小的 TINYINT 會(huì)更經(jīng)濟(jì)一些。如果你不需要記錄時(shí)間,使用 DATE 要比 DATETIME 好得多。
當(dāng)然,你也需要留夠足夠的擴(kuò)展空間,不然,你日后來(lái)干這個(gè)事,你會(huì)死的很難看,參看Slashdot 的例子?(2009年11月06日),一個(gè)簡(jiǎn)單的ALTER TABLE語(yǔ)句花了3個(gè)多小時(shí),因?yàn)槔锩嬗幸磺Я偃f(wàn)條數(shù)據(jù)。
19. 選擇正確的存儲(chǔ)引擎
在 MySQL 中有兩個(gè)存儲(chǔ)引擎 MyISAM 和 InnoDB,每個(gè)引擎都有利有弊。以前文章《MySQL: InnoDB 還是 MyISAM??》討論和這個(gè)事情。
MyISAM 適合于一些需要大量查詢的應(yīng)用,但其對(duì)于有大量寫(xiě)操作并不是很好。甚至你只是需要update一個(gè)字段,整個(gè)表都會(huì)被鎖起來(lái),而別的進(jìn)程,就算是讀進(jìn)程都 無(wú)法操作直到讀操作完成。另外,MyISAM 對(duì)于 SELECT COUNT(*) 這類的計(jì)算是超快無(wú)比的。
InnoDB 的趨勢(shì)會(huì)是一個(gè)非常復(fù)雜的存儲(chǔ)引擎,對(duì)于一些小的應(yīng)用,它會(huì)比 MyISAM 還慢。他是它支持“行鎖” ,于是在寫(xiě)操作比較多的時(shí)候,會(huì)更優(yōu)秀。并且,他還支持更多的高級(jí)應(yīng)用,比如:事務(wù)。
下面是MySQL的手冊(cè)
- target=”_blank”MyISAM Storage Engine
- InnoDB Storage Engine
20. 使用一個(gè)對(duì)象關(guān)系映射器(Object Relational Mapper)
使用 ORM (Object Relational Mapper),你能夠獲得可靠的性能增漲。一個(gè)ORM可以做的所有事情,也能被手動(dòng)的編寫(xiě)出來(lái)。但是,這需要一個(gè)高級(jí)專家。
ORM 的最重要的是“Lazy Loading”,也就是說(shuō),只有在需要的去取值的時(shí)候才會(huì)去真正的去做。但你也需要小心這種機(jī)制的副作用,因?yàn)檫@很有可能會(huì)因?yàn)橐?chuàng)建很多很多小的查 詢反而會(huì)降低性能。
ORM 還可以把你的SQL語(yǔ)句打包成一個(gè)事務(wù),這會(huì)比單獨(dú)執(zhí)行他們快得多得多。
目前,個(gè)人最喜歡的PHP的ORM是:Doctrine?。
21. 小心“永久鏈接”
“永久鏈接”的目的是用來(lái)減少重新創(chuàng)建MySQL鏈接的次數(shù)。當(dāng)一個(gè)鏈接被創(chuàng)建了,它會(huì)永遠(yuǎn)處在連接的狀態(tài),就算是數(shù)據(jù)庫(kù)操作已經(jīng)結(jié)束了。而且,自 從我們的Apache開(kāi)始重用它的子進(jìn)程后——也就是說(shuō),下一次的HTTP請(qǐng)求會(huì)重用Apache的子進(jìn)程,并重用相同的 MySQL 鏈接。
-
PHP 手冊(cè):mysql_pconnect()
在理論上來(lái)說(shuō),這聽(tīng)起來(lái)非常的不錯(cuò)。但是從個(gè)人經(jīng)驗(yàn)(也是大多數(shù)人的)上來(lái)說(shuō),這個(gè)功能制造出來(lái)的麻煩事更多。因?yàn)?#xff0c;你只有有限的鏈接數(shù),內(nèi)存問(wèn) 題,文件句柄數(shù),等等。
而且,Apache 運(yùn)行在極端并行的環(huán)境中,會(huì)創(chuàng)建很多很多的了進(jìn)程。這就是為什么這種“永久鏈接”的機(jī)制工作地不好的原因。在你決定要使用“永久鏈接”之前,你需要好好地 考慮一下你的整個(gè)系統(tǒng)的架構(gòu)。
阿里巴巴JAVA編程規(guī)范之MySQL規(guī)范
(一)?建表規(guī)約
1.?【強(qiáng)制】表達(dá)是與否概念的字段,必須使用is_xxx的方式命名,數(shù)據(jù)類型是unsigned tinyint。(?1表示是,0表示否),此規(guī)則同樣適用于odps建表。
說(shuō)明:任何字段如果為非負(fù)數(shù),必須是unsigned。
解讀:從優(yōu)化角度來(lái)講,應(yīng)該按字段的用途來(lái)定義合適的類型。表達(dá)是與否,用長(zhǎng)度為1個(gè)字節(jié)的tinyint足以
2.?【強(qiáng)制】表名、字段名必須使用小寫(xiě)字母或數(shù)字;禁止出現(xiàn)數(shù)字開(kāi)頭,禁止兩個(gè)下劃線中間只出現(xiàn)數(shù)字。數(shù)據(jù)庫(kù)字段名的修改代價(jià)很大,因?yàn)闊o(wú)法進(jìn)行預(yù)發(fā)布,所以字段名稱需要慎重考慮。
正例:getter_admin,task_config,level3_name
反例:GetterAdmin,taskConfig,level_3_name
說(shuō)明:MySQL 在 Windows 下不區(qū)分大小寫(xiě),但在 Linux 下默認(rèn)是區(qū)分大小寫(xiě)。因此,數(shù)據(jù)庫(kù)名、 表名、字段名,都不允許出現(xiàn)任何大寫(xiě)字母,避免節(jié)外生枝。
解讀:Win環(huán)境下開(kāi)發(fā),代碼中用的表名是小寫(xiě),本地?cái)?shù)據(jù)庫(kù)用的是大寫(xiě),那么在win環(huán)境下沒(méi)有問(wèn)題,但發(fā)布到linux環(huán)境會(huì)有問(wèn)題。Linux下MySQL安裝完后默認(rèn):區(qū)分表名的大小寫(xiě),不區(qū)分列名的大小寫(xiě)
3.?【強(qiáng)制】表名不使用復(fù)數(shù)名詞。
說(shuō)明:表名應(yīng)該僅僅表示表里面的實(shí)體內(nèi)容,不應(yīng)該表示實(shí)體數(shù)量,對(duì)應(yīng)于DO類名也是單數(shù)形式,符合表達(dá)習(xí)慣。
4.?【強(qiáng)制】禁用保留字,如desc、range、match、delayed等,參考官方保留字。
5.?【強(qiáng)制】唯一索引名為uk_字段名;普通索引名則為idx_字段名。
說(shuō)明:uk_即?unique key;idx_即index的簡(jiǎn)稱。
6.?【強(qiáng)制】小數(shù)類型為decimal,禁止使用float和double。
說(shuō)明:float和double在存儲(chǔ)的時(shí)候,存在精度損失的問(wèn)題,很可能在值的比較時(shí),得到不正確的結(jié)果。如果存儲(chǔ)的數(shù)據(jù)范圍超過(guò)decimal的范圍,建議將數(shù)據(jù)拆成整數(shù)和小數(shù)分開(kāi)存儲(chǔ)。
解讀:float和double都是浮點(diǎn)型,而decimal是定點(diǎn)型。MySQL 浮點(diǎn)型和定點(diǎn)型可以用類型名稱后加(M,D)來(lái)表示,M表示該值的總共長(zhǎng)度,D表示小數(shù)點(diǎn)后面的長(zhǎng)度。FLOAT和DOUBLE在不指定精度時(shí),默認(rèn)會(huì)按照實(shí)際的精度來(lái)顯示,而DECIMAL在不指定精度時(shí),默認(rèn)整數(shù)為10,小數(shù)為0。所以建議在定義表時(shí),定義(M,D)。float和double在設(shè)置超過(guò)定義長(zhǎng)度的數(shù)值時(shí),會(huì)自動(dòng)四舍五入,decimal會(huì)截?cái)?#xff0c;并給出一條警告。精度損失問(wèn)題:float和double類型的列,在做sum計(jì)算時(shí),會(huì)丟失精度,而decimal會(huì)精確計(jì)算。
7.?【強(qiáng)制】如果存儲(chǔ)的字符串長(zhǎng)度幾乎相等,使用CHAR定長(zhǎng)字符串類型。
解讀:從優(yōu)化角度來(lái)說(shuō),如果一個(gè)表的所有字段都是定長(zhǎng)的,那么每一條數(shù)據(jù)也就是定長(zhǎng)的,數(shù)據(jù)庫(kù)就可以直接計(jì)算出下一條數(shù)據(jù)的偏移量,查詢速度會(huì)更快。
8.?【強(qiáng)制】varchar是可變長(zhǎng)字符串,不預(yù)先分配存儲(chǔ)空間,長(zhǎng)度不要超過(guò)5000,如果存儲(chǔ)長(zhǎng)度大于此值,定義字段類型為T(mén)EXT,獨(dú)立出來(lái)一張表,用主鍵來(lái)對(duì)應(yīng),避免影響其它字段索引效率。
解讀:MySQL5.0以上版本,varchar最大可以存儲(chǔ)65535字節(jié)數(shù)據(jù)(內(nèi)容開(kāi)頭用1-2個(gè)字節(jié)存儲(chǔ)長(zhǎng)度信息,超過(guò)255時(shí)用兩個(gè)字節(jié),所以最大65535)。我們通常編碼設(shè)置為U8,每個(gè)字符最多占3個(gè)字節(jié),那么最大長(zhǎng)度不能超過(guò)21845。若定義的時(shí)候超過(guò)上述限制,則varchar字段會(huì)被強(qiáng)行轉(zhuǎn)為text類型,并產(chǎn)生warning。此外,受MYSQL行長(zhǎng)度限制影響,MySQL要求一個(gè)行的定義長(zhǎng)度不能超過(guò)65535。若定義的表長(zhǎng)度超過(guò)這個(gè)值,則提示ERROR 1118 (42000): Row size too large。數(shù)據(jù)庫(kù)中定義的varchar(20)指的是20個(gè)字符。關(guān)于5000的建議:由于通常定義的U8每個(gè)字符占3個(gè)字節(jié),那么5000字符需要15000個(gè)字節(jié),考慮行最大長(zhǎng)度限制和別的列,以及查詢性能,推薦5000。
9.?【強(qiáng)制】表必備三字段:id, gmt_create, gmt_modified。
說(shuō)明:其中id必為主鍵,類型為unsigned bigint、單表時(shí)自增、步長(zhǎng)為1;分表時(shí)改為從TDDL?Sequence取值,確保分表之間的全局唯一。gmt_create,gmt_modified的類型均為date_time類型。
10.【推薦】表的命名最好是加上“業(yè)務(wù)名稱_表的作用”,避免上云梯后,再與其它業(yè)務(wù)表關(guān)聯(lián)時(shí)有混淆。
正例:tiger_task / tiger_reader / mpp_config
11.【推薦】庫(kù)名與應(yīng)用名稱盡量一致。
12.【推薦】如果修改字段含義或?qū)ψ侄伪硎镜臓顟B(tài)追加時(shí),需要及時(shí)更新字段注釋。
13.【推薦】字段允許適當(dāng)冗余,以提高性能,但是必須考慮數(shù)據(jù)同步的情況。冗余字段應(yīng)遵循:
- 不是頻繁修改的字段。
- 不是varchar超長(zhǎng)字段,更不能是text字段。
正例:各業(yè)務(wù)線經(jīng)常冗余存儲(chǔ)商品名稱,避免查詢時(shí)需要調(diào)用IC服務(wù)獲取。商品類目名稱使用頻率高,字段長(zhǎng)度短,名稱基本一成不變,可在相關(guān)聯(lián)的表中冗余存儲(chǔ)類目名稱,避免關(guān)聯(lián)查詢。
14.【推薦】單表行數(shù)超過(guò)500萬(wàn)行或者單表容量超過(guò)2GB,才推薦進(jìn)行分庫(kù)分表。
說(shuō)明:如果預(yù)計(jì)三年后的數(shù)據(jù)量根本達(dá)不到這個(gè)級(jí)別,請(qǐng)不要在創(chuàng)建表時(shí)就分庫(kù)分表。
反例:某業(yè)務(wù)三年總數(shù)據(jù)量才2萬(wàn)行,卻分成1024張表,問(wèn):你為什么這么設(shè)計(jì)?答:分1024張表,不是標(biāo)配嗎?
15.【參考】合適的字符存儲(chǔ)長(zhǎng)度,不但節(jié)約數(shù)據(jù)庫(kù)表空間、節(jié)約索引存儲(chǔ),更重要的是提升檢索速度。
正例:人的年齡用unsigned tinyint(表示范圍0-255,人的壽命不會(huì)超過(guò)255歲);海龜就必須是small int,但如果是太陽(yáng)的年齡,就必須是int;如果是所有恒星的年齡都加起來(lái),那么就必須使用bigint。
(二)?索引規(guī)約
1.?【強(qiáng)制】業(yè)務(wù)上具有唯一特性的字段,即使是組合字段,也必須建成唯一索引。
說(shuō)明:不要以為唯一索引影響了insert速度,這個(gè)速度損耗可以忽略,但提高查找速度是明顯的;另外,即使在應(yīng)用層做了非常完善的校驗(yàn)和控制,只要沒(méi)有唯一索引,根據(jù)墨菲定律,必然有臟數(shù)據(jù)產(chǎn)生。
2.?【強(qiáng)制】超過(guò)三個(gè)表禁止join。需要join的字段,數(shù)據(jù)類型保持絕對(duì)一致;多表關(guān)聯(lián)查詢時(shí),保證被關(guān)聯(lián)的字段需要有索引。
說(shuō)明:即使雙表join也要注意表索引、SQL性能。
3.?【強(qiáng)制】在varchar字段上建立索引時(shí),必須指定索引長(zhǎng)度,沒(méi)必要對(duì)全字段建立索引,根據(jù)實(shí)際文本區(qū)分度決定索引長(zhǎng)度。
說(shuō)明:索引的長(zhǎng)度與區(qū)分度是一對(duì)矛盾體,一般對(duì)字符串類型數(shù)據(jù),長(zhǎng)度為20的索引,區(qū)分度會(huì)高達(dá)90%以上,可以使用count(distinct left(列名,索引長(zhǎng)度))/count(*)的區(qū)分度來(lái)確定。
解讀:區(qū)分度是指不重復(fù)的索引值和數(shù)據(jù)表的記錄總數(shù)的比值,范圍(0,1],值越高則查詢效率越高。對(duì)于blob,text,varchar的列必須使用前綴索引,MySQL不允許索引這些列的完整長(zhǎng)度。最好選擇足夠長(zhǎng)的前綴保證較高的區(qū)分度,也不能太長(zhǎng)(節(jié)省空間)。
4.?【強(qiáng)制】頁(yè)面搜索嚴(yán)禁左模糊或者全模糊,如果需要請(qǐng)走搜索引擎來(lái)解決。
說(shuō)明:索引文件具有B-Tree的最左前綴匹配特性,如果左邊的值未確定,那么無(wú)法使用此索引。
5.?【推薦】如果有order?by的場(chǎng)景,請(qǐng)注意利用索引的有序性。order?by?最后的字段是組合索引的一部分,并且放在索引組合順序的最后,避免出現(xiàn)file_sort的情況,影響查詢性能。
正例:where a=? and b=? order by c;?索引:a_b_c
反例:索引中有范圍查找,那么索引有序性無(wú)法利用,如:WHERE?a>10?ORDER?BY?b;?索引a_b無(wú)法排序。
解讀:只有在order by 數(shù)據(jù)列的時(shí)候才可能會(huì)出現(xiàn)using filesort,而且如果你不對(duì)進(jìn)行order by的這一列設(shè)置索引的話,無(wú)論列值是否有相同的都會(huì)出現(xiàn)using filesort。因此,只要用到order by 的這一列都應(yīng)該為其建立一個(gè)索引。 只有當(dāng)索引的列順序和order by子句的順序完全一致,并且所有列的排序方向都一樣時(shí),才能用索引排序。如果查詢需要關(guān)聯(lián)多張表,只有當(dāng)order by子句引用的字段全部為第一個(gè)表時(shí),才能使用索引排序。
6.?【推薦】利用覆蓋索引來(lái)進(jìn)行查詢操作,來(lái)避免回表操作。
說(shuō)明:如果一本書(shū)需要知道第11章是什么標(biāo)題,會(huì)翻開(kāi)第11章對(duì)應(yīng)的那一頁(yè)嗎?目錄瀏覽一下就好,這個(gè)目錄就是起到覆蓋索引的作用。
正例:IDB能夠建立索引的種類:主鍵索引、唯一索引、普通索引,而覆蓋索引是一種查詢的一種效果,用explain的結(jié)果,extra列會(huì)出現(xiàn):using index.
解讀:如果一個(gè)索引包含所有需要查詢的字段的值,稱之為“覆蓋索引”。由于覆蓋索引必須要存儲(chǔ)索引列的值,哈希索引、空間索引和全文索引都不存儲(chǔ)列的值,MySQL只有B-Tree索引可以做覆蓋索引。如:對(duì)id,name,title三個(gè)字段建立索引,在索引中會(huì)存儲(chǔ)這三個(gè)列的值,如果查詢:select id,name,title from table where id < 10; 通過(guò)explain會(huì)看到extra為using index。如果查詢select * from table where id < 10;就不會(huì)使用覆蓋索引,因?yàn)樗饕袥](méi)有包含所有的列值。
7.?【推薦】利用延遲關(guān)聯(lián)或者子查詢優(yōu)化超多分頁(yè)場(chǎng)景。
說(shuō)明:MySQL并不是跳過(guò)offset行,而是取offset+N行,然后返回放棄前offset行,返回N行,那當(dāng)offset特別大的時(shí)候,效率就非常的低下,要么控制返回的總頁(yè)數(shù),要么對(duì)超過(guò)特定閾值的頁(yè)數(shù)進(jìn)行SQL改寫(xiě)。
解讀:案例
select count(*) from user_game_info; // 共有956176條數(shù)據(jù)
select * from user_game_info a limit 900000, 20; // 此查詢耗時(shí)0.547S
?
select t1.* from user_game_info t1, (select id from user_game_info limit 900000, 20) t2 where t1.id = t2.id; // 優(yōu)化后耗時(shí)0.178S
8.?【推薦】SQL性能優(yōu)化的目標(biāo):至少要達(dá)到?range級(jí)別,要求是ref級(jí)別,如果可以是consts最好。
? 說(shuō)明:
- consts單表中最多只有一個(gè)匹配行(主鍵或者唯一索引),在優(yōu)化階段即可讀取到數(shù)據(jù)。
- ref指的是使用普通的索引。(normal index)
- range對(duì)索引進(jìn)范圍檢索。
反例:explain表的結(jié)果,type=index,索引物理文件全掃描速度非常慢,這個(gè)index級(jí)別比較range還低,與全表掃描是小巫見(jiàn)大巫。
9.?【推薦】建組合索引的時(shí)候,區(qū)分度最高的在最左邊。
正例:如果where a=? and b=??,a列的幾乎接近于唯一值,那么只需要單建idx_a索引即可。
說(shuō)明:存在非等號(hào)和等號(hào)混合判斷條件時(shí),在建索引時(shí),請(qǐng)把等號(hào)條件的列前置。如:wherea>? and b=??那么即使a的區(qū)分度更高,也必須把b放在索引的最前列。
10.【推薦】 防止因字段類型不同造成的隱式轉(zhuǎn)換,導(dǎo)致索引失效。
解讀:例如,給上文提到的tb_user_account表的username(varchar)字段加索引,由于字段是varchar類型的,所以上圖中的查詢類型匹配,命中索引,下圖是用int類型匹配的,無(wú)法命中索引。
以字符串形式查找,命中索引
?
因隱式轉(zhuǎn)換,未命中索引
?
隱式轉(zhuǎn)換規(guī)則:
- 兩個(gè)參數(shù)至少有一個(gè)是 NULL 時(shí),比較的結(jié)果也是 NULL,例外是使用 <=> 對(duì)兩個(gè) NULL 做比較時(shí)會(huì)返回 1,這兩種情況都不需要做類型轉(zhuǎn)換
- 兩個(gè)參數(shù)都是字符串,會(huì)按照字符串來(lái)比較,不做類型轉(zhuǎn)換
- 兩個(gè)參數(shù)都是整數(shù),按照整數(shù)來(lái)比較,不做類型轉(zhuǎn)換
- 十六進(jìn)制的值和非數(shù)字做比較時(shí),會(huì)被當(dāng)做二進(jìn)制串
- 有一個(gè)參數(shù)是 TIMESTAMP 或 DATETIME,并且另外一個(gè)參數(shù)是常量,常量會(huì)被轉(zhuǎn)換為 timestamp
- 有一個(gè)參數(shù)是 decimal 類型,如果另外一個(gè)參數(shù)是 decimal 或者整數(shù),會(huì)將整數(shù)轉(zhuǎn)換為 decimal 后進(jìn)行比較,如果另外一個(gè)參數(shù)是浮點(diǎn)數(shù),則會(huì)把 decimal 轉(zhuǎn)換為浮點(diǎn)數(shù)進(jìn)行比較
- 所有其他情況下,兩個(gè)參數(shù)都會(huì)被轉(zhuǎn)換為浮點(diǎn)數(shù)再進(jìn)行比較
11.【參考】創(chuàng)建索引時(shí)避免有如下極端誤解:
- 誤認(rèn)為一個(gè)查詢就需要建一個(gè)索引。
- 誤認(rèn)為索引會(huì)消耗空間、嚴(yán)重拖慢更新和新增速度。
- 誤認(rèn)為唯一索引一律需要在應(yīng)用層通過(guò)“先查后插”方式解決。
(三)?SQL規(guī)約
1.?【強(qiáng)制】不要使用count(列名)或count(常量)來(lái)替代count(*),count(*)就是SQL92定義的標(biāo)準(zhǔn)統(tǒng)計(jì)行數(shù)的語(yǔ)法,跟數(shù)據(jù)庫(kù)無(wú)關(guān),跟NULL和非NULL無(wú)關(guān)。
說(shuō)明:count(*)會(huì)統(tǒng)計(jì)值為NULL的行,而count(列名)不會(huì)統(tǒng)計(jì)此列為NULL值的行。
2.?【強(qiáng)制】count(distinct col)?計(jì)算該列除NULL之外的不重復(fù)數(shù)量。注意?count(distinct col1, col2)?如果其中一列全為NULL,那么即使另一列有不同的值,也返回為0。
3.?【強(qiáng)制】當(dāng)某一列的值全是NULL時(shí),count(col)的返回結(jié)果為0,但sum(col)的返回結(jié)果為 NULL,因此使用sum()時(shí)需注意NPE問(wèn)題。
正例:可以使用如下方式來(lái)避免sum的NPE問(wèn)題:SELECTIF(ISNULL(SUM(g)),0,SUM(g))FROM table;
4.?【強(qiáng)制】使用ISNULL()來(lái)判斷是否為NULL值。注意:NULL與任何值的直接比較都為NULL。
? 說(shuō)明:
- NULL<>NULL的返回結(jié)果是NULL,不是false。
- NULL=NULL的返回結(jié)果是NULL,不是true。
- NULL<>1的返回結(jié)果是NULL,而不是true。
5.?【強(qiáng)制】在代碼中寫(xiě)分頁(yè)查詢邏輯時(shí),若count為0應(yīng)直接返回,避免執(zhí)行后面的分頁(yè)語(yǔ)句。
6.?【強(qiáng)制】不得使用外鍵與級(jí)聯(lián),一切外鍵概念必須在應(yīng)用層解決。
說(shuō)明:(概念解釋)學(xué)生表中的student_id是主鍵,那么成績(jī)表中的student_id則為外鍵。如果更新學(xué)生表中的student_id,同時(shí)觸發(fā)成績(jī)表中的student_id更新,則為級(jí)聯(lián)更新。外鍵與級(jí)聯(lián)更新適用于單機(jī)低并發(fā),不適合分布式、高并發(fā)集群;級(jí)聯(lián)更新是強(qiáng)阻塞,存在數(shù)據(jù)庫(kù)更新風(fēng)暴的風(fēng)險(xiǎn);外鍵影響數(shù)據(jù)庫(kù)的插入速度。
7.?【強(qiáng)制】禁止使用存儲(chǔ)過(guò)程,存儲(chǔ)過(guò)程難以調(diào)試和擴(kuò)展,更沒(méi)有移植性。
8.?【強(qiáng)制】IDB數(shù)據(jù)訂正時(shí),刪除和修改記錄時(shí),要先select,避免出現(xiàn)誤刪除,確認(rèn)無(wú)誤才能提交執(zhí)行。
9.?【推薦】in操作能避免則避免,若實(shí)在避免不了,需要仔細(xì)評(píng)估in后邊的集合元素?cái)?shù)量,控制在1000個(gè)之內(nèi)。
10.【參考】因阿里巴巴全球化需要,所有的字符存儲(chǔ)與表示,均以u(píng)tf-8編碼,那么字符計(jì)數(shù)方法注意:
說(shuō)明:
SELECT LENGTH("阿里巴巴");?返回為12
SELECT CHARACTER_LENGTH("阿里巴巴");?返回為4
如果要使用表情,那么使用utfmb4來(lái)進(jìn)行存儲(chǔ),注意它與utf-8編碼。
11.【參考】TRUNCATE?TABLE?比?DELETE速度快,且使用的系統(tǒng)和事務(wù)日志資源少,但TRUNCATE無(wú)事務(wù)且不觸發(fā)trigger,有可能造成事故,故不建議在開(kāi)發(fā)代碼中使用此語(yǔ)句。
說(shuō)明:TRUNCATE TABLE?在功能上與不帶?WHERE子句的?DELETE語(yǔ)句相同。
(四)?ORM規(guī)約
1.?【強(qiáng)制】在表查詢中,一律不要使用?*作為查詢的字段列表,需要哪些字段必須明確寫(xiě)明。
說(shuō)明:1)增加查詢分析器解析成本。2)增減字段容易與resultMap配置不一致。3)無(wú)用字段增加網(wǎng)絡(luò)消耗,尤其是 text 類型的字段。
2.?【強(qiáng)制】POJO類的boolean屬性不能加is,而數(shù)據(jù)庫(kù)字段必須加is_,要求在resultMap中進(jìn)行字段與屬性之間的映射。
說(shuō)明:參見(jiàn)定義POJO類以及數(shù)據(jù)庫(kù)字段定義規(guī)定,在sql.xml增加映射,是必須的。
3.?【強(qiáng)制】不要用resultClass當(dāng)返回參數(shù),即使所有類屬性名與數(shù)據(jù)庫(kù)字段一一對(duì)應(yīng),也需要定義;反過(guò)來(lái),每一個(gè)表也必然有一個(gè)與之對(duì)應(yīng)。
說(shuō)明:配置映射關(guān)系,使字段與DO類解耦,方便維護(hù)。
4.?【強(qiáng)制】xml配置中參數(shù)注意使用:#{},#param#不要使用${}此種方式容易出現(xiàn)SQL注入。
解讀:#與$的區(qū)別:在預(yù)編譯中的處理是不一樣的。#{} 在預(yù)處理時(shí),會(huì)把參數(shù)部分用一個(gè)占位符 ? 代替,如:select * from user where name = ?;而 ${} 則只是簡(jiǎn)單的字符串替換,在動(dòng)態(tài)解析階段,該 sql 語(yǔ)句會(huì)被解析成select * from user where name = 'zhangsan’;以上,#{} 的參數(shù)替換是發(fā)生在 DBMS 中,而 ${} 則發(fā)生在動(dòng)態(tài)解析過(guò)程中。
5.?【強(qiáng)制】iBATIS自帶的queryForList(String?statementName,int?start,int?size)不推薦使用。
說(shuō)明:其實(shí)現(xiàn)方式是在數(shù)據(jù)庫(kù)取到statementName對(duì)應(yīng)的SQL語(yǔ)句的所有記錄,再通過(guò)subList取start,size的子集合,線上因?yàn)檫@個(gè)原因曾經(jīng)出現(xiàn)過(guò)OOM。
正例:在sqlmap.xml中引入?#start#, #size#
Map<String, Object> map = new HashMap<String,Object>();
map.put("start", start);
map.put("size", size);
6.?【強(qiáng)制】不允許直接拿HashMap與HashTable作為查詢結(jié)果集的輸出。
反例:某同學(xué)為避免寫(xiě)一個(gè)<resultMap>,直接使用HashTable來(lái)接收數(shù)據(jù)庫(kù)返回結(jié)果,結(jié)果出現(xiàn)日常是把bigint轉(zhuǎn)成Long值,而線上由于數(shù)據(jù)庫(kù)版本不一樣,解析成BigInteger,導(dǎo)致線上問(wèn)題。
7.?【強(qiáng)制】更新數(shù)據(jù)表記錄時(shí),必須同時(shí)更新記錄對(duì)應(yīng)的gmt_modified字段值為當(dāng)前時(shí)間。
8.?【推薦】不要寫(xiě)一個(gè)大而全的數(shù)據(jù)更新接口,傳入為POJO類,不管是不是自己的目標(biāo)更新字段,都進(jìn)行update?table?set?c1=value1,c2=value2,c3=value3;?這是不對(duì)的。執(zhí)行SQL時(shí),盡量不要更新無(wú)改動(dòng)的字段,一是易出錯(cuò);二是效率低;三是binlog增加存儲(chǔ)。
9.?【參考】@Transactional事務(wù)不要濫用。事務(wù)會(huì)影響數(shù)據(jù)庫(kù)的QPS,另外使用事務(wù)的地方需要考慮各方面的回滾方案,包括緩存回滾、搜索引擎回滾、消息補(bǔ)償、統(tǒng)計(jì)修正等。
10.【參考】<isEqual>中的compareValue是與屬性值對(duì)比的常量,一般是數(shù)字,表示相等時(shí)帶上此條件;<isNotEmpty>表示不為空且不為null時(shí)執(zhí)行;<isNotNull>表示不為null值時(shí)執(zhí)行。
阿里十年java老兵總結(jié)的50條mysql使用軍規(guī)
一.數(shù)據(jù)庫(kù)配置
????????1. innodb_flush_log_at_trx_commit,這個(gè)對(duì)支付業(yè)務(wù)來(lái)說(shuō)是關(guān)鍵性的設(shè)置之一,可選的參數(shù)值有0,1,2, 支付需要設(shè)置成1.
????????2. 對(duì)交易以及記賬部分來(lái)說(shuō),必須是innode引擎,以支持事務(wù)
????????3. 事務(wù)隔離級(jí)別,權(quán)衡安全性和效率,使用可重復(fù)讀
????????4. innodb_lock_wait_timeout,這個(gè)是鎖超時(shí)時(shí)間,不建議太大,怕引起雪崩
二.業(yè)務(wù)設(shè)計(jì)
????1. 不用物理刪除,即盡量避免用delete語(yǔ)句,drop命令等;通過(guò)軟刪除處理,即通過(guò)額外的字段標(biāo)
????2. 明記錄的刪除狀態(tài);雖然對(duì)寫(xiě)代碼來(lái)講有些麻煩,但實(shí)踐證明是非常值得的
????3. 在系統(tǒng)設(shè)計(jì)之初就要定好編碼規(guī)范,對(duì)存入的數(shù)據(jù)做相應(yīng)轉(zhuǎn)換并做好escape處理
????4. 除列表查詢外,盡量用主鍵操作;核心交易系統(tǒng)中盡量避免非主鍵操作
????5. 避免schema中1對(duì)多設(shè)計(jì),概念簡(jiǎn)單,編碼很難
????6. 就mysql來(lái)說(shuō),盡量使用其最簡(jiǎn)單的功能,不用其高級(jí)功能如觸發(fā)器,連表等
????7. 就mysql來(lái)說(shuō),盡量不讓其做計(jì)算功能,而是讓業(yè)務(wù)層來(lái)實(shí)現(xiàn)計(jì)算邏輯
????8. 當(dāng)要鎖多條記錄時(shí), 要考慮死鎖的可能以及預(yù)防的措施
????9. 按業(yè)務(wù)垂直劃分原則,盡量把不同的業(yè)務(wù)方不同的庫(kù)中
????10. 堅(jiān)持小事務(wù),一個(gè)大事務(wù)與將其拆分成的十個(gè)小事務(wù)相比,小事務(wù)對(duì)數(shù)據(jù)庫(kù)壓力更小;另外,事務(wù)
中做盡可能少的事情,神馬參數(shù)校驗(yàn)之類的事情能拉出去就拉出去
????11. 數(shù)據(jù)庫(kù)連接長(zhǎng)時(shí)間不用超時(shí)斷開(kāi)是常見(jiàn)的,應(yīng)用中需要考慮
????12. 對(duì)超大型系統(tǒng)來(lái)說(shuō),分布式事務(wù)是有價(jià)值的;但在大多下情況下,單機(jī)事務(wù)能很好的滿足需要
????13. 主從延遲總是會(huì)有的,有時(shí)候會(huì)很大,設(shè)計(jì)中要考慮
????14. 讀寫(xiě)賬號(hào)分開(kāi),讀賬號(hào)select權(quán)限即可,寫(xiě)賬號(hào)update,insert即可
????15. where條件key='value'的模式中,加上單引號(hào)總是對(duì)的,不加在某些情況下有很多令人意外的副
作用
????16. 盡量使用簡(jiǎn)單的數(shù)據(jù)類型,char系列,int系列以及date系列即可
????17. 事務(wù)的使用上,在線交易使用悲觀鎖
????18. 事務(wù)框架的選擇上,使用控制力度比較大的,直接TransactionManager,不推薦使用聲明式事務(wù)
????19. 采用InnoDB引擎,UTF8編碼
????20. 有狀態(tài)字段的記錄,狀態(tài)的取值不宜太多, 6 ~ 7個(gè)應(yīng)該是上限了, 最好不要超過(guò) 4個(gè)
????21. 每個(gè)表都應(yīng)該有自己的主鍵,且盡量讓表內(nèi)主鍵保持遞增
????22. 不使用自增主鍵
????23. 在線查詢的字段一定要建立覆蓋索引
????24. 分頁(yè)查找一定不能直接limit m,n,一定要做優(yōu)化
三. 應(yīng)用規(guī)范
????1. 當(dāng)進(jìn)行賬戶余額變化操作時(shí),總是校驗(yàn)賬戶是否被凍
????2. 對(duì)單據(jù)如Trade,Charge進(jìn)行處理,并發(fā)總是要考慮的,需要鎖記錄后進(jìn)行校驗(yàn);從數(shù)據(jù)庫(kù)查詢的
時(shí)候,請(qǐng)先起動(dòng)事務(wù),并用select...for update;防止并發(fā)帶來(lái)的問(wèn)題;從性能上將,鎖交易單本身不
會(huì)成為性能瓶頸
????3. 更新賬戶余額之前必須加鎖,即起事務(wù)+select...for update; 更新余額的語(yǔ)句建議是update table
set 余額=余額+/-發(fā)生額, 自然在代碼邏輯中做各種邊界值校驗(yàn),包括不溢出,不小于0等
????4.金額都統(tǒng)一單位,以分為單位合適;數(shù)據(jù)類型為有符號(hào)64位整形數(shù)合適
????5. 對(duì)金額計(jì)算時(shí),對(duì)溢出的預(yù)防總是需要的
????6.使用select時(shí),慎用*,盡量明確的枚舉出字段名
????7.與外部交互的地方,記錄下外部發(fā)生時(shí)間
????8. db命名采用"cashpay"前綴
????9. 表命名采用"t_"前綴
????10. 字段命名采用"F_"前綴
????11. 每個(gè)表都會(huì)有一個(gè)字段記錄上次更新時(shí)間
????12. 在一個(gè)session中的所有數(shù)據(jù)庫(kù)更新記錄,上次更新時(shí)間都是一致的
總結(jié)
以上是生活随笔為你收集整理的mysql 之 优化 (收集于网络)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: cad完全卸载工具
- 下一篇: linux cmake编译源码,linu