MySQL開發規範

說明,此規範爲內部制定的一個給開發人員如何使用MySQL的規範,由Team共同討論制定,還在不斷的完善中,有一些建議或者規定不必定十分合理,後續可能會修改。另外,MySQL版本不斷進化,也會致使有一些條款失效,請你們根據自身的狀況謹慎參考。html

blackjack ,  roulette ,  slots ,  slots ,  blackjack ,  roulette ,  casino ,  blackjack ,  blackjack ,  blackjack ,  slots

1、 表設計前端

  1. 庫名、表名、字段名 必須 使用小寫字母,「_」分割。 【FAQ】
  2. 庫名、表名、字段名 必須 不超過12個字符。 【FAQ】
  3. 庫名、表名、字段名見名知意,建議使用名詞而不是動詞。 【FAQ】
  4. 建議使用InnoDB存儲引擎。 【FAQ】
  5. 存儲精確浮點數 必須 使用DECIMAL替代FLOAT和DOUBLE。 【FAQ】
  6. 建議使用UNSIGNED存儲非負數值。 【FAQ】
  7. 建議使用INT UNSIGNED存儲IPV4。 【FAQ】
  8. 整形定義中不添加長度,好比使用INT,而不是INT(4)。 【FAQ】
  9. 使用短數據類型,好比取值範圍爲0-80時,使用TINYINT UNSIGNED。
  10. 不建議使用ENUM類型,使用TINYINT來代替。 【FAQ】
  11. 儘量不使用TEXT、BLOB類型。 【FAQ】
  12. VARCHAR(N),N表示的是字符數不是字節數,好比VARCHAR(255),能夠最大可存儲255個漢字,須要根據實際的寬度來選擇N。
  13. VARCHAR(N),N儘量小,由於MySQL一個表中全部的VARCHAR字段最大長度是65535個字節,進行排序和建立臨時表一類的內存操做時,會使用N的長度申請內存。 【FAQ】
  14. 表字符集選擇UTF8。 【FAQ】
  15. 使用VARBINARY存儲變長字符串。 【FAQ】
  16. 存儲年使用YEAR類型。
  17. 存儲日期使用DATE類型。
  18. 存儲時間(精確到秒)建議使用TIMESTAMP類型,由於TIMESTAMP使用4字節,DATETIME使用8個字節。 【FAQ】
  19. 建議字段定義爲NOT NULL。 【FAQ】
  20. 將過大字段拆分到其餘表中。
  21. 禁止 在數據庫中使用VARBINARY、BLOB存儲圖片、文件等。
  22. 表結構變動須要通知DBA審覈。

2、 索引 【FAQ】java

  1. 非惟一索引 必須 按照「idx_字段名稱_字段名稱[_字段名]」進行命名。
  2. 惟一索引 必須 按照「uniq_字段名稱_字段名稱[_字段名]」進行命名。
  3. 索引名稱 必須 使用小寫。
  4. 索引中的字段數 建議 不超過5個。
  5. 單張表的索引數量控制在5個之內。 【FAQ】
  6. 惟一鍵由3個如下字段組成,而且字段都是整形時,使用惟一鍵做爲主鍵。
  7. 沒有惟一鍵或者惟一鍵不符合5中的條件時,使用自增(或者經過發號器獲取)id做爲主鍵。
  8. 惟一鍵不和主鍵重複。
  9. 索引字段的順序須要考慮字段值去重以後的個數,個數多的放在前面。
  10. ORDER BY,GROUP BY,DISTINCT的字段須要添加在索引的後面。
  11. 使用EXPLAIN判斷SQL語句是否合理使用索引,儘可能避免extra列出現:Using File Sort,Using Temporary。 【FAQ】
  12. UPDATE、DELETE語句須要根據WHERE條件添加索引。
  13. 不建議使用%前綴模糊查詢,例如LIKE 「%weibo」。
  14. 對長度過長的VARCHAR字段創建索引時,添加crc32或者MD5 Hash字段,對Hash字段創建索引。 【FAQ】
  15. 合理建立聯合索引(避免冗餘),(a,b,c) 至關於 (a) 、(a,b) 、(a,b,c)。
  16. 合理利用覆蓋索引。 【FAQ】
  17. SQL變動須要確認索引是否須要變動並通知DBA。

3、 SQL 語句python

  1. 使用prepared statement,能夠提供性能而且避免SQL注入。
  2. SQL語句中IN包含的值不該過多。
  3. UPDATE、DELETE語句不使用LIMIT。 【FAQ】
  4. WHERE條件中 必須 使用合適的類型,避免MySQL進行隱式類型轉化。 【FAQ】
  5. SELECT語句只獲取須要的字段。
  6. SELECT、INSERT語句 必須 顯式的指明字段名稱,不使用SELECT *,不使用INSERT INTO table()。 【FAQ】
  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的個數不該過多。
  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。 【FAQ】

4、 散表mysql

  1. 對於字段類型簡單,字段數很少表,單表數據量建議控制在5000w如下。
  2. 能夠結合使用hash、range、lookup table進行散表。
  3. 散表若是使用md5(或者相似的hash算法)進行散表,表名後綴使用16進制,好比user_ff。
  4. 推薦 使用CRC32求餘(或者相似的算術算法)進行散表,表名後綴使用數字,數字 必須 從0開始並等寬,好比散100張表,後綴從00-99。
  5. 使用時間散表,表名後綴 必須 使用特定格式,好比按日散表user_2011020九、按月散表user_201102。

5、 其餘linux

  1. 批量導入、導出數據須要DBA進行審查,並在執行過程當中觀察服務。
  2. 批量更新數據,如update,delete 操做,須要DBA進行審查,並在執行過程當中觀察服務。
  3. 產品出現非數據庫平臺運維致使的問題和故障時,如前端被抓站,請及時通知DBA,便於維護服務穩定。
  4. 業務部門程序出現bug等影響數據庫服務的問題,請及時通知DBA,便於維護服務穩定。
  5. 業務部門推廣活動,請提早通知DBA進行服務和訪問評估。
  6. 若是出現業務部門人爲誤操做致使數據丟失,須要恢復數據,請在第一時間通知DBA,並提供準確時間,誤操做語句等重要線索。

———————————————————————————————————————————————————————————————-redis

FAQ算法

1-1.庫名、表名、字段名 必須 使用小寫字母,「_」分割。 
sql

a) MySQL有配置參數lower_case_table_names,不可動態更改,linux系統默認爲0,即庫表名以實際狀況存儲,大小寫敏感。若是是1,以小寫存儲,大小寫不敏感。若是是2,以實際狀況存儲,但以小寫比較。數據庫

b) 若是大小寫混合用,可能存在abc,Abc,ABC等多個表共存,容易致使混亂。

c) 字段名顯示區分大小寫,但實際使用不區分,即不能夠創建兩個名字同樣但大小寫不同的字段。

d) 爲了統一規範, 庫名、表名、字段名使用小寫字母。

back

1-2.庫名、表名、字段名 必須 不超過12個字符。

庫名、表名、字段名支持最多64個字符,但爲了統一規範、易於辨識以及減小傳輸量,必須不超過12字符。

back

1-3.庫名、表名、字段名見名知意,建議使用名詞而不是動詞。

a) 用戶評論可用表名usercomment或者comment。

b) 庫表是一種客觀存在的事物,一種對象,因此建議使用名詞。

back

1-4.建議使用InnoDB存儲引擎。

a) 5.5之後的默認引擘,支持事務,行級鎖,更好的恢復性,高併發下性能更好,對多核,大內存,ssd等硬件支持更好。

b) 具體比較可見附件的官方白皮書。

back

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

a) mysql中的數值類型(不包括整型): 
IEEE754浮點數: float (單精度) , double 或 real (雙精度) 
定點數: decimal 或 numeric 
單精度浮點數的有效數字二進制是24位,按十進制來講,是8位;雙精度浮點數的有效數字二進制是53位,按十進制來講,是16 位 
一個實數的有效數字超過8位,用單精度浮點數來表示的話,就會產生偏差!一樣,若是一個實數的有效數字超過16位,用雙精度浮點數來表示,也會產生偏差 
b) IEEE754標準的計算機浮點數,在內部是用二進制表示的,但在將一個十進制數轉換爲二進制浮點數時,也會形成偏差,緣由是否是全部的數都能轉換成有限長度的二進制數。 
即一個二進制能夠準確轉換成十進制,但一個帶小數的十進制不必定可以準確地用二進制來表示。

實例: 
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 |

+———–+

back

1-6.建議使用UNSIGNED存儲非負數值。

一樣的字節數,存儲的數值範圍更大。如tinyint 有符號爲 -128-127,無符號爲0-255

back

1- 7. 如何使用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

back

1-8. INT[M],M值表明什麼含義?

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

back

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

a) ENUM,有三個問題:添加新的值要作DDL,默認值問題(將一個非法值插入ENUM(也就是說,容許的值列以外的字符串),將插入空字符串以做爲特殊錯誤值),索引值問題(插入數字實際是插入索引對應的值)

實例:

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)

back

1-11.儘量不使用TEXT、BLOB類型。

a) 索引排序問題,只能使用max_sort_length的長度或者手工指定ORDER BY SUBSTRING(column, length)的長度來排序

b) Memory引擘不支持text,blog類型,會在磁盤上生成臨時表

c) 可能浪費更多的空間

d) 可能沒法使用adaptive hash index

e) 致使使用where沒有索引的語句變慢

back

1-13. VARCHAR中會產生額外存儲嗎?

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

back

1-14.表字符集選擇UTF8。

a) 使用utf8字符集,若是是漢字,佔3個字節,但ASCII碼字符仍是1個字節。 
b) 統一,不會有轉換產生亂碼風險 
c) 其餘地區的用戶(美國、印度、臺灣)無需安裝簡體中文支持,就能正常看您的文字,而且不會出現亂碼 
d) ISO-8859-1編碼(latin1)使用了單字節內的全部空間,在支持ISO-8859-1的系統中傳輸和存儲其餘任何編碼的字節流都不會被拋棄。即把其餘任何編碼的字節流看成ISO-8859-1編碼看待都沒有問題,保存的是原封不動的字節流。

back

1-15.使用VARBINARY存儲變長字符串。

二進制字節流,不存在編碼問題

back

1-18. 爲何建議使用TIMESTAMP來存儲時間而不是DATETIME?

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

如何使用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。

back

1-19.建議字段定義爲NOT NULL。

a)若是null字段被索引,須要額外的1字節

b)使索引,索引統計,值的比較變得更復雜

c)可用0,」代替

d)若是是索引字段,必定要定義爲not null

back

1-21. 禁止 在數據庫中使用VARBINARY、BLOB存儲圖片、文件等。

採用分佈式文件系統更高效

back

2. 爲何MySQL的性能依賴於索引?

MySQL的查詢速度依賴良好的索引設計,所以索引對於高性能相當重要。合理的索引會加快查詢速度(包括UPDATE和DELETE的速度,MySQL會將包含該行的page加載到內存中,而後進行UPDATE或者DELETE操做),不合理的索引會下降速度。

MySQL索引查找相似於新華字典的拼音和部首查找,當拼音和部首索引不存在時,只能經過一頁一頁的翻頁來查找。當MySQL查詢不能使用索引時,MySQL會進行全表掃描,會消耗大量的IO。

back

2-5. 爲何一張表中不能存在過多的索引?

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

back

2-11. EXPLAIN語句

EXPLAIN 語句(在MySQL客戶端中執行)能夠得到MySQL如何執行SELECT語句的信息。經過對SELECT語句執行EXPLAIN,能夠知曉MySQL執 行該SELECT語句時是否使用了索引、全表掃描、臨時表、排序等信息。儘可能避免MySQL進行全表掃描、使用臨時表、排序等。詳見 官方文檔 。

back

2-13.不建議使用%前綴模糊查詢,例如LIKE 「%weibo」。

會致使全表掃描

2-14. 如何對長度大於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)

)

back

2-16. 什麼是覆蓋索引?

InnoDB 存儲引擎中,secondary index(非主鍵索引)中沒有直接存儲行地址,存儲主鍵值。若是用戶須要查詢secondary index中所不包含的數據列時,須要先經過secondary index查找到主鍵值,而後再經過主鍵查詢到其餘數據列,所以須要查詢兩次。

覆蓋索引的概念就是查詢能夠經過在一個索引中完成,覆蓋索引效率會比較高,主鍵查詢是自然的覆蓋索引。

合理的建立索引以及合理的使用查詢語句,當使用到覆蓋索引時能夠得到性能提高。

好比SELECT email,uid FROM user_email WHERE uid=xx,若是uid不是主鍵,適當時候能夠將索引添加爲index(uid,email),以得到性能提高。

back

3-3.UPDATE、DELETE語句不使用LIMIT。

a) 可能致使主從數據不一致

b) 會記錄到錯誤日誌,致使日誌佔用大量空間

back

3-4. 爲何須要避免MySQL進行隱式類型轉化?

由於MySQL進行隱式類型轉化以後,可能會將索引字段類型轉化成=號右邊值的類型,致使使用不到索引,緣由和避免在索引字段中使用函數是相似的。

back

3-6. 爲何不建議使用SELECT *?

增長不少沒必要要的消耗(cpu、io、內存、網絡帶寬);減小了使用覆蓋索引的可能性;當表結構發生改變時,前端代碼可能也須要更新。

back

3-13. 如何減小與數據庫的交互次數?

使用下面的語句來減小和db的交互次數:

INSERT … ON DUPLICATE KEY UPDATE

REPLACE

INSERT IGNORE

INSERT INTO values(),()如何結合使用多個緯度進行散表散庫?

例如message,先按照crc32(message_id)%16將message散到16個庫中,而後針對每一個庫中的表,一天生成一張新表。

back

3-14. 爲何不能使用ORDER BY rand()?

由於ORDER BY rand()會將數據從磁盤中讀取,進行排序,會消耗大量的IO和CPU,能夠在程序中獲取一個rand值,而後經過在從數據庫中獲取對應的值。

back

3-15. MySQL中如何進行分頁?

假若有相似下面分頁語句:

SELECT * FROM table ORDER BY TIME DESC LIMIT 10000,10;

這種分頁方式會致使大量的io,由於MySQL使用的是提早讀取策略。

推薦分頁方式:

SELECT * FROM table WHERE TIME<last_TIME ORDER BY TIME DESC LIMIT 10.

SELECT * FROM table inner JOIN(SELECT id FROM table ORDER BY TIME LIMIT 10000,10) as t USING(id)

back

3-17.爲何避免使用複雜的SQL?

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

back

2. InnoDB存儲引擎爲何避免使用COUNT(*)?

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

參照

1. Optimization and Indexes

2. Data Types

 

 

原文  http://www.iamcjd.com/?p=1237

相關文章
相關標籤/搜索