|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
一. 基础规范
# `3 b8 M! j7 N9 u. Q% B* t I所有表都需要添加注释* k0 T+ p( G( h/ b, K2 G4 ^
使用comment从句添加表和列的备注,从一开始就做好数据字典维护。
5 t$ p. o' |2 W% }) h# W单表数据量建议控制在500万以内2 x+ o4 O3 }3 _- s* g5 s! h
过大会给备份和恢复带来困难,可用历史数据归档(用于日志数据),分库分表(用于业务数据)等手段来控制数据量大小。5 f4 P' `6 Q) c# s+ Y% l
不在数据库中存储图、文件等二进制数据
, i- J( M& Y: z7 A图或文件IO操作耗时,通常存储于文件服务器,数据库只存储文件地址信息。
1 t Y9 l" n1 |9 e7 k! P, a% w8 }8 z" J尽量冷热数据分离,减小表的宽度
# e* ]3 B( @5 }9 H- O* C把不常用且占宽大的字段从热表中分离可独立存储,每一行数据大小不超过65535字节。% y; T2 v( ^9 A# c1 f( c
禁止测试、开发环境直连线上数据库
F7 G* W$ d' X/ j K二、命名规范
9 E7 N% h- F5 N4 i0 T! k' d" k库名、表名、字段名必须使用小写字母,并采用下划线分割( x& ~2 d! l* _* X& k* a' w
MySQL配置参数lower_case_table_names=1,即库表名以小写存储,大小写不敏感。如果是0,则库表名以实际情况存储,大小写敏感;如果是2,以实际情况存储,但以小写比较。如果大小写混合使用,可能存在abc,Abc,ABC等多个表共存,容易导致混乱。% D: l$ |' @2 r
库名、表名、字段名不要超过32个字符,需见名知意易于辨识
4 {# q0 s* [ h9 |" {* v8 s6 s库名、表名、字段名支持最多64个字符,但为了减少传输量,禁止超过32个字符。1 w4 C. R/ o2 H, l
库名、表名、字段名禁止使用MySQL保留字
X' B0 ~" Z- F6 F$ B6 K当库名、表名、字段名等含有保留字时,SQL语句必须用反引号引用属性名称,这将使SQL语句书写、SHELL脚本中变量的转义变得非常复杂。
2 ~% a) P9 G* D1 j备份库、表必须以bak为前缀,并以日期为后缀
5 C k! H0 U8 T2 h- `形如:bak_user_account_20190313
3 Y+ H7 _, s( x! ^, u索引的命名
) u9 t& p. _! V0 b主键索引名为 pk_字段名;唯一索引名为 uk _字段名;普通索引名则为 idx_字段名。* {1 v0 }6 F4 b3 j
C1 b) b7 c! c
三、字段设计规范) N7 o6 B% P1 [+ p, q, E5 R
优先选择符合存储需要的最小的数据类型
5 [- C$ [: i8 w. H3 p2 {8 B( O列的字段类型越大,建立索引占据的空间就越大,使得一个页中的索引越少,IO次数增加,影响性能,能用int的就不用char或者varchar,能用tinyint的就不用int。状态status、类型type等字段推荐使用tinytint或者smallint类型节省存储空间;使用 UNSIGNED 存储非负数值;使用tinyint来代替 enum和boolean;存储 ip 最好用 int存储而非 char(15)。
$ e* U* M: e p8 p$ n不推荐使用blob,text等类型# W: v! r% j! j; H t
blob,text是为了存储极大的字符串而设计的数据类型,采用二进制与字符串方式存储,该数据类型不能设置默认值、不便于排序、不便于建立索引,varchar的性能会比text高很多,如果非要使用,建议将这种数据分离到单独的拓展表中。
0 }$ Q& g8 u9 O+ P Y+ ]4 s禁止使用字符串来存储日期型数据
, r' u \+ r, C2 F/ d0 z一是无法使用日期函数计算比较,二是因为字符串存储要占更多的内存空间,datetime占8字节,timestamp是以int存储,占4字节。/ E0 {' _+ j- J) a" S" O
用DECIMAL代替FLOAT和DOUBLE存储精确浮点数
! D8 ?* Q, a- S6 V J6 Z2 `Decimal类型为精准浮点数,float 和 double 都有精度损失的问题,很可能在值的比较时,得到不正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。
. L2 o0 }( Z, `+ t, S) G) d必须把字段定义为NOT NULL并设默认值# p6 Y/ Y, `! k5 v, |4 B
一是NULL需要更多的存储空间,二是NULL的列可能索引实效,三是NULL只能采用IS NULL或者IS NOT NULL,而在=/!=/in/not in时有大坑。! S) j9 U; A! f, o: `; i
使用varchar(20)存储手机号,不要使用整数7 @0 d6 }9 R" I* L: t" \7 z, b
一是牵扯到国家代号,可能出现+/-/()等字符,例如+86,二是varchar可以模糊查询,例如like ‘138%’3 P: u& F+ v8 G/ E- A$ C @
根据业务区分使用char/varchar
, F, D5 D3 ^& u, b1 u0 A字段长度固定,或者长度近似的业务场景,适合使用char,能够减少碎片,查询性能高
+ `6 E' `5 S# d7 B字段长度相差较大,或者更新较少的业务场景,适合使用varchar。
: T6 ]+ Y- `2 o3 j禁止在数据库中存储明文密码,把密码加密后存储- V. L6 e' [4 O. ?1 s
尽量不使用外键2 `# S7 @) G1 v$ A; j5 ~
建议在应用层实现外键的逻辑, 外键与级联更新不适合高并发场景,降低插入性能,大并发下容易产生死锁。# X/ e ~* \" w: x+ I# z' c
核心表必须有行数据的创建时间和最后更新时间4 _( y( ]7 e' B: Q2 V' t) i' {
核心表(如用户表,金钱相关的表)必须有行数据的创建时间字段create_time和最后更新时间字段update_time,便于查问题。
! g( S9 |7 ^8 k! R) B
2 m) W5 w4 R" }8 S7 i! d8 Q) s四、索引设计规范
3 b* s1 [1 {- Z: I7 E0 {( tMySQL的查询速度依赖良好的索引设计,因此索引对于高性能至关重要,合理的索引会加快查询速度,不合理的索引会降低速度。
8 S& e$ R: d4 _8 }索引其实就是一种数据结构(哈希表、树等),不同类型的索引有着不同的数据结构和功能。
, U, @+ w; e7 |7 L0 h7 H/ K索引的作用是加速查询速度,维护数据的约束性(完整性、一致性)。对于加速查询,使用索引不一定是最好的选择,小表就直接全表扫描,中到大表就建索引,超大表就分区分表,要做好索引带来的好处和维护索引的成本之间的权衡。
" l4 v% f6 j8 u9 \" B单表的索引数建议不超过 5 个,单个索引中的字段数建议不超过 5 个2 {. t' `9 X5 Y2 n8 Q4 o
太多就起不到过滤作用了,索引也占空间,管理也耗资源。' [7 J o2 p4 r- A5 `: S
对字符串使用前缀索引,前缀索引长度不超过8个字符
7 O- B L# b& X6 L: e- x# `& ~* Q不要索引blob/text等字段,不要索引大型字段,这样做会让索引占用太多的存储空间。
7 ?# a& l0 B' I- v$ p% U& f前缀索引就是对文本的前几个字符建立索引,前缀索引能有效减小索引文件的大小,提高索引的速度。但是前缀索引也有它的坏处:MySQL 不能在 ORDER BY 或 GROUP BY 中使用前缀索引,也不能把它们用作覆盖索引(Covering Index)
+ ]: f7 R: N: Y7 Y% ~重要的SQL必须被索引,核心SQL优先考虑覆盖索引$ d* T. D1 E2 \" e6 J
UPDATE、DELETE 语句的 WHERE 条件列;ORDER BY、GROUP BY、DISTINCT 的字段;多表 JOIN 的字段。覆盖索引可以避免Innodb表进行索引的二次查询,把随机IO变成顺序IO,加快查询效率。. L% I: y$ g X. G
联合索引区分度最大的字段放在前面
9 \" w4 e2 T0 R& r7 ~选择筛选性更优的字段放在最前面,比如userid,carid等,type,status等筛选性一般不建议放在最前面。索引根据左前缀原则,当建立一个联合索引(a,b,c),则查询条件里面只有包含(a)或(a,b)或(a,b,c)的时候才能走索引,(a,c)作为条件的时候只能使用到a列索引,所以这个时候要确定a的返回列一定不能太多,不然语句设计就不合理,(b,c)则不能走索引。
' k3 H( A, W. @5 ^) w X q: d5 g业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引
( q6 f V5 [% Y a3 o不要以为唯一索引影响了 insert 速度,这个速度损耗可以忽略,但提高查找速度是明5 C M2 x! [5 r- L* f
显的; 另外,即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。. L. V5 k* y! |; b& a
InnoDB 和 MyISAM 存储引擎表,索引类型必须为BTREE
4 e( L! L. }' {MEMORY表可以根据需要选择 HASH 或者 BTREE 类型索引。% v" |' R) e1 Y+ y
MYSQL 中索引的限制$ a1 X" S+ T5 ]5 y3 E0 x- ~
MYISAM 存储引擎索引长度的总和不能超过 1000 字节
- J: ~/ L2 D! A* z, |2 c4 zBLOB 和 TEXT 类型的列只能创建前缀索引
+ Y0 z+ e% [. i" w' q: C. E使用不等于 (!= 或者 <>) 的时候, MYSQL 无法使用索引3 N5 p/ X; R' }8 U
过滤字段使用函数运算 (如 abs (column)) 后, MYSQL无法使用索引" q0 a. d5 H, ?- @! b5 }5 e+ K, i
join语句中join条件字段类型不一致的时候MYSQL无法使用索引3 l1 R, q# L' s! |, y; f7 e
使用 LIKE 操作的时候如果条件以通配符开始 (如 ‘%abc…’)时, MYSQL无法使用索引
3 U- K4 t2 w7 M5 k! `- V- J使用非等值查询的时候, MYSQL无法使用 Hash 索引。, }$ Q# ?8 O, K' C
- M. ~; B$ H. S$ p$ P五、SQL查询规范
# I+ ^" C9 X Z6 e按需索取拒绝 select *
0 M( d3 d- P( `无法索引覆盖,回表操作,IO增加;额外的内存负担,大量冷数据灌入innodb_buffer_pool_size,降低查询命中率;额外的网络传输开销。
% z; O4 V6 d& q0 b超过三个表禁止 join
3 } q D& U, W- {) ]$ j& ^需要 join 的字段,数据类型必须绝对一致;多表关联查询时,保证被关联的字段有索引。! k: h3 k4 O$ q# n
涉及到复杂sql时,须用explain检验索引设计/ i- p: |8 n; I
对应同一列进行 or 判断时,使用 in 代替 or
6 @4 c4 [8 k: Z/ r$ i ~6 Q8 ~in 的值不要超过 500 个, in 操作可以更有效的利用索引,or 大多数情况下很少能利用到索引。' W% j S" ~" i: j6 {
in 操作能避免则避免
& I+ ?( I8 x- C" |; q2 E' t( i若实在避免不了,需要仔细评估 in 后边的集合元素数量,控制在 1000 个之内。
/ z9 Y% e* T5 [! P! i2 N$ {WHERE从句中禁止对列进行函数转换和计算
7 @8 \6 V6 L3 i7 J对列进行函数转换或计算时会导致无法使用索引。
" H, L3 q4 @3 Q Ccount(列名)或 count(常量) 替代不了 count(*)* H+ |& Y6 H5 K
count(*)是 SQL92 定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无,
* k* ?, ~2 e- P7 fcount(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。2 F2 |1 y* G/ O+ | X
不要使用UNION,推荐使用UNION ALL# K4 ?' A, F- h4 G1 u8 V! a" ~) E- O3 {
因为union all不需要去重,节省数据库资源,提高性能,另外子句个数限制在5个以内。
8 |4 n) Y8 l+ }禁止使用 order by rand() 进行随机排序0 V% m( L7 K* c# _9 u0 t- W! C
会把表中所有符合条件的数据装载到内存中,然后在内存中对所有数据根据随机生成的值进行排序,并且可能会对每一行都生成一个随机值,如果满足条件的数据集非常大,就会消耗大量的 CPU 和 IO 及内存资源,推荐在程序中获取一个随机值,然后从数据库中获取数据的方式。
' M( ?# a7 H; ?* c( M4 d; S; T* t4 v不得使用外键与级联,一切外键概念必须在应用层解决 Y# {4 `3 u8 A* b) E
以学生和成绩的关系为例,学生表中的 student_id是主键,那么成绩表中的 student_id则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新, 即为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群; 级联更新是强阻塞,存在数据库更新风暴的风险;而且外键影响数据库的插入速度。
) A8 q6 D, C2 a9 D3 S, ]2 A$ l使用合理的分页方式以提高分页效率" Y3 v: E/ C3 Q- Y( Q. [
不推荐 SELECT * FROM table ORDER BY TIME DESC LIMIT 10000,10;
O' l4 s. Q8 W' {( |5 a1 @: z原因:会导致大量的io,因为MySQL使用的是提前读取策略
+ g: e# c8 m9 g5 B+ L3 F推荐:SELECT * FROM table WHERE TIME < last_TIME ORDER BY TIME DESC LIMIT 10.. s# W7 I% W( _6 S% t% P1 P' ^
SELECT * FROM table inner JOIN (SELECT id FROM table ORDER BY TIME LIMIT 10000,10) as t USING(id)
- `' o* |! z& {* Y% f5 }' u( O1 U1 `2 i) j+ F" v0 m
|
|