MySQL建表規範與常見問題

1、 表設計 html

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

2、 索引 前端

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

3、 SQL語句 java

  1. 使用prepared statement,能夠提供性能而且避免SQL注入。
  2. SQL語句中IN包含的值不該過多。
  3. UPDATE、DELETE語句不使用LIMIT。
  4. WHERE條件中必須使用合適的類型,避免MySQL進行隱式類型轉化。
  5. SELECT語句只獲取須要的字段。
  6. SELECT、INSERT語句必須顯式的指明字段名稱,不使用SELECT *,不使用INSERTINTO 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 tableVALUES(),(),()……),values的個數不該過多。
  11. 避免使用存儲過程、觸發器、函數等,容易將業務邏輯和DB耦合在一塊兒,而且MySQL的存儲過程、觸發器、函數中存在必定的bug。
  12. 避免使用JOIN。
  13. 使用合理的SQL語句減小與數據庫的交互次數。
  14. 不使用ORDER BY RAND(),使用其餘方法替換。
  15. 建議使用合理的分頁方式以提升分頁的效率。
  16. 統計表中記錄數時使用COUNT(*),而不是COUNT(primary_key)和COUNT(1)。
  17. 禁止在從庫上執行後臺管理和統計類型功能的QUERY。

4、 散表 python

  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、 其餘 mysql

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

 

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ linux

FAQ redis

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

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

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

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

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


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

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


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

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

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


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

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

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


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 |

+-----------+


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

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


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

使用INTUNSIGNED而不是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


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

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


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)


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

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

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

c) 可能浪費更多的空間

d) 可能沒法使用adaptive hash index

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


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

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


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

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


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

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


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

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

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

a)  將當前時間做爲ts的默認值:ts TIMESTAMP DEFAULTCURRENT_TIMESTAMP。

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

c)  能夠將1和2結合起來:ts TIMESTAMP DEFAULTCURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP。


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

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

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

c)可用0,''代替

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


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

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


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

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

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


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

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


2-11. EXPLAIN語句

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


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

會致使全表掃描

2-14. 如何對長度大於50的VARCHAR字段創建索引?

下面的表增長一列url_crc32,而後對url_crc32創建索引,減小索引字段的長度,提升效率。

  • CREATE TABLE url(

       ……

       url VARCHAR(255) NOT NULL DEFAULT 0,
 
      url_crc32INT UNSIGNED NOT NULL DEFAULT 0,

       ……

       index idx_url(url_crc32)

    )


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

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

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

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

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


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

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

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

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

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


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

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


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

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

INSERT ... ON DUPLICATE KEY UPDATE

REPLACE

INSERT IGNORE

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

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


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

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


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

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

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

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

推薦分頁方式:

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

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


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

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


 

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

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

相關文章
相關標籤/搜索