原文地址:http://www.biaodianfu.com/mysql-best-practices.htmlphp
最近在看MySQL相關的內容,整理以下規範,做爲一名剛剛學習MySQL的菜鳥,整理的內容很是的基礎,中間可能涉及到有錯誤的地方,歡迎批評指正,看到有錯誤的地方指望看官留言。html
這些環境的機器,必定要作到權限劃分明確,讀寫賬號分離,而且有辨識度,能區分具體業務。例如用戶名w_account,r_ account 分別表明讀、寫帳號,account是讀寫帳號。mysql
基本命名原則git
爲何庫、表、字段所有采用小寫?github
在 MySQL 中,數據庫和表對就於那些目錄下的目錄和文件。於是,操做系統的敏感性決定數據庫和表命名的大小寫敏感。sql
- Windows下是不區分大小寫的。
- Linux下大小寫規則:
- 數據庫名與表名是嚴格區分大小寫的;
- 表的別名是嚴格區分大小寫的;
- 列名與列的別名在全部的狀況下均是忽略大小寫的;
- 變量名也是嚴格區分大小寫的;
若是已經設置了駝峯式的命名如何解決?須要在MySQL的配置文件my.ini中增長 lower_case_table_names = 1便可。數據庫
表命名後端
字段命名緩存
索引命名網絡
約束命名
觸發器命名
函數過程命名
序列命名
一、表引擎取決於實際應用場景;日誌及報表類表建議用myisam,與交易,審覈,金額相關的表建議用innodb引擎。如無說明,建表時一概採用innodb引擎。myisam與innodb的區別
二、默認使用utf8mb4字符集,數據庫排序規則使用utf8mb4_general_ci,(因爲數據庫定義使用了默認,數據表能夠再也不定義,但爲保險起見,建議都寫上)。
爲何字符集不選擇utf8,排序規則不使用utf8_general_ci?
採用utf8編碼的MySQL沒法保存佔位是4個字節的Emoji表情。爲了使後端的項目,全面支持客戶端輸入的Emoji表情,升級編碼爲utf8mb4是最佳解決方案。對於JDBC鏈接串設置了characterEncoding爲utf8或者作了上述配置仍舊沒法正常插入emoji數據的狀況,須要在代碼中指定鏈接的字符集爲utf8mb4。
三、全部表、字段均應用 comment 列屬性來描述此表、字段所表明的真正含義,如枚舉值則建議將該字段中使用的內容都定義出來。
四、如無說明,表中的第一個id字段必定是主鍵且爲自動增加,禁止在非事務內做爲上下文做爲條件進行數據傳遞。禁止使用varchar類型做爲主鍵語句設計。
五、如無說明,表必須包含create_time和modify_time字段,即表必須包含記錄建立時間和修改時間的字段
六、如無說明,表必須包含is_del,用來標示數據是否被刪除,原則上數據庫數據不容許物理刪除。
七、用盡可能少的存儲空間來存數一個字段的數據
datetime與timestamp有什麼不一樣?
相同點:TIMESTAMP列的顯示格式與DATETIME列相同。顯示寬度固定在19字符,而且格式爲YYYY-MM-DD HH:MM:SS。
不一樣點:
- TIMESTAMP
- 4個字節儲存,時間範圍:1970-01-01 08:00:01 ~ 2038-01-19 11:14:07
- 值以UTC格式保存,涉及時區轉化 ,存儲時對當前的時區進行轉換,檢索時再轉換回當前的時區。
- datetime
- 8個字節儲存,時間範圍:1000-01-01 00:00:00 ~ 9999-12-31 23:59:59
- 實際格式儲存,與時區無關
如何使用TIMESTAMP的自動賦值屬性?
- 將當前時間做爲ts的默認值:ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP。
- 當行更新時,更新ts的值:ts TIMESTAMP DEFAULT 0 ON UPDATE CURRENT_TIMESTAMP。
- 能夠將1和2結合起來:ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP。
如何使用INT UNSIGNED存儲ip?
使用INT UNSIGNED而不是char(15)來存儲ipv4地址,經過MySQL函數inet_ntoa和inet_aton來進行轉化。Ipv6地址目前沒有轉化函數,須要使用DECIMAL或者兩個bigINT來存儲。
八、如無備註,全部字段都設置NOT NULL,並設置默認值;
九、禁止在數據庫中存儲明文密碼
十、如無備註,全部的布爾值字段,如is_hot、is_deleted,都必須設置一個默認值,並設爲0;
十一、如無備註,排序字段order_id在程序中默認使用降序排列;
十二、整形定義中不添加長度,好比使用INT,而不是INT[4]
INT[M],M值表明什麼含義?
注意數值類型括號後面的數字只是表示寬度而跟存儲範圍沒有關係。不少人他們認爲INT(4)和INT(10)其取值範圍分別是 (-9999到9999)和(-9999999999到9999999999),這種理解是錯誤的。其實對整型中的 M值與 ZEROFILL 屬性結合使用時能夠實現列值等寬。無論INT[M]中M值是多少,其取值範圍仍是 (-2147483648到2147483647 有符號時),(0到4294967295無符號時)。
顯示寬度並不限制能夠在列內保存的值的範圍,也不限制超過列的指定寬度的值的顯示。當結合可選擴展屬性ZEROFILL使用時默認補充的空格用零代替。例如:對於聲明爲INT(5) ZEROFILL的列,值4檢索爲00004。請注意若是在整數列保存超過顯示寬度的一個值,當MySQL爲複雜聯接生成臨時表時會遇到問題,由於在這些狀況下MySQL相信數據適合原列寬度,若是爲一個數值列指定ZEROFILL, MySQL自動爲該列添加UNSIGNED屬性。
1三、使用VARBINARY存儲大小寫敏感的變長字符串
何時用CHAR,何時用VARCHAR?
CHAR和VARCHAR類型相似,但它們保存和檢索的方式不一樣。它們的最大長度和是否尾部空格被保留等方面也不一樣。CHAR和VARCHAR類型聲明的長度表示你想要保存的最大字符數。例如,CHAR(30)能夠佔用30個字符。
- CHAR列的長度固定爲建立表時聲明的長度。長度能夠爲從0到255的任何值。當保存CHAR值時,在它們的右邊填充空格以達到指定的長度。當檢索到CHAR值時,尾部的空格被刪除掉。在存儲或檢索過程當中不進行大小寫轉換。
- VARCHAR列中的值爲可變長字符串。長度能夠指定爲0到65,535之間的值。(VARCHAR的最大有效長度由最大行大小和使用的字符集肯定。總體最大長度是65,532字節)。
同CHAR對比,VARCHAR值保存時只保存須要的字符數,另加一個字節來記錄長度(若是列聲明的長度超過255,則使用兩個字節)。VARCHAR值保存時不進行填充。當值保存和檢索時尾部的空格仍保留,符合標準SQL。
char適合存儲用戶密碼的MD5哈希值,它的長度老是同樣的。對於常常改變的值,char也好於varchar,由於固定長度的行不容易產生碎片,對於很短的列,char的效率也高於varchar。char(1)字符串對於單字節字符集只會佔用一個字節,可是varchar(1)則會佔用2個字節,由於1個字節用來存儲長度信息。
MySQL的查詢速度依賴良好的索引設計,所以索引對於高性能相當重要。合理的索引會加快查詢速度(包括UPDATE和DELETE的速度,MySQL會將包含該行的page加載到內存中,而後進行UPDATE或者DELETE操做),不合理的索引會下降速度。MySQL索引查找相似於新華字典的拼音和部首查找,當拼音和部首索引不存在時,只能經過一頁一頁的翻頁來查找。當MySQL查詢不能使用索引時,MySQL會進行全表掃描,會消耗大量的IO。索引的用途:去重、加速定位、避免排序、覆蓋索引。
什麼是覆蓋索引?
InnoDB存儲引擎中,secondary index(非主鍵索引)中沒有直接存儲行地址,存儲主鍵值。若是用戶須要查詢secondary index中所不包含的數據列時,須要先經過secondary index查找到主鍵值,而後再經過主鍵查詢到其餘數據列,所以須要查詢兩次。覆蓋索引的概念就是查詢能夠經過在一個索引中完成,覆蓋索引效率會比較高,主鍵查詢是自然的覆蓋索引。合理的建立索引以及合理的使用查詢語句,當使用到覆蓋索引時能夠得到性能提高。好比SELECT email,uid FROM user_email WHERE uid=xx,若是uid不是主鍵,適當時候能夠將索引添加爲index(uid,email),以得到性能提高。
索引的基本規範
一、索引數量控制,單張表中索引數量不超過5個,單個索引中的字段數不超過5個。
爲何一張表中不能存在過多的索引?
InnoDB的secondary index使用b+tree來存儲,所以在UPDATE、DELETE、INSERT的時候須要對b+tree進行調整,過多的索引會減慢更新的速度。
二、對字符串使用前綴索引,前綴索引長度不超過8個字符,建議優先考慮前綴索引,必要時可添加僞列並創建索引。
什麼是前綴索引?
前綴索引說白了就是對文本的前幾個字符(具體是幾個字符在創建索引時指定)創建索引,這樣創建起來的索引更小,因此查詢更快。 前綴索引能有效減少索引文件的大小,提升索引的速度。可是前綴索引也有它的壞處:MySQL 不能在 ORDER BY 或 GROUP BY 中使用前綴索引,也不能把它們用做覆蓋索引(Covering Index)。
創建前綴索引的語法:ALTER TABLE table_name ADD KEY(column_name(prefix_length));
三、主鍵準則
四、 重要的SQL必須被索引,核心SQL優先考慮覆蓋索索引
五、區分度最大的字段放在前面
六、索引禁忌
七、 儘可能不使用外鍵
MYSQL 中索引的限制
一、使用預編譯語句
二、避免隱式轉換
三、充分利用前綴索引
四、不使用負向查詢,如not in/like
五、避免使用存儲過程、觸發器、UDF、events等
六、避免使用大表的JOIN
七、避免在數據庫中進行數學運算
七、減小與數據庫的交互次數
八、合理的使用分頁
如何正確的使用分頁?
假若有相似下面分頁語句:SELECT * FROM table ORDER BY id LIMIT 10000, 10
因爲MySQL裏對LIMIT OFFSET的處理方式是取出OFFSET+LIMIT的全部數據,而後去掉OFFSET,返回底部的LIMIT。因此,在OFFSET數值較大時,MySQL的查詢性能會很是低。可使用id > n 的方式進行解決:
使用id > n 的方式有侷限性,對於id不連續的問題,能夠經過翻頁的時候同時傳入最後一個id方式來解決。
1234567 //輸出時,找出當前結果集中的最大最小id//下一頁http://example.com/page.php?last=100select * from table where id<100 order by id desc limit 10//上一頁http://example.com/page.php?first=110select * from table where id>110 order by id desc limit 10這種方式比較大的缺點是,若是在瀏覽中有插入/刪除操做,翻頁不會更新,而總頁數可能仍然是根據新的count(*) 來計算,最終可能會產生某些記錄訪問不到。爲了修補這個問題,能夠繼續引入當前頁碼以及在上次翻頁之後是否有插入/刪除等影響總記錄數的操做並進行緩存
其餘變種方式:
1 select * from table where id >= (select id from table order by id limit #offset#, 1)
九、拒絕大SQL,拆分紅小SQL
十、使用in代替or,in的值不超過1000個
十一、禁止使用order by rand()
十二、使用EXPLAIN診斷,避免生成臨時表
EXPLAIN語句(在MySQL客戶端中執行)能夠得到MySQL如何執行SELECT語句的信息。經過對SELECT語句執行EXPLAIN,能夠知曉MySQL執行該SELECT語句時是否使用了索引、全表掃描、臨時表、排序等信息。儘可能避免MySQL進行全表掃描、使用臨時表、排序等。詳見官方文檔。
1三、用union all而不是union
union all與 union有什麼區別?
union和union all關鍵字都是將兩個結果集合併爲一個,但這二者從使用和效率上來講都有所不一樣。
union在進行表連接後會篩選掉重複的記錄,因此在表連接後會對所產生的結果集進行排序運算,刪除重複的記錄再返回結果。如:
123 select * from test_union1unionselect * from test_union2這個SQL在運行時先取出兩個表的結果,再用排序空間進行排序刪除重複的記錄,最後返回結果集,若是表數據量大的話可能會致使用磁盤進行排序。
而union all只是簡單的將兩個結果合併後就返回。這樣,若是返回的兩個結果集中有重複的數據,那麼返回的結果集就會包含重複的數據了。
從效率上說,union all要比union快不少,因此,若是能夠確認合併的兩個結果集中不包含重複的數據的話,那麼就使用union all,以下:
123 select * from test_union1union allselect * from test_union2
1四、程序應有捕獲SQL異常的處理機制
1五、禁止單條SQL語句同時更新多個表
1六、不使用select * ,SELECT語句只獲取須要的字段
1七、UPDATE、DELETE語句不使用LIMIT
1八、INSERT語句必須顯式的指明字段名稱,不使用INSERT INTO table()
1九、INSERT語句使用batch提交(INSERT INTO table VALUES(),(),()……),values的個數不超過500
20、統計表中記錄數時使用COUNT(*),而不是COUNT(primary_key)和COUNT(1) 備註:僅針對Myisam
2一、數據更新建議使用二級索引先查詢出主鍵,再根據主鍵進行數據更新
2二、禁止使用跨庫查詢
2三、禁止使用子查詢,建議將子查詢轉換成關聯查詢
2四、針對varchar類型字段的程序處理,請驗證用戶輸入,不要超出其預設的長度;
單表一到兩年內數據量超過500w或數據容量超過10G考慮分表,需提早考慮歷史數據遷移或應用自行刪除歷史數據,採用等量均衡分表或根據業務規則分表都可。要分表的數據表必須與DBA商量分表策略
日誌類數據不建議存儲在MySQL上,優先考慮Hbase或OceanBase,如須要存儲請找DBA評估使用壓縮表存儲。