分享一份公司DBA發的mysql資料庫開發規範文件
規範 - mysql開發規範
MySQL安全
賬號分類
- 管理員帳戶(DBA專用)
- 運維類帳戶(複製,監控,備份等)
- 程式用賬戶(根據業務命名,許可權最小化原則.命名規則: 業務_rw, 業務_r)
- 只讀用賬戶(根據內外網決定是否啟用SSL)
開發許可權
- 程式帳戶許可權:
*_rw賬號許可權: SELECT,INSERT,UPDATE,DELETE,SHOW VIEW
*_r賬號許可權: SELECT,SHOW VIEW,EXECUTE(不改寫資料)
其它安全規範
- 禁止使用程式帳號通過客戶端訪問資料庫,使用OPS系統
- 連線串中密碼必須加密
- 生產環境限定帳戶的Host為內網段IP。例: au_business_r@’10.10.7.%’
- 許可權預設給5+2(可選)種: insert,delete,update,select,show view; execute,mysql.proc select許可權
- DB伺服器禁用外網IP。如果需要外網訪問使用ip+port轉發方式並使用SSL加密資料
- 禁用連結伺服器(FEDERATED引擎)
- 禁止drop表,可以rename table到backupdb資料庫,後續刪除
- 刪除無用賬號
SQL上線流程
指令碼化
- 描述清楚本次上線的目的和步驟
- 必須以指令碼的形式提供給DBA,無法指令碼化時寫出詳細步驟
- 必須限定到某個資料庫: ip+port+database
- 不同的mysql例項,不同的業務,拆分成多個指令碼。目的:清晰,出錯易排查,易回滾
- DDL和DML指令碼分開。先執行DDL,後執行DML
- 需要按順序執行的指令碼,在指令碼名或指令碼中指定順序
- 涉及到原子操作,必須顯示宣告事務(begin; … commit;)
- 對同一個表的多次DDL操作合併為一次操作(更改主鍵除外)
郵件模板
業務描述:XXX上線【備註和注意事項也寫在這裡】
執行時間:現在/2018-06-01 22:00/研發通知
mysql: 192.168.1.1:3306 【注:還可能有sqlserver等】
db: au_business 【資料庫名稱】
SQL(共3步): 【注: 如有步驟,請描述,需要考慮回滾方案】
第1步:檢查xxx正確性,備份xxx表
select count(*) as cnt from tb_city where city_id in(1,2); #2條記錄則正常
第2步:更新xxx的值
begin;
update tb_city set city_name='北京', city_type='5' where city_id = 1;
update tb_city set city_name='深圳', city_type='5' where city_id = 2;
commit;
第3步:增加xxx商品
insert into tb_city(city_name, city_type) values('上海', '5'), ('香港', '1'); #儘量批量提交
注:
- 附件較大時請壓縮
- 附件名稱可加1,2,3備註
- 資料庫有多個時,SQL指令碼中加上use dbname;
Review
- 先了解清楚指令碼的目的和步驟
- 檢查指令碼是否符合資料庫開發規範
- 檢查指令碼是否存在bug(主要針對複雜SQL)
- 提出合理建議,總結規範
- DDL語句的稽核提前到開發,提測階段
上線
- 瞭解Move in相關的資料庫情況,確保不影響生產:庫大小,表大小,是否存在replication,是否鎖表等
- 耗時操作,儘量分段執行指令碼,有意外情況,及時回滾
- 業務相關INSERT,DELETE,UPDATE,SELECT通過OPS平臺操作,其餘通過DBA執行
- OPS平臺支援不了的功能,提交給DBA來完成
MysQL設計與開發規範
設計規範
庫
- 【必須】庫與庫之間解耦,不允許跨庫查詢(dbname.tablename),同一DB例項上的庫與庫之間的查詢寫成多條SQL。資料庫由於效能原因遷移時,程式只用修改連線串。
- 【必須】不同業務間的資料互動統一通過介面進行,不使用複製技術
- 【建議】複製技術僅用於讀寫分離,高可用,報表平臺數據同步等
表
- 【必須】庫名、表名、欄位名,全部小寫(mysql引數 :lower_case_table_names=1),使用26個英文字母,下劃線,數字。只能以英文字母開頭, 不超過32個字元。須見名知意,命名與業務、產品線等相關聯。庫命名:根據業務起名,表命名:業務名稱_表作用。如:risk_service.tb_risk_config。測試環境庫名需與線上保持一致,多套DB環境時,字尾加資料區分:risk_service_2
- 【必須】統一單數形式,如訂單表:order,反例:orders
- 【強制】資料庫表、欄位必須加入中文註釋
- 【必須】庫名、表名、欄位名禁止使用MySQL保留字
- 【建議】欄位允許適當冗餘,減少JOIN,遵循規則:1. 不是頻繁修改的欄位2.不是varchar超長欄位
- 【建議】大欄位、訪問頻率低的欄位拆分到單獨的表中儲存,分離冷熱資料
- 【建議】臨時性的資料或者生命週期很短的資料等資訊,不放在資料庫
- 【建議】監控分析類日誌資料不建議儲存在MySQL上,優先考慮非關係型資料庫或檔案中,如需要與DBA評估使用壓縮表儲存欄位
- 關鍵業務需要加history表。除了儲存修改前的所有欄位,還需要增加修改人,修改時間,修改型別(update,delete)等至少3個欄位。
- 【建議】單表超過500W行或容量超過2G, 才考慮分庫分表,或歸檔處理
- 【建議】表設計時常問3個問題:表是否是核心業務資料?表讀寫頻率?資料量大小,是否可歸檔?
- 【必須】臨時庫、表名必須以tmp為字首,並以日期(20170101)為字尾,用完立刻刪除。
- 【必須】備份庫、表必須以bak為字首,並以日期(20170101)為字尾,可以加上其它說明。
- 【必須】臨時表,備份表儲存到備份庫(backupdb),定期清除。
- 【建議】總體原則:冷熱分離,減少JOIN,讀寫狀態,考慮併發
欄位
- 【必須】所有欄位必須not null + default約束,減少三值邏輯。特殊情況與DBA確認。
- 【必須】同一業務欄位在不同的表中的型別必須一致,防止JOIN時發生型別轉換。名稱必須一致,自增列除外,自增列統一命名id。
- 【必須】欄位佔用位元組越小越好,儘量用數字型別,用tinyint代替enum型別
- 【建議】Varchar長度不允許超過5000,如果超長,定義為text,需要與DBA確認
- 【建議】儘可能不用text,blob型別,確定需要找DBA確認
- 【必須】禁止使用float, double型別,用decimal, int等替代
- 【必須】根據業務情況定義varchar長度,儘量不超過255
- 【建議】禁止使用varchar型別作為主鍵語句設計
- 【必須】長度不變用char,否則統一用varchar,長度不超過255
- 【必須】status, type等欄位型別,範圍不超過正負255,統一用tinyint
- 【必須】表自增列名稱必須為id,型別為int/bigint,步長為1。
- 【建議】不使用unsigned型別,統一使用有符號型別int/bigint。
- 【建議】表必有create_time, 人為觸發新增資料的表必須有create_user欄位,有資料修改的表必須有update_time欄位, 人為觸發修改資料的表必須有update_user欄位,型別為datetime, 更新資料表記錄時,必須同時更新相關的update_time,update_user值。
- 【建議】欄位必有註釋,欄位含義變更時需要維護欄位註釋。建議格式為: 1=正常; 2=異常/不可用; 3=刪除
鍵
- 【必須】表必須有主鍵,可用自增列做主鍵。業務鍵作主鍵需要考慮效能
- 【必須】禁用外來鍵約束,由程式實現資料完整性
- 【必須】業務上需要進行唯一性約束的,必須加唯一鍵
索引
- 【建議】一個索引中的欄位數建議不超過5個,一張表中的索引數一般不超過10個
- 【建議】建表時加上可預見的索引
- 【必須】選擇性高的欄位放在前面,不在低基數列上建立索引(比如:性別欄位)
- 【必須】優化複合索引中的欄位順序
- 【必須】避免冗餘和重複索引
- 【必須】重要的SQL才加索引
- 【必須】僅除錯時允許使用 FORCE INDEX
- 【必須】索引命名規範:索引:ix_field1_field2;唯一索引:uix_field1_field2;主鍵:預設(PRIMARY)
- 【建議】varchar欄位建立索引,欄位較長時需要指定索引長度: CREATE INDEX ix_name ON customer(name(10));
- 【建議】適當使用覆蓋索引來優化查詢,避免回表。主要針對高併發或查詢資料量比較大的情景。
- 【注意】如果有 order by 的場景,請注意利用索引的有序性。反例:WHERE a>10 ORDER BY b; 索引a_b 無法排序
其它物件
- 【必須】禁止使用MySQL儲存過程,函式,觸發器,定時事件,檢視。
開發規範
程式設計
- 【必須】SQL必須指定列名操作,禁止用。COUNT()除外。
- 【必須】注意SQL的資料型別,where條件左右兩邊資料型別需一致,不一致時,強制轉換不走索引的那一端,以防出現隱式轉換,導致索引不可用
- 【必須】SQL中同一欄位OR條件改用IN(),IN包含的值少於50個
- 【必須】應用程式應有捕獲SQL異常的處理機制
- 【建議】需要走索引的條件,禁止在where 條件的過濾欄位使用函式或表示式處理。where ltrim(name)=’test’; where date_format(now(), '%Y-%m-%d')= '2018-01-01'
- 【建議】不使用負向查詢(NOT, <>)和%開頭的模糊查詢,如果需要走搜尋引擎來解決
- 【建議】避免使用子查詢
- 【建議】拒絕複雜SQL,將大的SQL拆分成多條簡單SQL
- 【建議】書寫格式統一縮排
- 【必須】使用表別名,SELECT列表中的列必須帶上表別名
- 【必須】事務要簡單,整個事務的時間長度不能太長
- 【必須】更新或刪除時,先寫SELECT語句,再改成update,delete語句
- 【必須】能用union all就不要用union,注意邏輯不一樣
- 【必須】禁止一個update同時更新多張表
- 【必須】對同一個表的多次DDL操作合併為一次操作
- 【建議】不建議使用子查詢,建議將子查詢轉換成JOIN查詢
- 【必須】不要使用 count(列名)或count(常量)來替代count(), count()是 SQL92 定義的.標準統計行數的語法,跟資料庫無關,跟 NULL 和非 NULL 無關
- 【注意】count(distinct col) 計算該列除 NULL 之外的不重複行數, 注意 count(distinct col1, col2) 如果其中一列全為 NULL,那麼即使另一列有不同的值,也返回為 0
- 【注意】當某一列的值全是 NULL 時, count(col)的返回結果為 0,但 sum(col)的返回結果為NULL,可以如下: SELECT IF(ISNULL(SUM(g)), 0, SUM(g)) FROM table_name;
- 【注意】NULL值與任何值比較結果都是NULL
- 【必須】線上程式中不建議使用truncate table語法。
- 【建議】不建議超過3張表的JOIN。
- 【建議】能序列處理SQL,批量序列處理,不使用多執行緒。
- 【建議】關係型資料庫適合批量處理資料,不建議一條一條處理資料。
- 【必須】只能使用inner/left/ JOIN … ON …寫法,不使用tableA,tableB where…寫法。
- 【必須】SQL即邏輯。
分頁查詢
-
【必須】精確分頁:計算記錄總條數與詳細記錄查詢分兩種SQL寫。因為計算總條數時,只用count(*),且可以不用關聯不必要的表。
-
【建議】精確分頁:詳細記錄查詢,先取出分頁記錄的id主鍵,再關聯其它。正例:
select a.order_number, a.order_flag from sale_order a inner join ( select id from sale_order where order_time > '2017-01-01' limit 100000, 20 ) b on a.id=b.id
-
【建議】在程式碼中寫分頁查詢邏輯時,若 count 為 0 應直接返回,避免執行後面的分頁語句
效能規範
調優
- 在只讀伺服器(10.12.1.1)上使用explain調優SELECT語句,update&delete語句也可以改成select
故障
- 如果出現業務部門人為誤操作,需要恢復資料,請在第一時間通知 DBA,並提供準確時間點,誤操作語句,日誌等資訊。請提供日誌文字,不要截圖
DB運維規範
引數配置
- 必須使用InnoDB
- 符集必須使用UTF8或儲存emoji表情時使用UTF8MB4
複製
- 複製帳戶host限定為具體IP
- 複製master->slave的資料庫名必須相同,slave上覆制庫只能存放複製物件
- 複製資料庫必須同名,且slave上覆制庫中除了從master複製過來的物件,不能再有其它物件。
歡迎關注公眾號交流學習,會分享更多專案實踐以及學習資料: