|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
一. 基础规范
* q" s( h, `: h' m8 C所有表都需要添加注释
/ n( i: K7 t0 }使用comment从句添加表和列的备注,从一开始就做好数据字典维护。
* j3 R$ J+ K) I
. j4 {2 a0 s! E V4 ^' V3 Q单表数据量建议控制在500万以内
. @( w3 C6 Q; y过大会给备份和恢复带来困难,可用历史数据归档(用于日志数据),分库分表(用于业务数据)等手段来控制数据量大小。
3 ?: j7 A2 Z7 n) g! `& b8 K3 e7 T2 P$ c4 n# \/ P/ O! V
不在数据库中存储图、文件等二进制数据5 E4 w8 u' O. }; i$ B/ c
图或文件IO操作耗时,通常存储于文件服务器,数据库只存储文件地址信息。8 a: Z$ n* e7 d
# |7 D3 c& I' n' n
尽量冷热数据分离,减小表的宽度$ D T2 }8 M2 ] W* J7 p) x" m7 j9 B
把不常用且占宽大的字段从热表中分离可独立存储,每一行数据大小不超过65535字节。
- J8 N" w: p+ x4 ?" `! C# m( k8 j
# ^( ]6 p A: F2 U3 m; i0 d# c禁止测试、开发环境直连线上数据库) q& _; f- |/ A4 d* p9 ?& q
) L4 b% C G( F* }+ i
二、命名规范
% d( v8 g" h3 N6 k/ i, F6 C
7 V4 |# q/ ?( E- O* ?! H库名、表名、字段名必须使用小写字母,并采用下划线分割
1 z: r; U( b3 Y# |MySQL配置参数lower_case_table_names=1,即库表名以小写存储,大小写不敏感。如果是0,则库表名以实际情况存储,大小写敏感;如果是2,以实际情况存储,但以小写比较。如果大小写混合使用,可能存在abc,Abc,ABC等多个表共存,容易导致混乱。
1 K( E3 }# B$ F! L
j2 a* U6 Z, v1 _# [. C库名、表名、字段名不要超过32个字符,需见名知意易于辨识7 w. x% s" c. _# M- R# h
库名、表名、字段名支持最多64个字符,但为了减少传输量,禁止超过32个字符。
3 {5 F0 T/ G4 |5 d) W" z' D6 C$ A9 B+ m1 F
库名、表名、字段名禁止使用MySQL保留字: U- t; [& b- l* c
当库名、表名、字段名等含有保留字时,SQL语句必须用反引号引用属性名称,这将使SQL语句书写、SHELL脚本中变量的转义变得非常复杂。" a# s+ B" J8 W1 [" @% I" H6 t' H* d2 Z
' h) C( ?/ I1 B( Q" N0 j4 g: S7 ~
备份库、表必须以bak为前缀,并以日期为后缀
U' p2 h C- U1 A3 d1 I形如:bak_user_account_201903136 ~" _/ V ]: R
# z- t" S; i2 s$ \3 P索引的命名
! N& u3 _7 q- \# c$ H主键索引名为 pk_字段名;唯一索引名为 uk _字段名;普通索引名则为 idx_字段名。3 ^! A9 Q% G/ K+ m) Z
, F( o) Z1 ]! n9 y/ u! X
# z: W' Q0 n- y" ^% D) K三、字段设计规范8 k8 U. {$ M8 W% z6 P
; I" G# S) E$ a. B# w, K优先选择符合存储需要的最小的数据类型* v4 }% \; Q y |! X6 b
列的字段类型越大,建立索引占据的空间就越大,使得一个页中的索引越少,IO次数增加,影响性能,能用int的就不用char或者varchar,能用tinyint的就不用int。状态status、类型type等字段推荐使用tinytint或者smallint类型节省存储空间;使用 UNSIGNED 存储非负数值;使用tinyint来代替 enum和boolean;存储 ip 最好用 int存储而非 char(15)。
- h2 {% u0 y/ d( K3 M4 D+ B: R3 S& \4 h
不推荐使用blob,text等类型
# d" q \0 r$ {blob,text是为了存储极大的字符串而设计的数据类型,采用二进制与字符串方式存储,该数据类型不能设置默认值、不便于排序、不便于建立索引,varchar的性能会比text高很多,如果非要使用,建议将这种数据分离到单独的拓展表中。
) v8 L. n' K5 R4 O9 m* p! S6 {. e6 b- d$ {
禁止使用字符串来存储日期型数据3 U( n6 z6 C: O# p, [
一是无法使用日期函数计算比较,二是因为字符串存储要占更多的内存空间,datetime占8字节,timestamp是以int存储,占4字节。. W: F+ I# v3 z; K8 Y
; [- g+ _0 j) t4 C( Z. [
用DECIMAL代替FLOAT和DOUBLE存储精确浮点数
8 J7 m9 a" M. h+ _- `: `( hDecimal类型为精准浮点数,float 和 double 都有精度损失的问题,很可能在值的比较时,得到不正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。
; L0 C+ i# r. ^, k# Q
# l* f& o3 i' w! p必须把字段定义为NOT NULL并设默认值" ?/ m5 H1 f' Y% [* Y6 v" V: q
一是NULL需要更多的存储空间,二是NULL的列可能索引实效,三是NULL只能采用IS NULL或者IS NOT NULL,而在=/!=/in/not in时有大坑。1 k! I7 I7 i R3 u. V
; j E* {% S" E; F* z& H! o
使用varchar(20)存储手机号,不要使用整数" Q. v8 ~' m/ q; R* l) A( V' w
一是牵扯到国家代号,可能出现+/-/()等字符,例如+86,二是varchar可以模糊查询,例如like ‘138%’$ k3 d+ w! m( s3 p2 k3 c/ W0 D, E
& C8 H. V( {* x8 j) ]0 @' `根据业务区分使用char/varchar/ P) D8 c* U/ `1 f5 I
字段长度固定,或者长度近似的业务场景,适合使用char,能够减少碎片,查询性能高3 f: ~% O$ Q+ z5 G0 g0 P! ]! H9 G
2 D0 P# b( ^1 y$ a' \8 o. n5 u字段长度相差较大,或者更新较少的业务场景,适合使用varchar。# W& r u# _, @" r$ V8 K# U+ J
6 y5 f- o# T7 u( _6 _& @1 K" j
禁止在数据库中存储明文密码,把密码加密后存储) `* H8 d, x. ~5 ~3 E' Z# \
尽量不使用外键3 C: b+ T9 j& ^
建议在应用层实现外键的逻辑, 外键与级联更新不适合高并发场景,降低插入性能,大并发下容易产生死锁。' k1 M x9 J- Y6 J) m3 ^7 d& N
+ J4 O4 a+ Z ^- U6 e( k1 Y
核心表必须有行数据的创建时间和最后更新时间
2 l3 g* b7 _* N- @; p7 M核心表(如用户表,金钱相关的表)必须有行数据的创建时间字段create_time和最后更新时间字段update_time,便于查问题。
6 U6 w) B7 E5 X) t0 G3 Z& U" l4 K
; N# R+ Z' A$ r" I
四、索引设计规范# C( l$ v% h! Z6 [9 i& i
' P1 b5 x' a* E1 i: gMySQL的查询速度依赖良好的索引设计,因此索引对于高性能至关重要,合理的索引会加快查询速度,不合理的索引会降低速度。
$ @9 E! @9 j, g0 f J5 ~1 Z6 q+ f) E' t1 h/ [+ K f+ U
索引其实就是一种数据结构(哈希表、树等),不同类型的索引有着不同的数据结构和功能。
' E/ l/ m& O% o6 N: i: W( L0 h0 B$ B
2 _' J3 U3 x5 G3 M" c/ l& K索引的作用是加速查询速度,维护数据的约束性(完整性、一致性)。对于加速查询,使用索引不一定是最好的选择,小表就直接全表扫描,中到大表就建索引,超大表就分区分表,要做好索引带来的好处和维护索引的成本之间的权衡。
! n" N) ^' e8 C
, }6 E [' b* X: G! D$ E! q单表的索引数建议不超过 5 个,单个索引中的字段数建议不超过 5 个
' G* y# a7 O U6 V. _$ z太多就起不到过滤作用了,索引也占空间,管理也耗资源。' l o! V4 q$ {8 Z- y$ M- c
1 I3 l# [7 h/ \
对字符串使用前缀索引,前缀索引长度不超过8个字符6 o# Z$ L( [, [! }4 F4 S# N. X1 f& I
不要索引blob/text等字段,不要索引大型字段,这样做会让索引占用太多的存储空间。4 ?0 o& o1 B! }! u
( x0 Y4 }4 F1 v5 e @前缀索引就是对文本的前几个字符建立索引,前缀索引能有效减小索引文件的大小,提高索引的速度。但是前缀索引也有它的坏处:MySQL 不能在 ORDER BY 或 GROUP BY 中使用前缀索引,也不能把它们用作覆盖索引(Covering Index)
4 j7 h2 j, g p/ E9 _% h6 ~% y$ z8 T$ _
重要的SQL必须被索引,核心SQL优先考虑覆盖索引
) z: @4 |" C9 v7 j. JUPDATE、DELETE 语句的 WHERE 条件列;ORDER BY、GROUP BY、DISTINCT 的字段;多表 JOIN 的字段。覆盖索引可以避免Innodb表进行索引的二次查询,把随机IO变成顺序IO,加快查询效率。
; |$ v: `! X4 {1 v0 o% Z
& o. _# d+ S I$ P( ]' v联合索引区分度最大的字段放在前面
9 G! L/ W& i n! A4 x( {) d选择筛选性更优的字段放在最前面,比如userid,carid等,type,status等筛选性一般不建议放在最前面。索引根据左前缀原则,当建立一个联合索引(a,b,c),则查询条件里面只有包含(a)或(a,b)或(a,b,c)的时候才能走索引,(a,c)作为条件的时候只能使用到a列索引,所以这个时候要确定a的返回列一定不能太多,不然语句设计就不合理,(b,c)则不能走索引。
{( @" D2 ?4 U8 L; ]
; Y; B8 R9 [+ o, r& [9 s业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引6 x$ P% @4 W4 S# }+ U7 y8 u
不要以为唯一索引影响了 insert 速度,这个速度损耗可以忽略,但提高查找速度是明, g/ T. r: Q, W( l0 h* M. \
; x! O' I! w! Q- U# S& {" d显的; 另外,即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。
$ }( F% W* L% M
2 I. {) q; g% P8 n( ~InnoDB 和 MyISAM 存储引擎表,索引类型必须为BTREE
2 b! l$ P! r0 ~; l0 zMEMORY表可以根据需要选择 HASH 或者 BTREE 类型索引。
( O2 }: J9 ?! j6 f# |
! d1 A* Z4 i! qMYSQL 中索引的限制: \1 m: O, n1 V* o# L4 k
MYISAM 存储引擎索引长度的总和不能超过 1000 字节
8 m- a5 T x* Z6 N3 C: W3 ~* ~& _! T! H! h, Y$ ]/ c6 c) x# K/ t
BLOB 和 TEXT 类型的列只能创建前缀索引: D$ i c( g0 g# L: z& A
+ P# e* ~1 |; B7 Q使用不等于 (!= 或者 <>) 的时候, MYSQL 无法使用索引
! m2 M- p. O# ?: D( W3 j4 `) ]; i' L% z1 [7 ~- J
过滤字段使用函数运算 (如 abs (column)) 后, MYSQL无法使用索引0 }$ ]1 I9 f% I3 p3 Z/ V& `$ M2 h
0 l9 N9 g* c8 I
join语句中join条件字段类型不一致的时候MYSQL无法使用索引
0 ?1 }6 ^6 `) Z: m: i/ T' W n$ S3 v0 f" D3 p4 c* F
使用 LIKE 操作的时候如果条件以通配符开始 (如 ‘%abc…’)时, MYSQL无法使用索引
) U; y7 @2 I( W5 s% y0 y( f, I+ J2 [0 j- I+ r$ [
使用非等值查询的时候, MYSQL无法使用 Hash 索引。 y- D n5 \$ Y: }7 i- \/ D9 G( O
7 e$ C* V: p; u/ b. U! S _
) q% w, `+ p' U! v9 Q8 R5 t
五、SQL查询规范
5 a" p( |! d f
5 V7 P! T4 _5 j按需索取拒绝 select *
% O: }* q3 ?/ h" z- r8 W无法索引覆盖,回表操作,IO增加;额外的内存负担,大量冷数据灌入innodb_buffer_pool_size,降低查询命中率;额外的网络传输开销。6 A9 F, Y+ e$ r
) T: R7 j, ?4 u o. I
超过三个表禁止 join
3 \3 t; C- R( a& Z: Z/ \+ V需要 join 的字段,数据类型必须绝对一致;多表关联查询时,保证被关联的字段有索引。
3 n1 K2 T1 l1 b/ p' o6 y5 b" V, M2 R$ |* c
涉及到复杂sql时,须用explain检验索引设计3 T8 w2 q' Z) `# A' |! m
对应同一列进行 or 判断时,使用 in 代替 or
4 _/ W) \7 d: U! ?8 Min 的值不要超过 500 个, in 操作可以更有效的利用索引,or 大多数情况下很少能利用到索引。/ ~0 M( s' F7 P2 \# O! n
; J/ ^3 E8 N5 f) q! I1 ain 操作能避免则避免8 O. }. K6 U( [ |/ d) z9 R4 f* Z
若实在避免不了,需要仔细评估 in 后边的集合元素数量,控制在 1000 个之内。
2 |& U/ c2 u3 K4 F0 L( |% {6 w; z$ z
WHERE从句中禁止对列进行函数转换和计算- M. T6 D# Z: j7 R- _
对列进行函数转换或计算时会导致无法使用索引。1 i, ~: ?, ]8 ]2 H
& A' ^" n8 K+ W/ o) Zcount(列名)或 count(常量) 替代不了 count(*)
- t. q# h& ]8 @0 p2 G1 y9 tcount(*)是 SQL92 定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无,
. \5 d2 O; P: ~: ]% `3 |! y) @2 p. L+ v' k
count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。6 U# `/ F. Q. b
; C: C$ O/ N/ g) }$ s7 W: ] J
不要使用UNION,推荐使用UNION ALL
7 q, O9 R; Y0 s3 o" {因为union all不需要去重,节省数据库资源,提高性能,另外子句个数限制在5个以内。7 M0 c5 v# a/ |9 O" p$ q3 S; P
2 c7 Y, C. i" f+ [' Q6 \3 m
禁止使用 order by rand() 进行随机排序! `" @( ~. ^) D
会把表中所有符合条件的数据装载到内存中,然后在内存中对所有数据根据随机生成的值进行排序,并且可能会对每一行都生成一个随机值,如果满足条件的数据集非常大,就会消耗大量的 CPU 和 IO 及内存资源,推荐在程序中获取一个随机值,然后从数据库中获取数据的方式。0 k# E2 \. k! n! r
/ p% h# Q1 c' q* d V
不得使用外键与级联,一切外键概念必须在应用层解决
) W/ |" ?- v( K9 ^# N" ~1 `7 a以学生和成绩的关系为例,学生表中的 student_id是主键,那么成绩表中的 student_id则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新, 即为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群; 级联更新是强阻塞,存在数据库更新风暴的风险;而且外键影响数据库的插入速度。
0 L# ~& j( [" T% m3 ~$ F; J/ s' k, N' T, l0 M
使用合理的分页方式以提高分页效率
7 y1 i) _6 R, d% I9 N不推荐 SELECT * FROM table ORDER BY TIME DESC LIMIT 10000,10;
6 A7 i S3 c7 n B* Y2 }( ~1 p
6 I, r6 n- W/ z原因:会导致大量的io,因为MySQL使用的是提前读取策略
6 B) F2 R, V; `8 j5 e$ z# n* s' r5 d( L R
推荐:SELECT * FROM table WHERE TIME < last_TIME ORDER BY TIME DESC LIMIT 10.
- e! x$ d: f! H3 L: s% J& l) K& R. N
SELECT * FROM table inner JOIN (SELECT id FROM table ORDER BY TIME LIMIT 10000,10) as t USING(id)) c# v4 s8 X3 _$ o2 r
$ N" B8 f/ r1 |& {, d3 Y- ?" n
' m. O" U- i" t& G) W2 j |
|