MySQL數據庫規范 (設計規范+開發規范+操作規范)


I 文檔定義

1.1 編寫目的

      為了在軟件生命周期內規范數據庫相關的需求分析、設計、開發、測試、運維工作,便於不同團隊之間的溝通協調,以及在相關規范上達成共識,提升相關環節的工作效率和系統的可維護性。同時好的規范,在執行的時候可以培養出好的習慣,好的習慣是軟件質量的保證。

1.2  適用范圍 

       本文檔適用於開發、測試、QA及運維團隊成員。 

II . 命名設計規范

2.1 總則

(1)所有命名采用26個英文小寫字母和0-9這十個自然數,加上下划線_組成。不能出現其他字符(注釋除外)。

(2)對象名盡量短,長度不超過30個字符。

(3)對象名字盡量描述實體的內容,由英文單詞、單詞組合或單詞縮寫組成,不以數字和_開頭。

(4)命名中禁止使用SQL保留字。

2.2 庫名

庫名與應用名稱盡量一致,統一小寫,以下划線分割。

2.3 表名

表名必須使用小寫字母或數字,以下划線分割,禁止出現數字開頭,禁止兩個下划線中間只出現數字。如果表名僅有一個單詞,那么建議不使用縮寫,而是用完整的單詞。同一模塊的表盡可能使用相同的前綴,表名稱盡可能表達含義。

數據表 <模塊標識>_<表標識>  例如: order_header , order_detail

編碼表 base_<模塊標識>_<表標識>

日志表 log_<模塊標識>_<表標識>

2.4 字段名 

(1) 能表達字段功能的英文單詞或單詞縮寫,一般不超過三個英文單詞,以下划線分割。布爾類型的字段以“is_”作為前綴。

(2) 各表之間意義相同的字段應同名。

(3) 系統中所有屬於內碼的字段(僅用於表示唯一性和程序內部用到的標識性字段),名稱取為:<表標識>_id。

(4) 系統中屬於是業務范圍內的編號的字段,其代表一定的業務信息,這樣的字段建議命名為<業務標識>_code,其數據類型為VARCHAR,該字段需加唯一索引。

(5) 字段名不要與表名重復。

(6) 不要在列的名稱中包含數據類型。

(7) 每個字段添加字段說明。

(8) 數據庫字段名的修改代價很大,所以字段名稱需要慎重考慮。

(9) 統一命名字段:create_by、create_time、modify_by、modify_time、disabled

2.5 索引名 

A. 非唯一索引必須按照“idx_<構成索引的字段名>”進行命名 

例如:在age上添加索引idx_age

B. 唯一索引必須按照“uidx_<構成索引的字段名>”進行命名

例如:uidx_cardid

C. 組合索引建議包含所有字段名,過長的字段名可以采⽤縮寫形式

例如:idx_age_name

2.6 視圖命名 

v_<模塊標識>_<視圖標識> 

2.7 存儲過程命名 

usp_<模塊標識>_<存儲過程標識> 

2.8 函數命名 

ufn_<模塊標識>_<函數標識> 

III 數據庫設計規范 

3.1 表設計原則

(1) 表的存儲引擎建議是InnoDB存儲引擎,InnoDB 支持事務,支持行級鎖,更好的恢復性,高並發下性能更好

(2)同一個DB中的表,其存儲引擎、字符集應保持統一

(2) 數據表創建、變更具備說明文檔

   數據表創建、變更時必須提供數據表設計文檔: 包含表及字段詳細說明

(3) 規范化與反規范化

          規范化的優點是減少了數據冗余,節約了存儲空間,相應邏輯和物理的I/O次數減少,同時加快了增、刪、改的速度。但是一個完全規范化的設計並不總能生成最優的性能,因為對數據庫查詢通常需要更多的連接操作,從而影響到查詢的速度,而且范式越高性能就會越差。出於性能和方便管理的考慮,原則上表設計應滿足第三范式。有時為了提高某些查詢或應用的性能而可以破壞規范規則,即反規范化。數據應當按兩種類別進行組織:頻繁訪問的數據和頻繁修改的數據。對於頻繁訪問但是不頻繁修改的數據,內部設計應當物理不規范化。對於頻繁修改但並不頻繁訪問的數據,內部設計應當物理規范化。比較復雜的方法是將規范化的表作為邏輯數據庫設計的基礎,然后再根據整個應用系統的需要,物理地非規范化數據。

(4)臨時庫表必須以 _tmp_ 為前綴並以日期為后綴,備份表必須以 _bak_ 為前綴並以日期 為后綴。

(5)盡量控制單表數據量的大小,建議控制在 600 萬以內

         大表在查詢性能和結構修改、備份、恢復等運維方面存在很多弊端。可以用歷史數據歸檔,分庫分表、選擇其它類型數據庫等手段來控制數據量大小。

(6)數據表分類說明

  根據應用的實際需要和特點,可以將數據表進行如下分類: 

A. 基本數據表:描述業務實體的基本信息。例如:人員基本信息、單位基本信息等。 

B. 標准編碼表:描述屬性的列表值。例如:職稱、民族、狀態等。

C. 業務數據表:記錄業務發生的過程和結果。例如:人員調動登記、變更通知單等。

D. 系統信息表:存放與系統操作、業務控制有關的參數。例如:用戶信息、權限、用戶配置信息等。

E. 統計數據表:存放業務數據統計值。例如:通知單統計、人員類別統計等。

F. 臨時處理表:存放業務處理過程中的中間結果。

G. 其他類型表:存放應用層的日志、消息記錄等。

3.2 字段設計原則 

(1)完善的字段說明

         涉及數據字段新增、變更,必須提供字段說明,需要及時更新字段注釋。 

(2)選擇符合存儲需要的最小的數據類型

          一般來說,應該使用能正確存儲和表示數據的最小類型。如果不確定需要什么數據類型,則選擇不會超出范圍的最小類型。選擇更簡單的數據類型。例如,整數類型的比較其代價小於字符類型的比較,因為字符集和排序規則使字符比較更復雜。

(3)合理的字段默認值

         字段盡可能有默認值,字符型的默認值為一個空字符串,數字型的默認為數值0。 盡可能把字段定義為NOT NULL。對於字段能否NULL,應該在SQL建表腳本中明確指明,不應使用缺省。

(4)所有布爾類型字段數據類型是unsigned tinyint,數值0表示為假;數值1表示為真(根據表的字段意義:比如Disabled = 1表示 Disabled 值為真,可以表示數據被邏輯刪除)

(5)避免使用 ENUM 類型

          ENUM 類型的 ORDER BY 操作效率低,需要額外操作。

(6)MySQL最大行大小不能超過64KB(65535字節),所以一個表中的字段不要太多,理論上建議不要超過30個。

(7)如果存儲的字符串長度幾乎相等,推薦使用CHAR定長字符串類型。

(8)VARCHAR是可變長字符串,不預先分配存儲空間,長度不要超過2000,如果存儲長度大於此值,定義字段類型為text或blob,獨立出來一張表,用主鍵來對應,避免影響其他字段索引效率。TEXT 和 BLOB 的主要差別是 BLOB 能夠保存二進制數據;而 TEXT 只能保存字符數據。在程序設計時,盡可能不使用TEXT、BLOB類型。

(9)區分使用DATETIME和TIMESTAMP,兩者都可用來表示YYYY-MM-DD HH:MM:SS類型的日期。兩種都保存日期和時間信息,毫秒部分最高精確度都是6位數。建議使用TIMESTAMP(3)。

A. TIMESTAMP占用4字節,DATETIME占用8字節,當保存毫秒部分時兩者都使用額外的空間 (1-3 字節)。

B. TIMESTAMP的取值范圍比DATETIME小得多,不適合存放比較久遠的日期。TIMESTAMP只能存儲從 '1970-01-01 00:00:01.000000' 到 '2038-01-19 03:14:07.999999' 之間的時間。而DATETIME允許存儲從 '1000-01-01 00:00:00.000000' 到 '9999-12-31 23:59:59.999999' 之間的時間。

C. TIMESTAMP的插入和查詢受時區的影響。如果記錄的日期需要讓不同時區的人使用,最好使用 TIMESTAMP。

(10)根據實際需要選擇能夠滿足應用的最小存儲的日期類型。如果應用只需記錄“年份”,那么用1個字節的YEAR類型完全可以滿足,而不需要用4個字節來存儲的DATE類型。這樣不僅可以節約存儲,還可以提高表的操作效率。

(11)小數類型為decimal,禁止使用float和double。因為float和double在存儲的時候,存在精度損失問題,這是浮點數特有的問題。因此在精度要求比較高的應用中(比如貨幣)要使用定點數而不是浮點數來保存數據。浮點數指的就是含有小數的值,浮點數插入到指定列中超過指定精度后,浮點數會四舍五入,MySQL 中的浮點數指的就是 float 和 double,定點數指的是 decimal,定點數能夠更加精確的保存和顯示數據

(12)字段允許適當冗余,以提高性能,但是必須考慮數據完整性。冗余字段應遵循:

A. 不是頻繁修改的字段。

B. 不是varchar超長字段,更不能是text字段。

C. 需要維護冗余字段的數據完整性。

3.3 主鍵設計原則 

(1)一定要有顯式的主鍵。 

(2)針對InnoDB,在無特殊需求的情況下,建議使用與業務無關的自增ID作為主鍵。

(3)自增字段做主鍵時,字段類型必須是bigint 。

(4)不推薦使用聯合主鍵。由於InnoDB索引的數據結構都是B+tree,對包含聯合主鍵的表做大量寫入,會導致InnoDB為了維持B+tree而移動大量數據,降低性能。

(5)禁止外鍵。對性能損耗特別大,一般的做法是,在業務層設計專門的邏輯或解決方案來保證數據的一致性,以最終一致的時差來換取即使訪問的性能問題。

3.4 索引設計原則 

(1)不允許存在和主鍵重復的索引。主鍵其實就是一個非空的唯一索引,所以再在該字段上添加一個索引完全是多此一舉。

(2)業務上具有唯一特性的字段,即使是組合字段,也必須建成唯一索引。唯一索引的值是唯一的,可以更快速地通過該索引確定某條記錄。另外,即使在應用層做了非常完善的校驗控制,只要沒有唯一索引,必然有臟數據產生。

(3)考慮索引列值的分布。評估某一欄位是否值得建索引,是根據選擇性(符合條件筆數/總筆數)*100%來判斷,選擇性越低代表越值得,慣用的百分比界線是20%。如果某個數據列用於記錄性別(只有"M"和"F"兩種值),並且值出現的幾率幾乎相等,那么無論搜索哪個值都可能得到一半的數據行,在這種情況下索引的用處就不大。因為查詢優化器發現某個值出現在表的數據行中的百分比很高的時候,它一般會忽略索引,進行全表掃描。

(4)為經常需要排序、分組和關聯的字段建立索引。

(5)為常作為查詢條件的字段建立索引。

(6)使用短索引,不要索引大字段。如果對varchar字段進行索引,必須指定一個前綴長度,盡量使用前綴索引,沒必要對全字段建立索引,根據實際文本區分度決定索引長度即可。 使用前綴索引,對列的某幾個字符進行索引,可以提高檢索效率。

(7)合理創建聯合索引,(a,b,c) 相當於 (a) 、(a,b) 、(a,b,c),區分度最高的列在最左邊。

(8)合理使用覆蓋索引減少IO,避免排序。

(9)不要過度使用索引,單個表上的索引數量建議不要超過5個 。

(10)刪除不再使用或者很少使用的索引。

3.5 數據庫里不建議存放業務日志

業務日志的寫入量比較大,影響mysql的性能,建議存放到非關系型數據庫中。  

IV  SQL設計規范

4.1 避免數據類型的隱式轉換

例如:SQL中的字符串類型數據應該統一使用單引號。特別對純數字的字符串,必須用單引號,否則會導致隱式轉換而引起性能問題或索引失效問題。

4.2 避免復雜SQL 

對於非常復雜的SQL,特別是有多層嵌套,帶子句或相關子查詢的,應該先考慮是否設計不當引起的。對於一些復雜SQL可以考慮使用程序實現。

4.3 批量插入

