|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
一. 基础规范8 ]' s; i& G! A" k; C& g
所有表都需要添加注释7 r3 y$ b- O- e6 m6 N; j( U
使用comment从句添加表和列的备注,从一开始就做好数据字典维护。3 e4 M" P5 l: x; J4 a! U* {1 r
$ p7 b8 l- N; s! B, g
单表数据量建议控制在500万以内
) ] Q. M I& F d: A过大会给备份和恢复带来困难,可用历史数据归档(用于日志数据),分库分表(用于业务数据)等手段来控制数据量大小。
7 b- B5 ^+ b* A M
5 {" T7 B$ k8 P0 Z) _- J$ w. _不在数据库中存储图、文件等二进制数据7 C) s. ]* |5 R
图或文件IO操作耗时,通常存储于文件服务器,数据库只存储文件地址信息。
/ F/ \6 G' j& H. ~; ]% S' S( I2 B3 ?- H( l5 n4 P& I
尽量冷热数据分离,减小表的宽度/ y" r/ {3 B4 o! k
把不常用且占宽大的字段从热表中分离可独立存储,每一行数据大小不超过65535字节。) H3 g; m2 ]- W7 X3 D9 [
; i9 K3 [' O% n$ J& f5 Y/ A
禁止测试、开发环境直连线上数据库& X, v2 u1 o& i7 X9 u- J
! N8 j7 L# i* g. T! H
二、命名规范
( f/ d$ y, e% e. k4 `7 ~6 G3 l( z% F: Y; @, M! J& F' W6 \! S4 K; C
库名、表名、字段名必须使用小写字母,并采用下划线分割- K- i1 l2 A1 f' p4 R# v
MySQL配置参数lower_case_table_names=1,即库表名以小写存储,大小写不敏感。如果是0,则库表名以实际情况存储,大小写敏感;如果是2,以实际情况存储,但以小写比较。如果大小写混合使用,可能存在abc,Abc,ABC等多个表共存,容易导致混乱。& ?! p0 ], ]" i. Y' w, Q
( O, {9 r% y9 v1 A5 W" a库名、表名、字段名不要超过32个字符,需见名知意易于辨识* \9 @$ h- T" c
库名、表名、字段名支持最多64个字符,但为了减少传输量,禁止超过32个字符。6 }8 q; H% q) O0 Y, v
+ j& ~ z' |; R0 z+ e库名、表名、字段名禁止使用MySQL保留字1 i7 z' U6 K$ a9 L' r" l8 r! m
当库名、表名、字段名等含有保留字时,SQL语句必须用反引号引用属性名称,这将使SQL语句书写、SHELL脚本中变量的转义变得非常复杂。5 U J: H. \4 I4 @3 m/ L9 o& g
2 X, Y4 }1 z) V7 m7 ^9 @
备份库、表必须以bak为前缀,并以日期为后缀) v' ~: p8 C8 y& N# M) q( z) p: u' r
形如:bak_user_account_20190313
4 k$ s4 f3 Y% Q6 y g$ h9 @5 a" x" R& A& i0 G) f- a: N
索引的命名
6 y) I$ k+ y- B! E# R8 Y) D主键索引名为 pk_字段名;唯一索引名为 uk _字段名;普通索引名则为 idx_字段名。% i: v% Y) d7 [# k3 \6 z
4 F7 @7 f; ]9 x8 g7 ~4 ]4 {3 m# C
+ L$ |3 C4 ^7 [6 U三、字段设计规范6 H- Z1 X$ ?, ]: o: g
. {; J/ X. ?& [7 @ w2 G/ @
优先选择符合存储需要的最小的数据类型% k/ w) F J1 o$ u y0 k
列的字段类型越大,建立索引占据的空间就越大,使得一个页中的索引越少,IO次数增加,影响性能,能用int的就不用char或者varchar,能用tinyint的就不用int。状态status、类型type等字段推荐使用tinytint或者smallint类型节省存储空间;使用 UNSIGNED 存储非负数值;使用tinyint来代替 enum和boolean;存储 ip 最好用 int存储而非 char(15)。* H4 u9 E H- g4 Y0 }9 \6 @
" G( ] j% y. m5 A, U
不推荐使用blob,text等类型( m5 S+ P K! j6 o9 R
blob,text是为了存储极大的字符串而设计的数据类型,采用二进制与字符串方式存储,该数据类型不能设置默认值、不便于排序、不便于建立索引,varchar的性能会比text高很多,如果非要使用,建议将这种数据分离到单独的拓展表中。: e, U/ h/ `. Y5 s5 V `( k
& j, W! R+ T: l, E7 E% n3 L0 S
禁止使用字符串来存储日期型数据
. [( y! M) A* N, j一是无法使用日期函数计算比较,二是因为字符串存储要占更多的内存空间,datetime占8字节,timestamp是以int存储,占4字节。6 x- x- x4 v* @ h5 X: p$ V6 x
- K5 J3 f8 ~/ d( F9 ~6 _! E
用DECIMAL代替FLOAT和DOUBLE存储精确浮点数" W, v& I) {7 ^8 b4 x& Y
Decimal类型为精准浮点数,float 和 double 都有精度损失的问题,很可能在值的比较时,得到不正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。+ R( s$ X( \ [& l. \ C1 R, _
$ |+ T& t& W3 m5 d% Q
必须把字段定义为NOT NULL并设默认值
. j, [% e! _ D' v/ x$ Q# C一是NULL需要更多的存储空间,二是NULL的列可能索引实效,三是NULL只能采用IS NULL或者IS NOT NULL,而在=/!=/in/not in时有大坑。
: {3 W, b: g3 H3 D' e8 c' h, o G# D: w0 u3 ~, x t0 I
使用varchar(20)存储手机号,不要使用整数
7 M R8 g- J% |- i$ I \; _: T一是牵扯到国家代号,可能出现+/-/()等字符,例如+86,二是varchar可以模糊查询,例如like ‘138%’
4 e u- |4 M1 {& M2 l, ~, T( h7 d9 Y/ j( N" I- U( E
根据业务区分使用char/varchar; z( c/ q+ v4 ^+ ^3 U0 U
字段长度固定,或者长度近似的业务场景,适合使用char,能够减少碎片,查询性能高
5 i" a2 P5 I+ J- }0 C2 C3 L# m' }8 H0 T5 I8 a
字段长度相差较大,或者更新较少的业务场景,适合使用varchar。
- c% A0 ] {9 `" r% K5 n+ w5 B# A5 j }
禁止在数据库中存储明文密码,把密码加密后存储
9 |2 b, y# n$ C) V8 s: C% d# V4 b尽量不使用外键0 g3 ?. |, ^- w5 G) O
建议在应用层实现外键的逻辑, 外键与级联更新不适合高并发场景,降低插入性能,大并发下容易产生死锁。
8 }; G( L9 s7 Z/ e/ }( {$ B
1 `, x( Z6 f$ A9 _- O核心表必须有行数据的创建时间和最后更新时间
5 M* L; y1 T: d! b8 k核心表(如用户表,金钱相关的表)必须有行数据的创建时间字段create_time和最后更新时间字段update_time,便于查问题。
5 a$ n" a2 _0 a0 y/ L6 h' J$ U! b$ s
& n! h, {9 V1 m- M# ]% N四、索引设计规范( U: M* x/ G0 J+ Y2 j9 h
9 Z9 Y, t5 _1 a0 ]6 a4 n2 v$ g% I
MySQL的查询速度依赖良好的索引设计,因此索引对于高性能至关重要,合理的索引会加快查询速度,不合理的索引会降低速度。
6 r; F- f4 b+ k# A+ S9 ^% a; ]9 v X$ a+ R
索引其实就是一种数据结构(哈希表、树等),不同类型的索引有着不同的数据结构和功能。
^% p" X' B6 ?/ H9 M* L+ G) n8 G2 g: w# _, F
索引的作用是加速查询速度,维护数据的约束性(完整性、一致性)。对于加速查询,使用索引不一定是最好的选择,小表就直接全表扫描,中到大表就建索引,超大表就分区分表,要做好索引带来的好处和维护索引的成本之间的权衡。9 U t7 X$ y* N1 D: K9 a
# p' Q. l0 w# k' |' o8 x1 Z5 n3 i单表的索引数建议不超过 5 个,单个索引中的字段数建议不超过 5 个
8 X! A2 y6 v% U3 g- v太多就起不到过滤作用了,索引也占空间,管理也耗资源。
5 G7 g! e7 v9 F; W( R) F0 m$ _
0 h% J1 A; q0 l# k对字符串使用前缀索引,前缀索引长度不超过8个字符: t# I# r$ R! t7 q0 M e N
不要索引blob/text等字段,不要索引大型字段,这样做会让索引占用太多的存储空间。
: V0 C0 z- T+ T* A9 j9 ~$ b
) v9 O6 t, B! J+ S前缀索引就是对文本的前几个字符建立索引,前缀索引能有效减小索引文件的大小,提高索引的速度。但是前缀索引也有它的坏处:MySQL 不能在 ORDER BY 或 GROUP BY 中使用前缀索引,也不能把它们用作覆盖索引(Covering Index) {; m0 P% z2 ~( y9 f
; E4 ?9 I+ O, P7 ~2 s重要的SQL必须被索引,核心SQL优先考虑覆盖索引! J# _" i. ~; _; ]
UPDATE、DELETE 语句的 WHERE 条件列;ORDER BY、GROUP BY、DISTINCT 的字段;多表 JOIN 的字段。覆盖索引可以避免Innodb表进行索引的二次查询,把随机IO变成顺序IO,加快查询效率。
% z2 I v$ q: i5 S2 r6 t5 ]- `' v1 ]1 h1 `$ i: [
联合索引区分度最大的字段放在前面
* Z0 _; j, u0 B$ r选择筛选性更优的字段放在最前面,比如userid,carid等,type,status等筛选性一般不建议放在最前面。索引根据左前缀原则,当建立一个联合索引(a,b,c),则查询条件里面只有包含(a)或(a,b)或(a,b,c)的时候才能走索引,(a,c)作为条件的时候只能使用到a列索引,所以这个时候要确定a的返回列一定不能太多,不然语句设计就不合理,(b,c)则不能走索引。
3 o! Q; M. }3 u0 v" j: r1 x L A' Z4 S3 B4 f0 [2 e. Y5 e
业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引
6 z! M2 @- B9 m' I8 z/ g不要以为唯一索引影响了 insert 速度,这个速度损耗可以忽略,但提高查找速度是明
$ O8 s6 i9 R# ~: ]6 p
5 u+ ~: ]) e9 v, p& `7 n4 @显的; 另外,即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。
0 H& E& t! R" X# f+ X% W8 t
1 K; M/ {- ]6 {* d* jInnoDB 和 MyISAM 存储引擎表,索引类型必须为BTREE
2 h, s# N3 a C1 Q& x" MMEMORY表可以根据需要选择 HASH 或者 BTREE 类型索引。1 L" B5 A% N7 v3 O
5 Z8 L' O0 v' F' |
MYSQL 中索引的限制1 e4 P0 J& C( s! v0 k" b. i
MYISAM 存储引擎索引长度的总和不能超过 1000 字节( R4 l5 A: v$ O$ t' B" L1 f* l
: M$ f' }+ ]) C4 u2 }$ m$ }BLOB 和 TEXT 类型的列只能创建前缀索引
: B7 B$ m+ U( }$ w. s
+ ~) Z* J2 @* Z: N p5 u7 ^$ X% y使用不等于 (!= 或者 <>) 的时候, MYSQL 无法使用索引
9 N9 N6 r5 \/ k1 j* H9 r) X1 B0 E
过滤字段使用函数运算 (如 abs (column)) 后, MYSQL无法使用索引. \3 V! O( C( c3 R- A
. T( k# [- J# G- K( o2 Q0 z
join语句中join条件字段类型不一致的时候MYSQL无法使用索引1 O2 E" B2 b: r+ N9 ] J6 r
! l) J% N1 i; t7 V" v1 g使用 LIKE 操作的时候如果条件以通配符开始 (如 ‘%abc…’)时, MYSQL无法使用索引
3 o: w9 |( I5 `& f% d2 d' ^$ d. G! ~1 i) U* h! T( {8 d# J# |3 {
使用非等值查询的时候, MYSQL无法使用 Hash 索引。 E0 {5 @7 P+ f5 _1 B
$ P; {/ Z8 t9 E j! O: r
5 n; r# J" p4 y$ k- R% E% [2 w2 O" P五、SQL查询规范9 m) J1 k6 X: Q4 x1 e7 U
; q- q5 d+ |! X- ?2 x1 d4 |# \$ A
按需索取拒绝 select *
2 @* C7 s, ?) b* T0 m* D- |6 R无法索引覆盖,回表操作,IO增加;额外的内存负担,大量冷数据灌入innodb_buffer_pool_size,降低查询命中率;额外的网络传输开销。' k3 D/ `7 d* M7 o& T, Y8 O4 p
1 g, R! s% O2 e+ g/ X9 o1 q超过三个表禁止 join
, c- i& n) _9 P9 u% |/ e需要 join 的字段,数据类型必须绝对一致;多表关联查询时,保证被关联的字段有索引。! ~) N E* i1 x3 q( Y' a1 C
5 Q5 L) I ~2 I) r6 z8 n7 r涉及到复杂sql时,须用explain检验索引设计( a/ w7 n$ M6 n5 e
对应同一列进行 or 判断时,使用 in 代替 or2 M4 b3 a6 I' d' x3 `2 ]: d
in 的值不要超过 500 个, in 操作可以更有效的利用索引,or 大多数情况下很少能利用到索引。& r/ \( H* ^# w. v" l
4 b9 n- X b& V
in 操作能避免则避免* ~2 x5 z& m; ^* b
若实在避免不了,需要仔细评估 in 后边的集合元素数量,控制在 1000 个之内。
A& i% W# z3 C% x( }% E
" B/ M( |$ O {WHERE从句中禁止对列进行函数转换和计算
% N7 }( ?" q- u4 W4 n" G5 Z% u& j. y7 c对列进行函数转换或计算时会导致无法使用索引。
, n' u0 v J; N- e% w& c/ R1 Y( X% I6 V5 ]( a3 N4 E4 A
count(列名)或 count(常量) 替代不了 count(*)
3 A" M3 U. I- N0 k6 S3 N6 Ncount(*)是 SQL92 定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无,
2 m+ Z" s! n) r& H$ w, E" d% i( g; Q$ B! x
count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。
2 Q* R9 H Y1 N6 e- W8 D/ j
: Q( X& F/ H/ |4 E7 l3 T2 {不要使用UNION,推荐使用UNION ALL
6 w9 E( P0 }8 p3 ]因为union all不需要去重,节省数据库资源,提高性能,另外子句个数限制在5个以内。
& C5 f- V9 n+ r+ H S. |4 ~2 I5 M" e* p1 y. H4 Z. _
禁止使用 order by rand() 进行随机排序
0 G( p! n: _, ?! M% l* X, ^会把表中所有符合条件的数据装载到内存中,然后在内存中对所有数据根据随机生成的值进行排序,并且可能会对每一行都生成一个随机值,如果满足条件的数据集非常大,就会消耗大量的 CPU 和 IO 及内存资源,推荐在程序中获取一个随机值,然后从数据库中获取数据的方式。: t3 l Q( a( K
/ U! B! K0 H# R6 I& d& w. L不得使用外键与级联,一切外键概念必须在应用层解决; T) |5 d- g9 w" w1 s7 b7 D2 }4 D
以学生和成绩的关系为例,学生表中的 student_id是主键,那么成绩表中的 student_id则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新, 即为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群; 级联更新是强阻塞,存在数据库更新风暴的风险;而且外键影响数据库的插入速度。8 L- O3 O# U5 v* A! _0 E
% `- |: u! o6 { L5 Q" @
使用合理的分页方式以提高分页效率
) A$ {8 ~- X4 C; R不推荐 SELECT * FROM table ORDER BY TIME DESC LIMIT 10000,10; P8 v$ c8 H/ q4 s+ T1 T( O1 e1 [
8 n `* Y# K6 [ X" _
原因:会导致大量的io,因为MySQL使用的是提前读取策略
3 ^& }8 z$ W7 y. k0 i2 l
4 g- l; L5 y% X9 }0 P1 Q; |推荐:SELECT * FROM table WHERE TIME < last_TIME ORDER BY TIME DESC LIMIT 10.
6 r3 j$ w- Z, s ?; L
# E; W4 R" t. q# U) _: ?SELECT * FROM table inner JOIN (SELECT id FROM table ORDER BY TIME LIMIT 10000,10) as t USING(id)$ M2 ?: m$ [& J" B
2 k- j/ e2 Y) s, y" m
( s/ k. s, ^; R8 {& p |
|