溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

MySQL生產(chǎn)庫開發(fā)規(guī)范

發(fā)布時間:2020-07-08 21:04:27 來源:網(wǎng)絡(luò) 閱讀:13095 作者:dbapower 欄目:數(shù)據(jù)庫

MySQL開發(fā)規(guī)范



文件狀態(tài):
[  ] 草稿
[√] 正式發(fā)布
[  ] 正在修改 文件標(biāo)識: 
 當(dāng)前版本: V1.0
 作    者: 賀磊
 完成日期: 2016-05-24


變更記錄
序號 修改日期 修改內(nèi)容 修改人 審核人 批準(zhǔn)人 批準(zhǔn)日期
1 2016-05-24 MySQL開發(fā)規(guī)范 賀磊   
 

 
MySQL開發(fā)規(guī)范
1. 簡介
持續(xù)借鑒、收集并整理一些開發(fā)規(guī)范和技巧,期望能更充分利用MySQL的特性,得到更好的性能。
規(guī)范是死的,人是活的。
現(xiàn)在定義的規(guī)范,是為以后推翻準(zhǔn)備的。
1.1 目的
提供給開發(fā)人員參考,方便寫成更有效率的開發(fā)。
1.2 范圍
文檔涉及的范圍:需要基于MySQL做應(yīng)用開發(fā)的人員。
1.3 定義、首字母縮寫詞和縮略語
暫無



2. 數(shù)據(jù)庫設(shè)計(jì)
目標(biāo)三個:功能實(shí)現(xiàn),可伸縮性,可用性。
關(guān)鍵點(diǎn):平衡業(yè)務(wù)技術(shù)各個方面,做好取舍。
80%的性能優(yōu)化來自架構(gòu)設(shè)計(jì)的優(yōu)化。
2.1 引擎及版本選擇
******引擎建議使用InnoDB
根據(jù)目前我們業(yè)務(wù)的特點(diǎn),建議使用MySQL5.6社區(qū)版。
2.2 架構(gòu)淺談
開發(fā)大牛都擅長,這里不多提,僅標(biāo)注一下。
2.2.1 讀寫分離
2.2.2 分庫分表



3. 表設(shè)計(jì)
先看一個范例:

create
table erp_example(
id int(10) unsigned NOT NULL AUTO_INCREMENT,/**每個表都要有主鍵id, int無符號自增,且與業(yè)務(wù)無關(guān)。**/
server_type TINYINT(3) UNSIGNED NOT NULL DEFAULT 0,/**如果存儲類型,例如1:北京,2:上海,3:廣州,若無負(fù)數(shù)則用unsigned可以提高范圍。若無需字母,不要用char(1)來存**/
name varchar(5) NOT NULL DEFAULT '',/**varchar,char根據(jù)實(shí)際需要賦值,越小越好,且能用NOT NULL DEFAULT ''就用,NULL不但占據(jù)空間,且會降低索引效率,用''空字符串代替。**/
PRIMARY
KEY(id),
KEY `idx_name` (`name`)/**索引用idx_列名來標(biāo)記,唯一索引用uniq_列名來標(biāo)記**/
)
ENGINE=innodb
AUTO_INCREMENT=1
DEFAULT
CHARSET=utf8;

3.1 設(shè)計(jì)規(guī)范
3.1.1 每張表都要有主鍵id int(10) UNSIGNED ,且與業(yè)務(wù)無關(guān)。 NOT NULL AUTO_INCREMENT 。
3.1.2 建議字段定義為NOT NULL,需為空字段可定義為NOT NULL DEFAULT ''或0(0不要賦予實(shí)際意義)。
3.1.3 庫名、表名、字段名必須使用小寫字母,“_”分割。
3.1.4 庫名、表名、字段名不超過12個字符。
3.1.5 庫名、表名、字段名見名知意,建議使用名詞而不是動詞。
3.1.6 存儲精確浮點(diǎn)數(shù)必須使用DECIMAL替代FLOAT和DOUBLE。
3.1.7 建議使用UNSIGNED存儲非負(fù)數(shù)值。
3.1.8 建議使用INT UNSIGNED存儲IPV4。
3.1.9 ×××定義中建議采用INT(10),而不是INT(1),INT(11)或其他。
3.1.10 存儲狀態(tài),性別等,用TINYINT,如果不需要負(fù)數(shù)加UNSIGNED。使用TINYINT UNSIGNED。
3.1.11 不建議使用ENUM類型,使用TINYINT來代替。
3.1.12 盡可能不使用TEXT、BLOB類型。
3.1.13 VARCHAR(N),N表示的是字符數(shù)不是字節(jié)數(shù),比如VARCHAR(255),可以最大可存儲255個漢字,需要根據(jù)實(shí)際的寬度來選擇N。VARCHAR(N),N盡可能小,因?yàn)镸ySQL一個表中所有的VARCHAR字段最大長度是65535個字節(jié)21845個漢字,進(jìn)行排序和創(chuàng)建臨時表一類的內(nèi)存操作時,會使用N的長度申請內(nèi)存。
3.1.14 表字符集選擇UTF8。
3.1.15 存儲年使用YEAR類型。
3.1.16 存儲日期使用DATE類型。
3.1.17 存儲時間(精確到秒)建議使用TIMESTAMP類型,因?yàn)門IMESTAMP使用4字節(jié),DATETIME使用8個字節(jié)。TIMESTAMP類型保存的值不能比1970早或比2037晚。
3.1.18 將過大字段拆分到其他表中。
3.1.19 禁止在數(shù)據(jù)庫中使用VARBINARY、BLOB存儲圖片、文件等。
3.1.20 表結(jié)構(gòu)變更需要通知DBA審核。


