目的:设计高效的MySQL库表,减少及使用中的容错率和复杂度,提升性能。
一、规范命名:
命名时的字符取值范围:小写字母a-z,0-9和"_"(下划线)
1、库、表、字段命名一律使用小写字母,字母开头,"_"用来分割关键字,不要放在结尾。
2、库、表、字段命名要避开数据库关键字,如name、date、status、time、type、update、select等,可以加前缀来区别开,如"xx_status"。
具体有哪些关键字可参考mysql5.7官方文档关键字部分: https://dev.mysql.com/doc/refman/5.7/en/keywords.html
3、名称要知其意,建议不要太长,控制字符在数不超过40,合理利用组合词、缩写词等。
4、表名、字段名要区分开。字段名不要是其他表的名称。比如表名可以统一使用"tb_"或"t_"开头,用来提高辨识度。
5、合理利用后缀对表进行分类如:
涉及月分表要统一使用"_年_月"作为业务表后缀,如 "t_order_2022_07"。
涉及扩展表的统一使用"_extend"作为业务表后缀,如"t_user_extend"。
涉及日志表的统一使用"_log"作为业务表后缀,如"t_login_log"。
涉及永久备份表的统一使用"_allbak_日期"作为业务表后缀,如"t_user_allbak_20220719"。
涉及临时表的统一使用"_tmp_日期"作为业务表后缀,如"t_user_tmp_20220719"。
6、合理利用前缀对索引类型分类如:
主键索引若命名使用"pk_"作为前缀,后面接字段名或者适当的字段缩写,如"pk_id"。
唯一索引命名使用"uk_"作为前缀,后面接字段名或者适当的字段缩写,如"uk_user_id"。
普通索引命名使用"idx_"作为前缀,后面接字段名或者适当的字段缩写,如"idx_ctime"。
多字段联合索引,命名时除了前缀,多字段可以用"_"分割,考虑到索引名长度,可适当合并或缩写字段名。如"o_status"、"o_type"、"o_date"字段的普通索引"idx_status_type_date" 或"idx_st_tp_dt" 等。
二、规范设计表:
注意事项:当前数据库sql_mode是非严谨格式,一些超范围的不合理的插入可能不会报错。比如 int UNSIGNED 可以插入负数不会报错,实际存入是0,也可以插入超过最大值的数,但是只会存入最大值。如char、varchar类型,存入超过字符串大小的,只会截取保留最大字符串长度的字符。
1、创建表的时候显式的指定存储引擎,无特殊要求,均使用Innodb。
2、表中必须有主键,最好是自增主键,不允许使用联合主键。
可以选择int、bigint类型,这两类有有符号和无符号区分。mysql的整型分类及其占用空间和存储值大小如下表:
int类型有符号的正常自增id,都可以存放21亿数据,满足一般的需求了。大家可以根据业务需求选择合适的类型作为自增主键。
3、整型指定大小的分别如下,合理利用:
如int、int(4)、int(11)存储的值大小是一样的,bigint、bigint(15)、bigint(22)存储的值大小是一样的。
为啥会有int、bigint 后面括号赋值的设定。这个是要配合可选属性 ZEROFILL 来设定,属性意思是用0填充,加了ZEROFILL属性,不管有没有定义UNSIGNED,都被默认是无符号,不可存放负数。
int 是int(10),bigint是bigint(20)。
如 int(12) UNSIGNED ZEROFILL 。表示里面存储的值范围是0~4294967295, 但是查询的时候显示的值有12位,不满12位的会在前面补0。 比如存放12345,那么显示的值会在前面补7个0,凑满12位,是000000012345。
4、表的时间字段建议用timestamp只占用4个字节,datetime要占用8个字节。但是timestamp范围是1970-01-01 00:00:01到2038-01-01 00:00:00。
5、不建议使用枚举类型enum和set类型。
枚举类型只是用来限制取值范围。若是字符串类的取值范围,可以使用char或者varchar类型。若是整型类型的取值范围,可以使用上述表中合适的整型类型。这样做虽然没有了限制取值范围,但是灵活多变,便于维护。
6、建议所有的字段都建议设置为not null default '默认值',尤其是被索引的字段。开发可以考虑通过给定无意义的默认值,来替代允许null。
因为mysql对null值的存储会耗费更多的空间,null的列使索引/索引统计/值比较都更加复杂,性能不佳。而且not in或!= 的条件查询,过滤不出null所在行的数据。
7、每个字段和每个表都要有comment备注。用来备注字段和表的含义,可以用来弥补名称描述含义不清的问题,也便于数据字典统计。
8、每个表必须包含update_time 这样的字段用来表示更新时间,设置 timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,并创建索引。便于记录数据变化,排查问题。
9、字符串类型的建议统一字符集,目前数据库的字符集是UTF8,非必要情况,都使用UTF8类型。因为如果是表关联查询,不同字符集类型的字段关联时,是没法使用索引,会影响性能。
10、所有小数类型选择使用定点类型的decimal,特别是涉及金额的,虽然比double和float会占用更多的存储空间,但是精度不会有问题,尤其是在涉及小数计算的时候。
11、字符串类型的字段,主要是char、varchar ,能使用char的就不使用varchar。满足数据存储的条件下,选择合适的字符串长度,不要为了方便,一味的设定很大的长度。
char是定长,适合存放固定长度的字段或者长度变化很小的字段或者长度很小的字段,比如身份证号码。
varchar是变长,相比char会需要多的1-2个字节存储长度,而且在内存中保存时会消耗更多的内存,varchar适合长度不定的字段,或者最大长度和平均长度相差很大的字段。比如地址有的很长有的很短。
12、 能不使用text、blob最好。表中有一些描述意义的大类型字段,建议采用分表的模式存储,把大类型字段单独存在一个表中。这里可以考虑使用一些字段冗余来提升性能。
13、表的设计禁止使用外键约束,容易导致表之间的耦合,也影响性能不好维护。若需要这样的完整性约束,可以程序端使用事务处理。
14、mysql在处理大表时性能不佳,适当控制表的行数和列数。列数尽量不超过30,较多大字段的表行数上千万后考虑查询和分表等问题。
15、索引设计时合理利用联合索引,索引覆盖等,要注意索引字段顺序。表的索引数量不宜过多,因为索引维护也是比较消耗性能,且占用空间。大字段需要索引时,可考虑使用前缀索引。
三、sql规范:
1、程序中的查询禁止使用select * 。select * 的sql,在改变的表结构加减字段时可能会导致程序出错,要不可能就需要程序和数据表同步更新。而且有的可能业务并不需要select *操作,text、blob这样的大字段的查询会很耗io,能规避就规避,而且对指定字段的查询有可能会用到索引覆盖,可以减少回表提升性能。
2、select、update、delete操作必须有where条件,哪怕真的是全表操作,也可以设定类似 where id>0; select操作可能不是非必须,但可以养成一个良好的习惯。
3、程序中的update、delete最好单表操作,禁止连表操作和子查询操作。
4、update和delete这样的操作,最好不要使用limit,因为可能在执行前有新的数据产生,被更新和删除的数据可能不是想要的数据。可以考虑复杂点操作先查询出id,根据id去更新和删除数据。
5、where条件中禁止在索引列上使用任何函数,包括数据类型转换函数,常见的如时间处理,字符串处理。 如 WHERE (UNIX_TIMESTAMP(CURDATE())-dx_etime)/(24*3600)<15 。哪怕dx_etime字段有索引,也使用不到。
6、大表数据分页操作,不建议使用 limit 100000,200 这样的操作分页,到后面分页越多,性能越差。可以考虑获取当前页的最后id作为下一页的查询条件。 也可以业务考虑需不需要那么多分页,一般大家都不会翻个几百几千页吧。
7、非必须非难度下,禁止索引列负向查询如not in,!=,not like ,!>,!< 这样的查询。禁止索引列前模糊查询如 like '%name';
8、Join中使用的关联字段使用统一数据类型,比如on的字段类型要统一。
9、建议连接MySQL不要设置成autocommit=0。涉及到多条sql需要一起执行的,可以使用事务。