使用INSERT語句一定要給出插入值的字段列表,這樣即使表加了字段也不會影響現有系統的運行。對於小批量插入,可以將多條記錄合並為同一個SQL,使用INSERT INTO tablename (col1,col2,...) VALUES (value1, value2,...),(value1, value2,...),...; 插入多條數據只有一次提交,效率明顯提高。對於大批量插入和文件的導入導出,避免使用insert .... select和create table…select的形式,可能會阻止對源表的並發更新,如果查詢比較復雜,會造成嚴重的性能問題。推薦使用select...into outfile和load data infile的組合來實現,采用這種方式MySQL不會給source_tab 加鎖,還可以大大縮短數據的導出導入時間。但是,由於這種方式存在一定的安全隱患,所以如果需要使用這種方式,必須提交DBA審批,審批通過以后才可執行。

4.4 數據更新

推薦使用主鍵更新,其它維度條件的更新操作會造成頁鎖。對多個表進行關聯update操作風險較大,尤其是當執行計划出現錯誤時,可導致多個表同時被鎖住,應該盡量避免。不帶條件的update會導致全表操作,耗時較長,如有此需求,請聯系DBA評估、操作。

4.5 避免使用TRUNCATE TABLE

TRUNCATE TABLE 比 DELETE速度快,且使用的系統和事務日志資源較少,也可以直接釋放磁盤空間,但TRUNCATE無事務且不觸發trigger,有可能造成事故,故不建議在代碼中使用此語句。

TRUNCATE TABLE在功能上與不帶where子句的delete語句相同。

4.6 避免使用SELECT *

如果不必要取出所有數據,不要用 * 來代替,應給出字段列表。

4.7 使用索引做條件查詢count(*)

innodb引擎在統計方面和myisam是不同的,Myisam內置了一個計數器,所以在使用 select count(*) from table 的時候,直接可以從計數器中取出數據。而innodb必須全表掃描一次方能得到總的數量。每執行一次掃描一次,代價非常高。需要進行count(*)統計表記錄總數時,加上secondary index掃描條件,可以加快掃描速度。例如:SELECT COUNT(*) FROM sbtest1 WHERE id>=0;

4.8 避免IN子句

使用 IN 或 NOT IN 子句時,特別是當子句中有多個值且表數據較多時,速度會明顯下降。可以采用連接查詢或外連接查詢來提高性能。

4.9 避免不必要的排序

不必要的數據排序大大的降低系統性能。 

比如:在使用group by col的時候,mysql會默認order by col ,在只需要分組不需要排序的情況下,可以使用GROUP BY col ORDER BY NULL提升執行效率,僅僅對col列分組,而不排序。

4.10 合理利用最左索引

組合索引的生效原則是:從前往后依次使用生效,如果中間某個索引沒有使用,那么斷點前面的索引部分起作用,斷點后面的索引沒有起作用。對於組合索引,注意索引的使用順序,where子句中將最左索引放在第一列。

比如:(a,b,c) 三個列上加了聯合索引(是聯合索引,不是在每個列上單獨加索引)where a=3 and b=45 and c=5 .... 這種三個索引順序使用中間沒有斷點,全部發揮作用 where a=3 and c=5... 這種情況下b就是斷點,a發揮了效果,c沒有效果where b=3 and c=4... 這種情況下a就是斷點,在a后面的索引都沒有發揮作用,這種寫法聯合索引沒有發揮任何效果where b=45 and a=3 and c=5 .... 這個跟第一個一樣,全部發揮作用,abc只要用上了就行,跟寫的順序無關 。

4.11 多表連接

做多表操作時,應該給每個表取一個別名,每個表字段都應該標明其所屬哪個表。 

為關聯操作的字段建立索引,並使用統一數據類型,不同數據類型做關聯時,MySQL會進行隱式轉換,導致無法用到索引,開銷較大。

多表連接個數建議不超過3個。

4.12 避免在where后的索引字段上使用函數

在where后的索引字段上使用函數會導致索引失效,嚴重情況下會拖慢整個數據庫實例的速度。

例如:

SELECT orderid

FROM order_detail

WHERE from_unixtime(create_time)>'2017-12-04 12:00:00';

這樣使用函數會導致查詢條件不使用索引,使查詢性能下降。應改為:

SELECT orderid

FROM order_detail

WHERE create_time>unix_timestamp('2017-12-04 12:00:00');

4.13 盡量不要做’%’前綴模糊查詢

col like “abc%” 能用上索引,而col like “%abc”不能用上索引

4.14 使用UNION ALL代替UNION

UNION合並兩個或多個SELECT語句的結果集,並消去表中任何重復行。而UNION ALL不會消除重復行。從效率上說,UNION ALL要比UNION快很多,所以如果可以確認合並的多個結果集中不包含重復數據時,建議使用UNION ALL。

4.15 盡量避免OR操作

通常情況下,如果條件中有or,即使其中有條件帶索引也不會使用,所以除非每個列都建立了索引,否則不建議使用OR。在多列OR中,建議用UNION ALL替換。

比如:

select f_crm_id from d_dbname1.t_tbname1 where f_xxx_id = 926067

and (f_mobile ='1234567891' or f_phone ='1234567891' );

應改為:

select f_crm_id from d_dbname1.t_tbname1 where f_xxx_id = 926067

and f_mobile ='1234567891'

UNION ALL

select f_crm_id from d_dbname1.t_tbname1 where f_xxx_id = 926067

and f_phone ='1234567891'

相同字段or可改成 in,如 f_id=1 or f_id=100 --> f_id in (1,100)。

4.16 MySQL 在否定條件中不能使用索引

例如,where 條件里面有<>、not in 、not exists的時候,即便是在這些判斷字段上加有索引,也不會起作用。

4.7 MySQL 在JOIN中連接字段類型如果不一致,則不能使用索引

但是例外就是char和varchar如果在定義表的時候,長度一致,就可以利用索引JOIN,反正不行。例如,char(20)和varchar(20)可以利用索引,char(20)和varchar(25)則不行,不管varchar里面實際存儲的值是多長。

4.18 如果兩個字段列的字符集不同,不推薦JOIN

字符集不同的列,索引失效,容易引起慢查詢故障。

V 完整性設計規范

采用數據庫系統實現數據的完整性,這不但包括通過標准化實現的完整性而且還包括數據的功能性。

5.1 主鍵約束

每個表要求有主健,主健字段或組合字段必須滿足非空屬性和唯一性要求。

5.2 NULL值

(1)由於NULL值在參加任何運算時,結果均為NULL,所以盡可能把字段定義為NOT NULL。對於所有聲明為NOT NULL的字段,必須顯式指定默認值。 

(2)不要使用count(列名)或者count(常量)來替代 count(*),count(*)是SQL92定義的標准統計行數的語法,跟數據庫無關,跟null和非null無關。 

  說明:count(*)會統計值為null的行,而count(列名)不會統計此列為null的行。

(3)count(distinct col)計算該列除null之外不重復的行數

  注意:count(distinct col1, col2),如果其中一列全為null,那么即使另一列有不同的值,也返回0。 

(4)當某一列的值全為null,count(col)的返回結果為0,但sum(col)的返回結果為null,因此使用sum()時需要注意NPE問題。

 例如,可以使用ISNULL()來判斷是否為NULL值,來避免sum的NPE問題: 

 SELECT IF(ISNULL(SUM(g)), 0, SUM(g)) FROM table;

(5)NULL與任何值的直接比較都為null。

NULL<>NULL的返回結果是NULL,而不是false。

NULL=NULL的返回結果是NULL,而不是true。

NULL<>1的返回結果是NULL,而不是true。

5.3 視圖使用原則

為了在應用程序和數據庫之間提供一層抽象,可以為應用程序建立視圖而不必直接訪問表。使用試圖可以簡化操作,不用關注表結構的定義,可以把經常使用的數據集合定義成視圖;屏蔽了表結構變化對用戶的影響, 表增加列對視圖沒有影響,具有一定的獨立性。此外,用戶對視圖不可以隨意的更改和刪除,可以保證數據的安全性。視圖是虛擬的數據庫表,在使用時要遵循以下原則:

A. 盡可能減少使用視圖。

B. 視圖中如果嵌套使用視圖,級數不要超過3級。

C. 由於視圖中只能固定條件或沒有條件,所以對於數據量較大或隨時間的推移逐漸增多的表,不宜使用視圖。

D. 除特殊需要,避免類似SELECT * FROM [Table Name] 而沒有檢索條件的視圖 

E. 視圖中盡量避免出現數據排序的SQL語句。

VI 安全性設計規范

6.1 數據庫賬號使用規范

嚴格管理程序的專用賬號,禁止用戶使用此賬號進行數據操作。 請使用開發人員專用只讀賬號進行數據查詢。

6.2 用戶與權限

為不同用戶設定允許的權限,管理和使用權限分離。確定每個用戶對數據庫表的操作權限,如查詢、新增、更新等。每個用戶擁有剛好能夠完成任務的權限。

嚴格把控好管理權限,只將管理權限賦予管理員。禁止有super權限的應用程序賬號存在。禁止有DDL、DCL權限的應用程序賬號存在。 

6.3 用戶密碼管理

用戶帳號的密碼必須進行加密處理,確保在任何地方查詢都不會出現密碼的明文。

VII 開發行為規范

7.1 總則

(1) 業務部門推廣活動或上線新功能,必須提前通知DBA,並留出必要時間以便DBA完成壓力評估和擴容 ;

(2) 單表多次alter操作必須合並一次操作;

例如:
要給表t增加一個字段aa,同時給已有的字段bb建立索引,通常的做法分為兩步:

alter table t add column aa varchar(10);

然后增加索引:

alter table t add index idx_bb(bb); 

正確的做法是:

alter table t add column aa varchar(10),add index idx_bb(bb);

(3) 懷疑有性能瓶頸的SQL及早提交DBA調優,避免上線出現性能問題;

(4) 批量更新數據,必須通知DBA進行審核,並在執行過程中觀察服務及主從延遲;

(5) 重要業務庫的變更,須告知DBA重要等級、是否數據備份和執行時間要求;

(6) 避免在業務高峰期批量更新、查詢數據庫;

(7) 提交線上建表改表需求,必須詳細注明涉及到的所有SQL語句,便於DBA進行審核和優化;

(8) 所有DDL和DML語句必須要在運維平台上提交申請,禁止口頭或通過聊天工具傳送需求; 

(9) 不要在MySQL數據庫中存放業務邏輯如果把業務邏輯放到數據庫中,將會影響橫向發展和上線測試。建議把業務邏輯提前,放到前端或中間邏輯層,數據庫僅作為存儲層,實現邏輯與存儲的分離;

(10) 出現業務部門人為誤操作導致數據丟失,需要恢復數據的,必須第一時間通知DBA,並提供准確時間地點、誤操作語句等重要線索;

(11) 業務部門程序出現BUG等影響數據庫服務的問題,必須及時通知DBA,便於維護服務穩定; 

(12) 重要項目的數據庫方案選型和設計必須提前通知DBA參與。

7.2 避免使用觸發器

MySQL中觸發器是行觸發的,每次增加、修改或者刪除記錄都會觸發進行處理,編寫過於復雜的觸發器或者增加過多的觸發器對記錄的插入、更新、刪除操作會有比較嚴重的影響,因此不要將應用的處理邏輯過多地依賴於觸發器來處理。觸發器的功能通常可以用其他方式實現,確實需要采用觸發器,請聯系DBA進行確認。

7.3 避免使用存儲過程和函數

在數據庫服務器上進行大量的復雜運算會占用服務器的CPU,造成數據庫服務器的壓力,影響數據庫的正常使用,所以應盡量將這些運算操作分攤到應用服務器上執行。此外,存儲過程難以調試和擴展,數據庫擴展能力遠遠不如應用。

7.4 避免使用視圖

視圖可能導致執行計划錯亂,影響SQL運行效率。對視圖的修改,數據庫必須把它轉化為對基本表的信息修改,不便於維護。

VIII 其他規范

8.1 編制文檔

對所有的命名規范、限制、數據字典、存儲過程、函數都要編制文檔。數據庫文檔化會大大減少犯錯的機會,對開發、支持和跟蹤修改非常有用。

8.2 維護計划規范

(1) 數據歸檔設計

根據業務功能,做最小限度保留,將數據備份至歸檔庫,系統功能兼容訪問歷史數據庫。

(2) 數據歸檔刪除

需要物理刪除不需要歸檔的數據,直接由DBA排作業自動物理刪除。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM