找回密码
 注册
关于网站域名变更的通知
查看: 566|回复: 3
打印 上一主题 下一主题

[行为准则] 数据库设计和研发规范

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2021-9-6 11:08 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
一.  基础规范
% Q" T# Y! f% w, B7 S所有表都需要添加注释+ C5 n# h" a4 _& o4 z6 {) T
使用comment从句添加表和列的备注,从一开始就做好数据字典维护。9 U% y) C  L9 L( N8 k
单表数据量建议控制在500万以内$ U( P1 `; F5 A! ^. l2 u
过大会给备份和恢复带来困难,可用历史数据归档(用于日志数据),分库分表(用于业务数据)等手段来控制数据量大小。7 {( ~3 _. p# A2 {; e. Z- c
不在数据库中存储图、文件等二进制数据
  A% c# n: h. F图或文件IO操作耗时,通常存储于文件服务器,数据库只存储文件地址信息。* q2 W5 |- [: x5 E" `! K
尽量冷热数据分离,减小表的宽度
* I3 }1 l! B3 q! r0 U. e5 [1 n把不常用且占宽大的字段从热表中分离可独立存储,每一行数据大小不超过65535字节。
* H9 ^% M7 w; A! t2 B/ g7 b禁止测试、开发环境直连线上数据库; l* w! j2 D8 z" Q0 b% F% J  `
二、命名规范2 P: g; t! R3 ~1 X' d$ E* Y
库名、表名、字段名必须使用小写字母,并采用下划线分割# G, v( [# S4 \; K# p
MySQL配置参数lower_case_table_names=1,即库表名以小写存储,大小写不敏感。如果是0,则库表名以实际情况存储,大小写敏感;如果是2,以实际情况存储,但以小写比较。如果大小写混合使用,可能存在abc,Abc,ABC等多个表共存,容易导致混乱。! c  X9 _! E/ n
库名、表名、字段名不要超过32个字符,需见名知意易于辨识6 P+ v* I' N' V5 X. z
库名、表名、字段名支持最多64个字符,但为了减少传输量,禁止超过32个字符。
0 \  ^, t' c, \库名、表名、字段名禁止使用MySQL保留字
% f9 O. w  u9 ^  r, Q- y当库名、表名、字段名等含有保留字时,SQL语句必须用反引号引用属性名称,这将使SQL语句书写、SHELL脚本中变量的转义变得非常复杂。( T; a7 b  f7 `1 R: L
备份库、表必须以bak为前缀,并以日期为后缀6 F5 G) _  z/ b0 a, J8 ~
形如:bak_user_account_20190313
( [# _- S0 M2 e9 n2 v- Y索引的命名) c4 j& i5 E% S$ C5 c2 \; G3 e, J
主键索引名为 pk_字段名;唯一索引名为 uk _字段名;普通索引名则为 idx_字段名。
& b1 c: z9 X. u0 _- P5 o4 H% H6 F2 |1 G5 Y3 I) E: D) k
三、字段设计规范* |8 L. O! x6 x) s/ n! u7 t" x
优先选择符合存储需要的最小的数据类型  C( p$ k; e8 I% Q  W9 I
列的字段类型越大,建立索引占据的空间就越大,使得一个页中的索引越少,IO次数增加,影响性能,能用int的就不用char或者varchar,能用tinyint的就不用int。状态status、类型type等字段推荐使用tinytint或者smallint类型节省存储空间;使用 UNSIGNED 存储非负数值;使用tinyint来代替 enum和boolean;存储 ip 最好用 int存储而非 char(15)。5 {6 _$ k! y- Q* P7 p  R0 C: {. v/ M
不推荐使用blob,text等类型
9 r3 Y+ H( e3 Xblob,text是为了存储极大的字符串而设计的数据类型,采用二进制与字符串方式存储,该数据类型不能设置默认值、不便于排序、不便于建立索引,varchar的性能会比text高很多,如果非要使用,建议将这种数据分离到单独的拓展表中。% O& L/ c7 S0 Y$ W8 X; \# F
禁止使用字符串来存储日期型数据
1 _: E# V8 r# h0 d. }, j& d一是无法使用日期函数计算比较,二是因为字符串存储要占更多的内存空间,datetime占8字节,timestamp是以int存储,占4字节。% [- O4 u- u% c, W6 c: K
用DECIMAL代替FLOAT和DOUBLE存储精确浮点数
9 N. _! Y/ W/ B& Y3 c3 ^Decimal类型为精准浮点数,float 和 double 都有精度损失的问题,很可能在值的比较时,得到不正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。
' z/ A9 P6 z$ r, D  @" W8 X9 t必须把字段定义为NOT NULL并设默认值9 F, B% ^$ `) c+ h' R- H/ z
一是NULL需要更多的存储空间,二是NULL的列可能索引实效,三是NULL只能采用IS NULL或者IS NOT NULL,而在=/!=/in/not in时有大坑。
! L0 ~' |3 O- D1 h8 d# H使用varchar(20)存储手机号,不要使用整数
) P8 I" R9 g* X* l一是牵扯到国家代号,可能出现+/-/()等字符,例如+86,二是varchar可以模糊查询,例如like ‘138%’
: V7 n, m! O) y: s" l6 P根据业务区分使用char/varchar
4 O9 ?' F% o- M( |字段长度固定,或者长度近似的业务场景,适合使用char,能够减少碎片,查询性能高9 v1 L* Z2 `" \
字段长度相差较大,或者更新较少的业务场景,适合使用varchar。# d$ x' ]) f& O- p# ?
禁止在数据库中存储明文密码,把密码加密后存储
0 q* j- j1 L  J* X尽量不使用外键
  |/ H! E3 n5 @  s$ ]& x建议在应用层实现外键的逻辑, 外键与级联更新不适合高并发场景,降低插入性能,大并发下容易产生死锁。% w6 M6 a# Q' S" }- h( u7 S
核心表必须有行数据的创建时间和最后更新时间( H& k! a) Y! j' `* j4 ^
核心表(如用户表,金钱相关的表)必须有行数据的创建时间字段create_time和最后更新时间字段update_time,便于查问题。
. ?+ b& y3 j& E1 Y% c6 `" I- c: a; v* Y. D
四、索引设计规范3 f, ]0 V' \8 Q. V3 H/ v8 w4 k
MySQL的查询速度依赖良好的索引设计,因此索引对于高性能至关重要,合理的索引会加快查询速度,不合理的索引会降低速度。
1 D5 b4 i3 w( \- e0 r! N索引其实就是一种数据结构(哈希表、树等),不同类型的索引有着不同的数据结构和功能。* P9 X* K' k4 L0 J
索引的作用是加速查询速度,维护数据的约束性(完整性、一致性)。对于加速查询,使用索引不一定是最好的选择,小表就直接全表扫描,中到大表就建索引,超大表就分区分表,要做好索引带来的好处和维护索引的成本之间的权衡。1 W: _4 o( D2 J5 L$ |* \6 L: B
单表的索引数建议不超过 5 个,单个索引中的字段数建议不超过 5 个
! H* }# r/ N; o太多就起不到过滤作用了,索引也占空间,管理也耗资源。
# C+ Z' @0 y: w# y+ v对字符串使用前缀索引,前缀索引长度不超过8个字符; Q2 E! J. |8 O: h
不要索引blob/text等字段,不要索引大型字段,这样做会让索引占用太多的存储空间。
* }8 v( b9 P7 `( @1 s" M5 m前缀索引就是对文本的前几个字符建立索引,前缀索引能有效减小索引文件的大小,提高索引的速度。但是前缀索引也有它的坏处:MySQL 不能在 ORDER BY 或 GROUP BY 中使用前缀索引,也不能把它们用作覆盖索引(Covering Index)0 a& z, }1 U5 T2 Z1 ?# e
重要的SQL必须被索引,核心SQL优先考虑覆盖索引
1 U1 K2 G! }/ ^+ G# |( aUPDATE、DELETE 语句的 WHERE 条件列;ORDER BY、GROUP BY、DISTINCT 的字段;多表 JOIN 的字段。覆盖索引可以避免Innodb表进行索引的二次查询,把随机IO变成顺序IO,加快查询效率。
6 ?3 `% U! l9 s7 {联合索引区分度最大的字段放在前面- w  T9 e7 G9 b& `7 C0 _2 G
选择筛选性更优的字段放在最前面,比如userid,carid等,type,status等筛选性一般不建议放在最前面。索引根据左前缀原则,当建立一个联合索引(a,b,c),则查询条件里面只有包含(a)或(a,b)或(a,b,c)的时候才能走索引,(a,c)作为条件的时候只能使用到a列索引,所以这个时候要确定a的返回列一定不能太多,不然语句设计就不合理,(b,c)则不能走索引。
3 W) w, p/ q/ G业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引$ x' U5 Q( b( ^' Z
不要以为唯一索引影响了 insert 速度,这个速度损耗可以忽略,但提高查找速度是明; M3 B. W0 _6 B
显的; 另外,即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。+ W. y8 K1 y& b. k/ P5 G/ Z5 H7 c& r( _# F
InnoDB 和 MyISAM 存储引擎表,索引类型必须为BTREE
9 ]$ O7 z! k* j- V! u" C3 i2 |! DMEMORY表可以根据需要选择 HASH 或者 BTREE 类型索引。$ d9 ]/ W+ ~% ^0 Q
MYSQL 中索引的限制  S* |  {3 ^" w# u' K" w
MYISAM 存储引擎索引长度的总和不能超过 1000 字节9 m  C4 M2 k$ H6 T# Y
BLOB 和 TEXT 类型的列只能创建前缀索引
, `4 {' H; }, ~使用不等于 (!= 或者 <>) 的时候, MYSQL 无法使用索引7 ^# H- o" B7 y( [8 `/ m! p
过滤字段使用函数运算 (如 abs (column)) 后, MYSQL无法使用索引
& X  u+ m) i% {! ljoin语句中join条件字段类型不一致的时候MYSQL无法使用索引
, M& I5 \5 M6 F2 j# b9 x. }" R5 k5 `使用 LIKE 操作的时候如果条件以通配符开始 (如 ‘%abc…’)时, MYSQL无法使用索引
; t* }: c2 V9 L: D使用非等值查询的时候, MYSQL无法使用 Hash 索引。6 _. r) e7 G. W& H' S( h% [
' U& B" G* H# F% L
五、SQL查询规范; w  b0 U0 o5 [5 J* G$ z4 |
按需索取拒绝 select *1 c8 L5 A9 B: }8 d% K4 ]0 z
无法索引覆盖,回表操作,IO增加;额外的内存负担,大量冷数据灌入innodb_buffer_pool_size,降低查询命中率;额外的网络传输开销。
" F; n+ p: A! h9 |% R超过三个表禁止 join
) y  n# h: o% b& o; c' P需要 join 的字段,数据类型必须绝对一致;多表关联查询时,保证被关联的字段有索引。
: O) q4 o: w. s4 c涉及到复杂sql时,须用explain检验索引设计
0 H8 n8 p: F& X3 ~( E/ ^1 D  {对应同一列进行 or 判断时,使用 in 代替 or$ K* K0 d0 K: F" d5 K7 N* p6 a
in 的值不要超过 500 个, in 操作可以更有效的利用索引,or 大多数情况下很少能利用到索引。( {& A3 E' `0 f0 h6 B8 @
in 操作能避免则避免7 U2 j* C8 N  G) k, A: S& e
若实在避免不了,需要仔细评估 in 后边的集合元素数量,控制在 1000 个之内。- t* k$ k7 V, h; S2 L
WHERE从句中禁止对列进行函数转换和计算
' j: S$ u! e1 R3 q对列进行函数转换或计算时会导致无法使用索引。! h: l1 \6 y  ?3 t3 [2 s8 Z+ q( Y
count(列名)或 count(常量) 替代不了 count(*)
5 ?6 Y! ~. E8 bcount(*)是 SQL92 定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无,
. y2 U" M6 l6 i, \( ]. Ocount(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。
" |6 f# J/ \+ u( q; Z不要使用UNION,推荐使用UNION ALL
$ a0 Y2 w8 g3 ~3 d0 \7 x因为union all不需要去重,节省数据库资源,提高性能,另外子句个数限制在5个以内。6 L2 V5 U# p/ }5 c
禁止使用 order by rand() 进行随机排序
+ Z4 A% o6 c; T; e会把表中所有符合条件的数据装载到内存中,然后在内存中对所有数据根据随机生成的值进行排序,并且可能会对每一行都生成一个随机值,如果满足条件的数据集非常大,就会消耗大量的 CPU 和 IO 及内存资源,推荐在程序中获取一个随机值,然后从数据库中获取数据的方式。- ~# m0 J1 [1 T) N+ h
不得使用外键与级联,一切外键概念必须在应用层解决
2 k# p" [, R- L) K以学生和成绩的关系为例,学生表中的 student_id是主键,那么成绩表中的 student_id则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新, 即为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群; 级联更新是强阻塞,存在数据库更新风暴的风险;而且外键影响数据库的插入速度。
# ^' A5 _0 E/ j. n4 ~- {使用合理的分页方式以提高分页效率
" t% A' C% W, x, N. m  u1 `7 ~1 \+ |不推荐 SELECT * FROM table ORDER BY TIME DESC LIMIT 10000,10;
, O: G8 j0 A& G3 D8 @4 ~原因:会导致大量的io,因为MySQL使用的是提前读取策略
7 }/ m: h( L, O2 l$ E9 M推荐:SELECT * FROM table WHERE TIME < last_TIME ORDER BY TIME DESC LIMIT 10.
/ Z9 G6 D. [# I0 _" rSELECT * FROM table inner JOIN (SELECT id FROM table ORDER BY TIME LIMIT 10000,10) as t USING(id)* t- ^! H7 v+ t( A9 U& s8 i

6 V: o, I" E: M

该用户从未签到

2#
发表于 2021-9-6 13:32 | 只看该作者
在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生

该用户从未签到

3#
发表于 2021-9-6 13:40 | 只看该作者
选择筛选性更优的字段要放在最前面

该用户从未签到

4#
发表于 2021-9-6 15:26 | 只看该作者
varchar的性能比text高很多
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

推荐内容上一条 /1 下一条

EDA365公众号

关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

GMT+8, 2025-9-10 18:29 , Processed in 0.125000 second(s), 23 queries , Gzip On.

深圳市墨知创新科技有限公司

地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

快速回复 返回顶部 返回列表