MySQL建表規範與常見問題

来源:http://www.cnblogs.com/kungfupanda/archive/2016/08/29/5818348.html
-Advertisement-
Play Games

一、 表設計 二、 索引 三、 SQL語句 四、 散表 五、 其他 FAQ 1-1.庫名、表名、欄位名必須使用小寫字母,“_”分割。 a)MySQL有配置參數lower_case_table_names,不可動態更改,linux系統預設為0,即庫表名以實際情況存儲,大小寫敏感。如果是1,以小寫存儲, ...


 

一、 表設計

  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審核。

二、 索引

  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。

三、 SQL語句

  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。

四、 散表

  1. 每張表數據量建議控制在5000w以下。
  2. 可以結合使用hash、range、lookup table進行散表。
  3. 散表如果使用md5(或者類似的hash演算法)進行散表,表名尾碼使用16進位,比如user_ff。
  4. 推薦使用CRC32求餘(或者類似的算術演算法)進行散表,表名尾碼使用數字,數字必須從0開始並等寬,比如散100張表,尾碼從00-99。
  5. 使用時間散表,表名尾碼必須使用特定格式,比如按日散表user_20110209、按月散表user_201102。

五、 其他

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

 

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

FAQ

1-1.庫名、表名、欄位名必須使用小寫字母,“_”分割。

a)MySQL有配置參數lower_case_table_names,不可動態更改,linux系統預設為0,即庫表名以實際情況存儲,大小寫敏感。如果是1,以小寫存儲,大小寫不敏感。如果是2,以實際情況存儲,但以小寫比較。

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,非實時統計可以使用單獨統計表,定時更新。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在 Xamarin.Forms 中,我們通常使用 TableView 來構建輸入表單。Xamarin 為我們提供了 EntryCell 用於輸入文本,但是其並不支持密碼輸入,即密碼掩碼。這裡要對 EntryCell 進行擴展,使其支持密碼輸入。 首先,我們需要在共用項目(多平臺項目)中增加控制項定義, ...
  • 牛B的swift屏幕旋轉經驗終結者(OC統一思路) 1、AppDelegate (1)定義變數 var blockRotation: Bool = false (2)定義方法 Swift代碼 func application(application: UIApplication, supported ...
  • 1.方法的命名規範,以NSString和NSMutableString為例如果方法是新創建的方法的第一個名字應該是其返回類型 + (instancetype)string; + (instancetype)stringWithString:(NSString *)string; + (instanc... ...
  • iOS系列 基礎篇 05 視圖鼻祖 - UIView 目錄: 在Cocoa和Cocoa Touch框架中,“根”類時NSObject類。同樣,在UIKit框架中,也存在一個神奇的類——UIView。 從繼承關係上看,UIView是所有視圖的根,我們形象地稱其為“始祖”。 本篇,咱們就一起研究UIVi ...
  • http://www.path8.net/tn/archives/951 MySQL支持大量的列類型,它可以被分為3類:數字類型、日期和時間類型以及字元串(字元)類型。本節首先給出可用類型的一個概述,並且總結每個列類型的存儲需求,然後提供每個類中的類型性質的更詳細的描述。概述有意簡化,更詳細的說明應 ...
  • 增加了一個Tools類,放了一些常用的工具 然後寫了一個比較通用的update方法 懶得寫測試類,肯定好使,相信我~ ...
  • 在使用sqlplus登錄資料庫的時候,輸入sys用戶名出現報錯 解決這個問題就是在輸入用戶名的時候加上as sysdba 這樣就不會出現上面ORA-28009:connection as sys should be as sysdba or sysoper ...
  • 前提條件: 1、Spark Standalone 集群部署完成 2、Intellij Idea 能夠運行 Spark local 模式的程式。 源碼: 這裡主要的思想還是將打包的jar提交到集群。 使用.setJars方法 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...