一句話總結(jié):能NOT NULL 就NOT NULL,char、varchar用NOT NULL DEFAULT '',tinyint、smallint、int用NOT NULL DEFAULT 0。char、varchar取值要吝嗇,根據(jù)實(shí)際需求給,比如人名一般不超過5個,varchar(5),不要varchar(200)。int、tinyint這類,int(1)和int(13)都是一樣的,我們統(tǒng)一用int(10),tinyint取值范圍[-128,127],加了unsigned取值[0,255],如果不需要存儲負(fù)數(shù),整型類型的加unsigned。
 

3.2 索引
3.2.1 非唯一索引必須按照“idx_字段名稱_字段名稱[_字段名]”進(jìn)行命名。
3.2.2 唯一索引必須按照“uniq_字段名稱_字段名稱[_字段名]”進(jìn)行命名。
3.2.3 索引名稱必須使用小寫。
3.2.4 索引中的字段數(shù)建議不超過5個。
3.2.5 單張表的索引數(shù)量控制在5個以內(nèi)。
3.2.6 索引字段的順序需要考慮字段值去重之后的個數(shù),個數(shù)多的放在前面。
3.2.7 使用EXPLAIN判斷SQL語句是否合理使用索引,盡量避免extra列出現(xiàn):Using File Sort,UsingTemporary。
3.2.8 UPDATE、DELETE語句需要根據(jù)WHERE條件添加索引。
3.2.9 不建議使用%前綴模糊查詢,例如LIKE “%weibo”。
3.2.10 合理創(chuàng)建聯(lián)合索引(避免冗余),(a,b,c) 相當(dāng)于 (a) 、(a,b) 、(a,b,c),但(a,c)只能用到部分索引。
3.2.11 合理利用覆蓋索引。
3.2.12 SQL變更需要確認(rèn)索引是否需要變更并通知DBA。


3.3  SQL語句
3.3.1 SQL語句中IN包含的值不應(yīng)過多。
3.3.2 UPDATE、DELETE語句不使用LIMIT。
3.3.3 WHERE條件中必須使用合適的類型,避免MySQL進(jìn)行隱式類型轉(zhuǎn)化。
3.3.4 SELECT語句只獲取需要的字段。
3.3.5 SELECT、INSERT語句必須顯式的指明字段名稱,不使用SELECT *,不使用INSERT INTO table()。
3.3.6 使用SELECT column_name1, column_name2 FROM table WHERE[condition]而不是SELECT column_name1 FROM table WHERE[condition]和SELECT column_name2 FROM table WHERE [condition]。
3.3.7 WHERE條件中的非等值條件(IN、BETWEEN、<、<=、>、>=)會導(dǎo)致后面的條件使用不了索引。
3.3.8 避免在SQL語句進(jìn)行數(shù)學(xué)運(yùn)算或者函數(shù)運(yùn)算,容易將業(yè)務(wù)邏輯和DB耦合在一起。
3.3.9 INSERT語句使用batch提交(INSERT INTO table VALUES(),(),()……),values的個數(shù)不應(yīng)過多。
3.3.10 避免使用存儲過程、觸發(fā)器、函數(shù)等,容易將業(yè)務(wù)邏輯和DB耦合在一起,并且MySQL的存儲過程、觸發(fā)器、函數(shù)中存在一定的bug。
3.3.11 避免使用JOIN。
3.3.12 使用合理的SQL語句減少與數(shù)據(jù)庫的交互次數(shù)。
3.3.13 不使用ORDER BY RAND(),使用其他方法替換。
3.3.14 建議使用合理的分頁方式以提高分頁的效率。
3.3.15 統(tǒng)計(jì)表中記錄數(shù)時使用COUNT(*),而不是COUNT(primary_key)和COUNT(1)。


3.4  分表
3.4.1 每張表數(shù)據(jù)量建議控制在500w以下。
3.4.2 使用時間分表,表名后綴必須使用特定格式,比如按年分表user_2016按月分表user_201602、按日分表user_20160209。
 


4. FAQ
4.1 庫名、表名、字段名使用小寫字母,“_”分割。
a)MySQL有配置參數(shù)lower_case_table_names,不可動態(tài)更改,linux系統(tǒng)默認(rèn)為0,即庫表名以實(shí)際情況存儲,大小寫敏感。如果是1,以小寫存儲,大小寫不敏感。如果是2,以實(shí)際情況存儲,但以小寫比較。
b) 如果大小寫混合用,可能存在abc,Abc,ABC等多個表共存,容易導(dǎo)致混亂。
c) 字段名顯示區(qū)分大小寫,但實(shí)際使用不區(qū)分,即不可以建立兩個名字一樣但大小寫不一樣的字段。
d) 為了統(tǒng)一規(guī)范, 庫名、表名、字段名使用小寫字母。

4.2 庫名、表名、字段名必須不超過12個字符。
庫名、表名、字段名支持最多64個字符,但為了統(tǒng)一規(guī)范、易于辨識以及減少傳輸量,必須不超過12字符。

4.3 庫名、表名、字段名見名知意,建議使用名詞而不是動詞。
a) 用戶評論可用表名usercomment或者comment。
b) 庫表是一種客觀存在的事物,一種對象,所以建議使用名詞。

4.4 建議使用InnoDB存儲引擎。
a) 5.5以后的默認(rèn)引擘,支持事務(wù),行級鎖,更好的恢復(fù)性,高并發(fā)下性能更好,對多核,大內(nèi)存,ssd等硬件支持更好。
b) 具體比較可見附件的官方白皮書。

4.5 存儲精確浮點(diǎn)數(shù)必須使用DECIMAL替代FLOAT和DOUBLE。
a) mysql中的數(shù)值類型(不包括整型):
IEEE754浮點(diǎn)數(shù):float? (單精度) , double? 或real? (雙精度)
定點(diǎn)數(shù): decimal或 numeric
單精度浮點(diǎn)數(shù)的有效數(shù)字二進(jìn)制是24位,按十進(jìn)制來說,是8位;雙精度浮點(diǎn)數(shù)的有效數(shù)字二進(jìn)制是53位,按十進(jìn)制來說,是16 位
一個實(shí)數(shù)的有效數(shù)字超過8位,用單精度浮點(diǎn)數(shù)來表示的話,就會產(chǎn)生誤差!同樣,如果一個實(shí)數(shù)的有效數(shù)字超過16位,用雙精度浮點(diǎn)數(shù)來表示,也會產(chǎn)生誤差
b)IEEE754標(biāo)準(zhǔn)的計(jì)算機(jī)浮點(diǎn)數(shù),在內(nèi)部是用二進(jìn)制表示的,但在將一個十進(jìn)制數(shù)轉(zhuǎn)換為二進(jìn)制浮點(diǎn)數(shù)時,也會造成誤差,原因是不是所有的數(shù)都能轉(zhuǎn)換成有限長度的二進(jìn)制數(shù)。
即一個二進(jìn)制可以準(zhǔn)確轉(zhuǎn)換成十進(jìn)制,但一個帶小數(shù)的十進(jìn)制不一定能夠準(zhǔn)確地用二進(jìn)制來表示。

實(shí)例:
drop table if exists t;

create table t(value float(10,2));

insert into t values(131072.67),(131072.68);

select value from t;
+-----------+
|value|
+-----------+
| 131072.67 |
| 131072.69 |
+-----------+

4.6 建議使用UNSIGNED存儲非負(fù)數(shù)值。
同樣的字節(jié)數(shù),存儲的數(shù)值范圍更大。如tinyint 有符號為 -128-127,無符號為0-255

4.7 如何使用INT UNSIGNED存儲ip?
使用INTUNSIGNED而不是char(15)來存儲ipv4地址,通過MySQL函數(shù)inet_ntoa和inet_aton來進(jìn)行轉(zhuǎn)化。Ipv6地址目前沒有轉(zhuǎn)化函數(shù),需要使用DECIMAL或者兩個bigINT來存儲。例如:
SELECT INET_ATON('209.207.224.40');
3520061480
SELECT INET_NTOA(3520061480);
209.207.224.40

4.8 INT[M],M值代表什么含義?
注意數(shù)值類型括號后面的數(shù)字只是表示寬度而跟存儲范圍沒有關(guān)系,比如INT(3)默認(rèn)顯示3位,空格補(bǔ)齊,超出時正常顯示,python、java客戶端等不具備這個功能。如果采用INT類型,我們都用INT(10)

4.9 不建議使用ENUM、SET類型,使用TINYINT來代替。
a)ENUM,有三個問題:添加新的值要做DDL,默認(rèn)值問題(將一個非法值插入ENUM(也就是說,允許的值列之外的字符串),將插入空字符串以作為特殊錯誤值),索引值問題(插入數(shù)字實(shí)際是插入索引對應(yīng)的值)
實(shí)例:
drop table if exists t;
create table t(sex enum('0','1'));
insert into t values(1);
insert into t values('3');
select * from t;
+------+
| sex |
+------+
| 0    |
|      |
+------+
2 rows in set (0.00 sec)

4.10  盡可能不使用TEXT、BLOB類型。
a) 索引排序問題,只能使用max_sort_length的長度或者手工指定ORDER BY SUBSTRING(column,length)的長度來排序
b) Memory引擘不支持text,blog類型,會在磁盤上生成臨時表
c) 可能浪費(fèi)更多的空間
d) 可能無法使用adaptive hash index
e) 導(dǎo)致使用where沒有索引的語句變慢

4.11  VARCHAR中會產(chǎn)生額外存儲嗎?
VARCHAR(M),如果M<256時會使用一個字節(jié)來存儲長度,如果M>=256則使用兩個字節(jié)來存儲長度。

4.12  表字符集選擇UTF8。
a) 使用utf8字符集,如果是漢字,占3個字節(jié),但ASCII碼字符還是1個字節(jié)。
b) 統(tǒng)一,不會有轉(zhuǎn)換產(chǎn)生亂碼風(fēng)險(xiǎn)
c) 其他地區(qū)的用戶(美國、印度、臺灣)無需安裝簡體中文支持,就能正常看您的文字,并且不會出現(xiàn)亂碼
d)ISO-8859-1編碼(latin1)使用了單字節(jié)內(nèi)的所有空間,在支持ISO-8859-1的系統(tǒng)中傳輸和存儲其他任何編碼的字節(jié)流都不會被拋棄。即把其他任何編碼的字節(jié)流當(dāng)作ISO-8859-1編碼看待都沒有問題,保存的是原封不動的字節(jié)流。

4.13  使用VARBINARY存儲變長字符串。
二進(jìn)制字節(jié)流,不存在編碼問題

4.14  為什么建議使用TIMESTAMP來存儲時間而不是DATETIME?
DATETIME和TIMESTAMP都是精確到秒,優(yōu)先選擇TIMESTAMP,因?yàn)門IMESTAMP只有4個字節(jié),而DATETIME8個字節(jié)。同時TIMESTAMP具有自動賦值以及自動更新的特性。
如何使用TIMESTAMP的自動賦值屬性?
a) 將當(dāng)前時間作為ts的默認(rèn)值:ts TIMESTAMP DEFAULTCURRENT_TIMESTAMP。
b)當(dāng)行更新時,更新ts的值:ts TIMESTAMP DEFAULT 0 ONUPDATE CURRENT_TIMESTAMP。
c) 可以將1和2結(jié)合起來:ts TIMESTAMP DEFAULTCURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP。
TIMESTAMP類型保存的值不能比1970早或比2037晚
4.15  建議字段定義為NOT NULL。
a)如果null字段被索引,需要額外的1字節(jié)
b)使索引,索引統(tǒng)計(jì),值的比較變得更復(fù)雜
c)可用'',0代替
d)如果是索引字段,一定要定義為not null
例如:NOT NULL default ''

4.16  禁止在數(shù)據(jù)庫中使用VARBINARY、BLOB存儲圖片、文件等。
采用分布式文件系統(tǒng)更高效

