|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
一. 基础规范2 j5 I& r {+ u2 z
所有表都需要添加注释
& [8 ?( j9 V8 X: c" w% O( i; r使用comment从句添加表和列的备注,从一开始就做好数据字典维护。
2 @# W8 t' O3 K5 H4 i3 _4 u单表数据量建议控制在500万以内
% _* z: T4 z3 I9 D过大会给备份和恢复带来困难,可用历史数据归档(用于日志数据),分库分表(用于业务数据)等手段来控制数据量大小。
: c4 I5 w3 O4 g5 ]% N4 x! R不在数据库中存储图、文件等二进制数据
, ]: [& c; C4 N( i- v7 @/ l3 ^9 K图或文件IO操作耗时,通常存储于文件服务器,数据库只存储文件地址信息。
7 e8 t" k6 W. Y# U( K尽量冷热数据分离,减小表的宽度+ G$ v$ L+ Z, E5 t6 I; Q$ b1 a% w
把不常用且占宽大的字段从热表中分离可独立存储,每一行数据大小不超过65535字节。7 `7 l9 H6 {7 ]+ v( j3 d2 e
禁止测试、开发环境直连线上数据库: r- S2 Y5 @8 j8 N2 `! P: d
二、命名规范( }9 a7 J1 _, i/ y
库名、表名、字段名必须使用小写字母,并采用下划线分割
* c; {2 n0 V' h3 y3 V' ^4 HMySQL配置参数lower_case_table_names=1,即库表名以小写存储,大小写不敏感。如果是0,则库表名以实际情况存储,大小写敏感;如果是2,以实际情况存储,但以小写比较。如果大小写混合使用,可能存在abc,Abc,ABC等多个表共存,容易导致混乱。, C/ ]* E+ l. P7 |& K# J
库名、表名、字段名不要超过32个字符,需见名知意易于辨识
; v/ w1 u3 z, L库名、表名、字段名支持最多64个字符,但为了减少传输量,禁止超过32个字符。7 X8 J, g1 j7 j* Z" Y, c
库名、表名、字段名禁止使用MySQL保留字
! Q& j7 I3 C. k当库名、表名、字段名等含有保留字时,SQL语句必须用反引号引用属性名称,这将使SQL语句书写、SHELL脚本中变量的转义变得非常复杂。6 a! a' j! }) h0 a% u
备份库、表必须以bak为前缀,并以日期为后缀! g: X Z# R F" C
形如:bak_user_account_20190313
; A* f9 i& a3 c3 e& W9 q* S+ B/ a& B索引的命名
* F# C0 L8 b r$ ?主键索引名为 pk_字段名;唯一索引名为 uk _字段名;普通索引名则为 idx_字段名。
1 d' d3 y. v. T% V+ D3 s0 P9 N+ A3 K3 Q5 t7 X. @0 _9 M
三、字段设计规范% d: r$ v W' C: @" E
优先选择符合存储需要的最小的数据类型
2 o: M% s+ |! K$ P, f列的字段类型越大,建立索引占据的空间就越大,使得一个页中的索引越少,IO次数增加,影响性能,能用int的就不用char或者varchar,能用tinyint的就不用int。状态status、类型type等字段推荐使用tinytint或者smallint类型节省存储空间;使用 UNSIGNED 存储非负数值;使用tinyint来代替 enum和boolean;存储 ip 最好用 int存储而非 char(15)。
4 u; M E+ @2 t! n$ r6 X不推荐使用blob,text等类型
+ E2 O) Y& y) I% e% i, b" wblob,text是为了存储极大的字符串而设计的数据类型,采用二进制与字符串方式存储,该数据类型不能设置默认值、不便于排序、不便于建立索引,varchar的性能会比text高很多,如果非要使用,建议将这种数据分离到单独的拓展表中。
$ }- I3 v( D* y7 Q) h禁止使用字符串来存储日期型数据0 f( N$ U% i$ y& d6 {6 R& Y8 o+ P
一是无法使用日期函数计算比较,二是因为字符串存储要占更多的内存空间,datetime占8字节,timestamp是以int存储,占4字节。
/ g! W" ^. }) v5 T) |% L用DECIMAL代替FLOAT和DOUBLE存储精确浮点数* j) X4 F2 Z" g2 J, S K4 H/ M
Decimal类型为精准浮点数,float 和 double 都有精度损失的问题,很可能在值的比较时,得到不正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。& y3 c4 m0 @9 U' X* w& ^/ l, |
必须把字段定义为NOT NULL并设默认值' g' ~ a3 @3 \+ s4 v
一是NULL需要更多的存储空间,二是NULL的列可能索引实效,三是NULL只能采用IS NULL或者IS NOT NULL,而在=/!=/in/not in时有大坑。
/ ?2 |* z- \7 D/ a使用varchar(20)存储手机号,不要使用整数: H8 R5 Y; C* C$ d
一是牵扯到国家代号,可能出现+/-/()等字符,例如+86,二是varchar可以模糊查询,例如like ‘138%’ v, S' q" @, a
根据业务区分使用char/varchar
' v9 [. i% U0 z字段长度固定,或者长度近似的业务场景,适合使用char,能够减少碎片,查询性能高4 e$ w8 b' E( _& Q4 s
字段长度相差较大,或者更新较少的业务场景,适合使用varchar。
+ C# h4 _+ P- B$ _7 G禁止在数据库中存储明文密码,把密码加密后存储
7 ^" s) S% X/ o2 E% W1 K尽量不使用外键; v! h) o. w" r+ W6 H, e
建议在应用层实现外键的逻辑, 外键与级联更新不适合高并发场景,降低插入性能,大并发下容易产生死锁。
. I" x' A3 Z% U6 f3 {, a核心表必须有行数据的创建时间和最后更新时间$ W. l5 V' K0 [
核心表(如用户表,金钱相关的表)必须有行数据的创建时间字段create_time和最后更新时间字段update_time,便于查问题。
( d% \; ~' `- V. |; C( l9 e) T( m8 R' V
四、索引设计规范
- X" {, H: u( ] G+ Y6 G( D) K3 sMySQL的查询速度依赖良好的索引设计,因此索引对于高性能至关重要,合理的索引会加快查询速度,不合理的索引会降低速度。; r/ P5 g4 j" c7 {+ u- b$ Z
索引其实就是一种数据结构(哈希表、树等),不同类型的索引有着不同的数据结构和功能。
* _9 U0 z* F% X+ |5 V7 {索引的作用是加速查询速度,维护数据的约束性(完整性、一致性)。对于加速查询,使用索引不一定是最好的选择,小表就直接全表扫描,中到大表就建索引,超大表就分区分表,要做好索引带来的好处和维护索引的成本之间的权衡。! V/ `0 X0 i$ S* E/ o! m. t& e
单表的索引数建议不超过 5 个,单个索引中的字段数建议不超过 5 个( C* K: v' O5 `1 R& T# ~+ @
太多就起不到过滤作用了,索引也占空间,管理也耗资源。
2 q$ M+ f, W$ P$ ]& o2 ^- d对字符串使用前缀索引,前缀索引长度不超过8个字符9 }. d6 U/ v3 h. x8 Z5 [$ x; ^
不要索引blob/text等字段,不要索引大型字段,这样做会让索引占用太多的存储空间。5 ~( O) a$ N0 S, K' ~
前缀索引就是对文本的前几个字符建立索引,前缀索引能有效减小索引文件的大小,提高索引的速度。但是前缀索引也有它的坏处:MySQL 不能在 ORDER BY 或 GROUP BY 中使用前缀索引,也不能把它们用作覆盖索引(Covering Index)
- N% c- G1 q- H! n4 U重要的SQL必须被索引,核心SQL优先考虑覆盖索引/ N9 @7 m0 [1 N! P' b+ z, h
UPDATE、DELETE 语句的 WHERE 条件列;ORDER BY、GROUP BY、DISTINCT 的字段;多表 JOIN 的字段。覆盖索引可以避免Innodb表进行索引的二次查询,把随机IO变成顺序IO,加快查询效率。
" y1 a/ O& a- j, |4 I4 d7 a" U联合索引区分度最大的字段放在前面9 G0 T, x' Z# q7 I0 E( Z8 A4 y! `' {& O
选择筛选性更优的字段放在最前面,比如userid,carid等,type,status等筛选性一般不建议放在最前面。索引根据左前缀原则,当建立一个联合索引(a,b,c),则查询条件里面只有包含(a)或(a,b)或(a,b,c)的时候才能走索引,(a,c)作为条件的时候只能使用到a列索引,所以这个时候要确定a的返回列一定不能太多,不然语句设计就不合理,(b,c)则不能走索引。, ]3 U( C; C) \, t. ^# ^8 o
业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引$ w" i2 f7 B4 B
不要以为唯一索引影响了 insert 速度,这个速度损耗可以忽略,但提高查找速度是明
, l, V) Y+ c! g4 o7 A2 ~+ }显的; 另外,即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。3 b# `" Z$ \+ q
InnoDB 和 MyISAM 存储引擎表,索引类型必须为BTREE
: ]& I. D/ a2 tMEMORY表可以根据需要选择 HASH 或者 BTREE 类型索引。
) p; @" _) Y2 G* q9 IMYSQL 中索引的限制4 L2 F0 s5 a9 i9 c
MYISAM 存储引擎索引长度的总和不能超过 1000 字节
. \3 [. V" ]4 o+ `& ]* ?BLOB 和 TEXT 类型的列只能创建前缀索引
3 H: q1 v( X! Q) |; l使用不等于 (!= 或者 <>) 的时候, MYSQL 无法使用索引
% I" s! O2 y4 H- h! F$ ] D过滤字段使用函数运算 (如 abs (column)) 后, MYSQL无法使用索引* W" i2 W" S& Q0 K& ]% p) |
join语句中join条件字段类型不一致的时候MYSQL无法使用索引. P( g/ J3 E& }
使用 LIKE 操作的时候如果条件以通配符开始 (如 ‘%abc…’)时, MYSQL无法使用索引
* g+ Y6 h5 e$ p- q( _9 w使用非等值查询的时候, MYSQL无法使用 Hash 索引。
6 o0 v" t& P/ [6 c* E* b
9 v7 ]1 ?# [1 v) V _9 e: G# ?五、SQL查询规范
5 m% @; z, g) Z% s, A+ x0 N+ f$ g- _按需索取拒绝 select *
+ ^0 c" N0 ~" g3 ~) I5 y无法索引覆盖,回表操作,IO增加;额外的内存负担,大量冷数据灌入innodb_buffer_pool_size,降低查询命中率;额外的网络传输开销。/ u% \) U4 X( }0 q5 u; m; F5 k3 D
超过三个表禁止 join- a! v, C4 m9 t p: A# V$ q$ }" B- T* B2 x
需要 join 的字段,数据类型必须绝对一致;多表关联查询时,保证被关联的字段有索引。
& N& T3 Z: o" t0 v涉及到复杂sql时,须用explain检验索引设计, C" [' K z5 F' w4 L' k- ]
对应同一列进行 or 判断时,使用 in 代替 or/ ~ L5 S7 q, M# B8 o
in 的值不要超过 500 个, in 操作可以更有效的利用索引,or 大多数情况下很少能利用到索引。& @, ~8 v, G* V" c1 h
in 操作能避免则避免
, Z W- y( A% \若实在避免不了,需要仔细评估 in 后边的集合元素数量,控制在 1000 个之内。
% K. P# d& j+ t0 A/ Y" f$ oWHERE从句中禁止对列进行函数转换和计算
/ O& z9 j" ^* j9 j9 l对列进行函数转换或计算时会导致无法使用索引。) o( }1 m) @. G1 P- @ ?: ~/ `6 T
count(列名)或 count(常量) 替代不了 count(*)
2 {9 x. S' k0 e, z W: B1 n, Qcount(*)是 SQL92 定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无,7 m) q$ M9 J% i9 b: |; }0 l V
count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。3 J! j2 ^" S9 z- f! G
不要使用UNION,推荐使用UNION ALL% ?+ m; |: j; T$ p
因为union all不需要去重,节省数据库资源,提高性能,另外子句个数限制在5个以内。
4 s! I: g8 v' D4 j a禁止使用 order by rand() 进行随机排序
0 c& e7 G; K5 D会把表中所有符合条件的数据装载到内存中,然后在内存中对所有数据根据随机生成的值进行排序,并且可能会对每一行都生成一个随机值,如果满足条件的数据集非常大,就会消耗大量的 CPU 和 IO 及内存资源,推荐在程序中获取一个随机值,然后从数据库中获取数据的方式。( E& L5 E8 v* Z2 W( \2 G% F
不得使用外键与级联,一切外键概念必须在应用层解决1 c- G' b+ ]+ F; ^( H
以学生和成绩的关系为例,学生表中的 student_id是主键,那么成绩表中的 student_id则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新, 即为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群; 级联更新是强阻塞,存在数据库更新风暴的风险;而且外键影响数据库的插入速度。
% i8 M, z9 W: e& A* t7 s使用合理的分页方式以提高分页效率+ I( }- G+ X: a) z
不推荐 SELECT * FROM table ORDER BY TIME DESC LIMIT 10000,10;2 e! n" O) d' A- o; m
原因:会导致大量的io,因为MySQL使用的是提前读取策略
: V. {5 r9 \- ~2 S4 _9 ^推荐:SELECT * FROM table WHERE TIME < last_TIME ORDER BY TIME DESC LIMIT 10.& z/ p- Y1 p1 J+ M: L( \7 i* Q/ w
SELECT * FROM table inner JOIN (SELECT id FROM table ORDER BY TIME LIMIT 10000,10) as t USING(id)
9 ]4 C, w- o, H" j6 o T; P# L( r+ R$ R- D( [7 _- R" S6 e
|
|