Mysql系列二:Mysql 開發標準規範

一、表設計

1. 庫名、表名、字段名使用小寫字母,“_”分割。

2. 庫名、表名、字段名不超過12個字符。

3. 庫名、表名、字段名見名知意,儘量使用名詞而不是動詞。

4. 優先使用InnoDB存儲引擎。

5. 存儲精確浮點數使用DECIMAL替代FLOAT和DOUBLE。

6. 使用UNSIGNED存儲非負數值。

7. 使用INT UNSIGNED存儲IPV4。【FAQ】

8. 整形定義中不添加長度,比如使用INT,而不是INT[4]。【FAQ】

9. 使用短數據類型,比如取值範圍爲0-80時,使用TINYINT UNSIGNED。

10. 不建議使用ENUM、SET類型,使用TINYINT來代替。

11. 儘可能不使用TEXT、BLOB類型。

12. VARCHAR(N),N表示的是字符數不是字節數,比如VARCHAR(255),可以最大可存儲255個漢字,需要根據實際的寬度來選擇N。

13. VARCHAR(N),N儘可能小,因爲MySQL一個表中所有的VARCHAR字段最大長度是65535個字節,進行排序和創建臨時表一類的內存操作時,會使用N的長度申請內存。

14. VARCHAR(N),N>5000時,使用BLOB類型。

15. 表字符集選擇UTF8。

16. 使用VARBINARY存儲變長字符串。

17. 存儲年使用YEAR類型。

18. 存儲日期使用DATE類型。

19. 存儲時間(精確到秒)使用TIMESTAMP類型,因爲TIMESTAMP使用4字節,DATETIME使用8個字節。【FAQ】

20. 字段定義爲NOT NULL。

21. 將過大字段拆分到其他表中。

22. 不在數據庫中使用VARBINARY、BLOB存儲圖片、文件等。

二、 索引

1. 非唯一索引按照“idx_字段名稱_字段名稱[_字段名]”進行命名。

2. 唯一索引按照“uniq_字段名稱_字段名稱[_字段名]”進行命名。

3. 索引名稱使用小寫。

4. 索引中的字段數不超過5個。

5. 唯一鍵由3個以下字段組成,並且字段都是整形時,使用唯一鍵作爲主鍵。

6. 沒有唯一鍵或者唯一鍵不符合5中的條件時,使用自增(或者通過發號器獲取)id作爲主鍵。

7. 唯一鍵不和主鍵重複。

8. 索引字段的順序需要考慮字段值去重之後的個數,個數多的放在前面。

9. ORDER BY,GROUP BY,DISTINCT的字段需要添加在索引的後面。

10. 單張表的索引數量控制在5個以內。#索引少走索引查詢快.

11. 使用EXPLAIN判斷SQL語句是否合理使用索引,儘量避免extra列出現:Using File Sort,Using Temporary。【FAQ】

12. UPDATE、DELETE語句需要根據WHERE條件添加索引。#注意要是不加條件可能全部執行,災難性的,一定要避免,或者用技術手段阻止這樣的執行方式.

13. 不建議使用%前綴模糊查詢,例如LIKE “%weibo”。#相當於全表做匹配,查詢會較慢.

14. 對長度大於50的VARCHAR字段建立索引時,使用其他方法。【FAQ】

15. 合理創建聯合索引(避免冗餘),(a,b,c) 相當於 (a) 、(a,b) 、(a,b,c)。

16. 合理利用覆蓋索引。【FAQ】

17. 在合理情況下使用FORCE INDEX。

18. SQL變更需要確認索引是否需要變更並通知DBA。

三、 SQL語句

1. 使用prepared statement,可以提供性能並且避免SQL注入。#參考文檔:http://www.cnblogs.com/liuhongfeng/p/4175765.html

2. SQL語句中IN包含的值不超過500。

3. UPDATE、DELETE語句不使用LIMIT。#沒理解,用limit豈不是更快.有限制.

4. WHERE條件中使用合適的類型,避免MySQL進行隱式類型轉化。【FAQ】

5. SELECT語句只獲取需要的字段。

6. SELECT、INSERT語句顯式的指明字段名稱,不使用SELECT *,不適用INSERT INTO table()。

7. 使用SELECT column_name1, column_name2 FROM table WHERE [condition]而不是SELECT column_name1 FROM table WHERE [condition]和SELECT column_name2 FROM table WHERE

[condition]。#加上相應的條件.

8. WHERE條件中的非等值條件(IN、BETWEEN、<、<=、>、>=)會導致後面的條件使用不了索引。

9. 避免在SQL語句進行數學運算或者函數運算,容易將業務邏輯和DB耦合在一起。

10. INSERT語句使用batch提交(INSERT INTO table VALUES(),(),()„„),values的個數不超過500。

11. 避免使用存儲過程、觸發器、函數等,容易將業務邏輯和DB耦合在一起,並且MySQL的存儲過程、觸發器、函數中存在一定的bug。#沒理解

12. 避免使用JOIN。

13. 使用合理的SQL語句減少與數據庫的交互次數。【FAQ】

14. 不使用ORDER BY RAND(),使用其他方法替換。【FAQ】

15. 使用合理的分頁方式以提高分頁的效率。【FAQ】

16. 統計表中記錄數時使用COUNT(*),而不是COUNT(primary_key)和COUNT(1)。

17. 禁止在從庫上執行後臺管理和統計類型功能的QUERY。

四、 散表

1. 每張表數據量控制在5000w以下。

2. 可以結合使用hash、range、lookup table進行散表。

3. Hash散表,表名後綴使用16進制,比如user_ff。

4. 使用時間散表,表名後綴使用日期,比如按日散表user_20110209、按月散表user_201102。

五、 其他

1. 批量導入、導出數據需要DBA進行審查,並在執行過程中觀察服務。

2. 批量更新數據,如update,delete 操作,需要DBA進行審查,並在執行過程中觀察服務。#避免影響其他數據.

3. 產品出現非數據庫平臺運維導致的問題和故障時,如前端被抓站,請及時通知DBA,便於維護服務穩定。

4. 業務部門程序出現bug等影響數據庫服務的問題,請及時通知DBA,便於維護服務穩定。

5. 業務部門推廣活動,請提前通知DBA進行服務和訪問評估。

6. 如果出現業務部門人爲誤操作導致數據丟失,需要恢復數據,請在第一時間通知DBA,並提供準確時間,誤操作語句等重要線索。

 

6.FAQ

1. 如何使用INT UNSIGNED存儲ip?

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

2. INT[M],M值代表什麼含義?

注意數值類型括號後面的數字只是表示寬度而跟存儲範圍沒有關係,比如INT(3)默認顯示3位,空格補齊,超出時正常顯示,python、java客戶端等不具備這個功能。

3. 爲什麼建議使用TIMESTAMP來存儲時間而不是DATETIME?

DATETIME和TIMESTAMP都是精確到秒,優先選擇TIMESTAMP,因爲TIMESTAMP只有4個字節,而DATETIME 8個字節。同時TIMESTAMP具有自動賦值以及自動更新的特性。

4. 如何使用TIMESTAMP的自動賦值屬性?

a) 將當前時間作爲ts的默認值:ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP。

b) 當行更新時,更新ts的值:ts TIMESTAMP DEFAULT 0 ON UPDATE CURRENT_TIMESTAMP。

c) 可以將1和2結合起來:ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP。

5.如何對長度大於50的VARCHAR字段建立索引?

下面的表增加一列url_crc32,然後對url_crc32建立索引,減少索引字段的長度,提高效率。

• CREATE TABLE url(

„„ url VARCHAR(255) NOT NULL DEFAULT 0, url_crc32 INT UNSIGNED NOT NULL DEFAULT 0, „„ index idx_url(url_crc32)

6. 爲什麼需要避免MySQL進行隱式類型轉化?

因爲MySQL進行隱士類型轉化之後,可能會將索引字段類型轉化成=號右邊值的類型,導致使用不到索引,原因和避免在索引字段中使用函數是類似的。

7. 爲什麼避免使用複雜的SQL?

拒絕使用複雜的SQL,將大的SQL拆分成多條簡單SQL分步執行。原因:簡單的SQL容易使用到MySQL的query cache;減少鎖表時間特別是MyISAM;可以使用多核cpu。

8. 爲什麼不建議使用SELECT *?

增加很多不必要的消耗(cpu、io、內存、網絡帶寬);增加了使用覆蓋索引的可能性;當表結構發生改變時,前段也需要更新。

9. InnoDB存儲引擎爲什麼避免使用COUNT(*)?

InnoDB表避免使用COUNT(*)操作,計數統計實時要求較強可以使用memcache或者redis,非實時統計可以使用單獨統計表,定時更新。

10. MySQL中如何進行分頁?

假如有類似下面分頁語句: SELECT * FROM table ORDER BY TIME DESC LIMIT 10000,10; 這種分頁方式會導致大量的io,因爲MySQL使用的是提前讀取策略。 推薦分頁方式: SELECT * FROM table WHERE TIME

11. 爲什麼不能使用ORDER BY rand()?

因爲ORDER BY rand()會將數據從磁盤中讀取,進行排序,會消耗大量的IO和CPU,可以在程序中獲取一個rand值,然後通過在從數據庫中獲取對應的值。

12. 如何減少與數據庫的交互次數?

使用下面的語句來減少和db的交互次數: INSERT ... ON DUPLICATE KEY UPDATE REPLACE INSERT IGNORE INSERT INTO values(),()#即數據儘量一次插入,不要分開多次插入,減少交互次數.

13. 如何結合使用多個緯度進行散表散庫?

例如微博message,先按照crc32(message_id)將message散到16個庫中,然後針對每個庫中的表,

一天生成一張新表。

14. VARCHAR中會產生額外存儲嗎?

VARCHAR(M),如果M<256時會使用一個字節來存儲長度,如果M>=256則使用兩個字節來存儲長度。

15. 爲什麼MySQL的性能依賴於索引?

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

16. 爲什麼一張表中不能存在過多的索引?

InnoDB的secondary index使用b+tree來存儲,因此在UPDATE、DELETE、INSERT的時候需要對b+tree進行調整,過多的索引會減慢更新的速度。

17. 什麼是覆蓋索引?

InnoDB存儲引擎中,secondary index(非主鍵索引)中沒有直接存儲行地址,存儲主鍵值。如果用戶需要查詢secondary index中所不包含的數據列時,需要先通過secondary index查找到主鍵值,然後再通過主鍵查詢到其他數據列,因此需要查詢兩次。 覆蓋索引的概念就是查詢可以通過在一個索引中完成,覆蓋索引效率會比較高,主鍵查詢是天然的覆蓋索引。 合理的創建索引以及合理的使用查詢語句,當使用到覆蓋索引時可以獲得性能提升。 比如SELECT email,uid FROM user_email WHERE uid=xx,如果uid不是主鍵,適當時候可以將索引添加爲index(uid,email),以獲得性能提升。#相當於就是都走索引操作效率會變高.

18. EXPLAIN語句

EXPLAIN語句(在MySQL客戶端中執行)可以獲得MySQL如何執行SELECT語句的信息。通過對SELECT語句執行EXPLAIN,可以知曉MySQL執行該SELECT語句時是否使用了索引、全表掃描、臨時表、排序等信息。儘量避免MySQL進行全表掃描、使用臨時表、排序等

發佈了49 篇原創文章 · 獲贊 59 · 訪問量 20萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章