4.17  為什么MySQL的性能依賴于索引?
MySQL的查詢速度依賴良好的索引設(shè)計(jì),因此索引對于高性能至關(guān)重要。合理的索引會加快查詢速度(包括UPDATE和DELETE的速度,MySQL會將包含該行的page加載到內(nèi)存中,然后進(jìn)行UPDATE或者DELETE操作),不合理的索引會降低速度。
MySQL索引查找類似于新華字典的拼音和部首查找,當(dāng)拼音和部首索引不存在時,只能通過一頁一頁的翻頁來查找。當(dāng)MySQL查詢不能使用索引時,MySQL會進(jìn)行全表掃描,會消耗大量的IO。

4.18  為什么一張表中不能存在過多的索引?
InnoDB的secondaryindex使用b+tree來存儲,因此在UPDATE、DELETE、INSERT的時候需要對b+tree進(jìn)行調(diào)整,過多的索引會減慢更新的速度。

4.19  EXPLAIN語句
EXPLAIN語句(在MySQL客戶端中執(zhí)行)可以獲得MySQL如何執(zhí)行SELECT語句的信息。通過對SELECT語句執(zhí)行EXPLAIN,可以知曉MySQL執(zhí)行該SELECT語句時是否使用了索引、全表掃描、臨時表、排序等信息。盡量避免MySQL進(jìn)行全表掃描、使用臨時表、排序等。詳見官方文檔。

4.20  不建議使用%前綴模糊查詢,例如LIKE “%weibo”。
會導(dǎo)致全表掃描

4.21  什么是覆蓋索引?
InnoDB 存儲引擎中,secondaryindex(非主鍵索引)中沒有直接存儲行地址,存儲主鍵值。如果用戶需要查詢secondaryindex中所不包含的數(shù)據(jù)列時,需要先通過secondaryindex查找到主鍵值,然后再通過主鍵查詢到其他數(shù)據(jù)列,因此需要查詢兩次。
覆蓋索引的概念就是查詢可以通過在一個索引中完成,覆蓋索引效率會比較高,主鍵查詢是天然的覆蓋索引。
合理的創(chuàng)建索引以及合理的使用查詢語句,當(dāng)使用到覆蓋索引時可以獲得性能提升。

4.22  UPDATE、DELETE語句不使用LIMIT。
a) 可能導(dǎo)致主從數(shù)據(jù)不一致
b) 會記錄到錯誤日志,導(dǎo)致日志占用大量空間
4.23  為什么需要避免MySQL進(jìn)行隱式類型轉(zhuǎn)化?
因?yàn)镸ySQL進(jìn)行隱式類型轉(zhuǎn)化之后,可能會將索引字段類型轉(zhuǎn)化成=號右邊值的類型,導(dǎo)致使用不到索引,原因和避免在索引字段中使用函數(shù)是類似的。

4.24  為什么不建議使用SELECT *?
增加很多不必要的消耗(cpu、io、內(nèi)存、網(wǎng)絡(luò)帶寬);增加了使用覆蓋索引的可能性;當(dāng)表結(jié)構(gòu)發(fā)生改變時,前段也需要更新。
4.25  為什么不能使用ORDER BY rand()?
因?yàn)镺RDER BYrand()會將數(shù)據(jù)從磁盤中讀取,進(jìn)行排序,會消耗大量的IO和CPU,可以在程序中獲取一個rand值,然后通過在從數(shù)據(jù)庫中獲取對應(yīng)的值。

4.26  MySQL中如何進(jìn)行分頁?
假如有類似下面分頁語句:
SELECT * FROM table ORDER BY TIME DESC LIMIT 10000,10;
這種分頁方式會導(dǎo)致大量的io,因?yàn)镸ySQL使用的是提前讀取策略。
推薦分頁方式:
SELECT * FROM table where id >=(select id from table order by id limit 10000,1)limit 10;
SELECT * FROM table inner JOIN(SELECT id FROM table ORDER BY id LIMIT 10000,10) as t USING(id)

4.27  為什么避免使用復(fù)雜的SQL?
拒絕使用復(fù)雜的SQL,將大的SQL拆分成多條簡單SQL分步執(zhí)行。原因:簡單的SQL容易使用到MySQL的querycache;減少鎖表時間特別是MyISAM;可以使用多核cpu。

2. InnoDB存儲引擎為什么避免使用COUNT(*)?
InnoDB表避免使用COUNT(*)操作,計(jì)數(shù)統(tǒng)計(jì)實(shí)時要求較強(qiáng)可以使用memcache或者redis,非實(shí)時統(tǒng)計(jì)可以使用單獨(dú)統(tǒng)計(jì)表,定時更新。


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI