数据库设计规范
一、数据字典规范
(一)建表规约
1.1 命名规约
【强制】
1. 库名、表名必须使用小写字母或数字,“_”分割,禁止以数字或大写字母开头,两个下划线之间不出现数字,其中库名不超过15个字符, 表名不超过20个字符。数据表名统一使用“t_”开头,使用“t_业务名称_表的作用”英文单词的规则命名。
正例:t_order_content, t_business_type, t_level3_name
反例:tAreaCity, t_kehuXinxi, t_level_3_name, 2customer_city
说明:MySQL配置参数lower_case_table_names如设置为0以实际表名存储区分大小写,如果大小写混合用,可能存在t_order,t_Order等多个表共存,容易导致混乱。为了统一规范,库名、表名、字段名使用小写字母。
【强制】
2. 字段名使用大小写字母或数字,禁止以数字或大写字母开头,字段名长度不超过15个字符。
易订货产品统一使用 “f + 字段作用” 英文单词的规则命名。
正例:fdbid, fcustomerid, fusername, fcreatetime
反例:FPRODUCTNAME, Productcode, Funit_Name, F_2_Price
解决方案产品直接使用 “字段作用 + _(多单词分割)” 的规则命名,不加“f”前缀。
正例:dbid, customer_id, user_name, create_time
反例:fdbid, Customerid, productcode, F_2_Price
【推荐】
3. 表名要见名知意,建议使用名词而不是动词。如订单内容表:t_order_content。
说明:库表是一种客观存在的事物,一种对象,所以建议使用名词。
【推荐】
4. 表名不使用复数名词,表名应仅仅表示表里面的实体内容,不应该表示实体数量,对应于DO类也是单数形式,符合表达习惯。
1.2 字符集规约
【强制】
1. 数据表字符集统一utf8mb4, 校对规则统一utf8mb4_general_ci。
说明:utf8mb4兼容utf8,能够用四个字节存储更多的字符。
CREATE TABLE `t_user_info` (
`fid` BIGINT UNSIGNED NOT NULL PRIMARY KEY,
`fname` VARCHAR(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
1.3 引擎规约
【推荐】
1. 建议使用InnoDB存储引擎, MYSQL 5.5以后的默认引擘,支持事务处理与外键和行级锁,更好的恢复性,高并发下性能更好,对多核、大内存、SSD等硬件支持更好。
1.4 主外键规约
【推荐】
1. 数据表须设计主键,一般为fid, 类型为unsigned bingint。
说明:建议不使用UUID作主键,虽然UUID可以很好的生成全局主键,在分布式的方案中有时会 需要用uuid,但是缺点也是非常明显的。由于UUID非常的长,除占用大量存储空间外,最主要 的问题是在索引上,在建立索引和基于索引进行查询时都存在性能问题。
【推荐】
2. 表的主键设计可从单独sequence表中取值。
说明:主键设计一般不建议使用自增,这样会影响CACHE,建议可采用flickr的sharding主键生 成方案。它与一般Sequence表方案有些类似,但却很好地解决了性能瓶颈和单点问题,是一种 可靠而高效的全局主键生成方案。
【参考】
3. 表如果需使用外键要设计适当,外键在一定程序上说明业务逻辑,但不用外键数据管理简单,操作方便,易迁移,且性能高(如DML操作),对于性能要求高的系统,建议不使用外键与级联,一切外键概念在应用层解决。
【参考】
4. 如业务需要设计复合主键,建议使用“主键”加“唯一索引”的方式,不建议创建复合主键,因为复合主键会使用更多的BLOCK去创建索引,所以在写操作上性能要低一些。
1.5 字段规约
【强制】
1. 创建字段时必须附字段中文注释,如果修改字段含义或对字段表示的含义追加时,需要及时更新字段中文注释。字段值如由多个值来表示,必须清楚注释各值代表的含义。
正例: `ftype` CHAR(1) DEFAULT NULL COMMENT '类型(I或空:增加,U:修改,D:删除)'
`fproductCode` VARCHAR(100) DEFAULT NULL COMMENT '商品编码'
反例: `fproductCode` VARCHAR(100) DEFAULT NULL
`fproductCode` VARCHAR(100) DEFAULT NULL COMMENT 'fproductCode'
【强制】
2. 表达与否概念的字段,数据类型定义为TINYINT, 其中1表示“是”, 0表示“否”。
正例: `fdelete` TINYINT DEFAULT NULL COMMENT '删除字段(0或空:正常,1:删除)'
反例: `fdelete` INT DEFAULT NULL COMMENT '删除字段(0或空:正常,1:删除)'
【强制】
3. 任何字段如果非负数,必须定义为 unsigned 无符号。
说明:同样的字节数,无符号存储的数值范围更大。如tinyint 有符号为 -128-127,无符号为0-255。
【强制】
4. 新增字段如果定义为不允许NULL值,则必须给出默认值。
正例: `fstatus` TINYINT NOT NULL DEFAULT ‘0’ COMMENT '审核状态(0:待审核,1:审核中)',
反例: `fstatus` TINYINT NOT NULL COMMENT '审核状态(0:待审核,1:审核中)',
【推荐】
5. 可适当考虑增加fcreateTime, fmodifyTime 字段,类型均为datetime,描述该行数据的创建和修改时间。如需实现乐观琐可考虑增加fversion字段,描述行的数据版本号。
【推荐】
6. 字段允许适当冗余, 以提高性能,但是必须考虑数据同步的情况。冗余字段应遵循:
① 不是频繁修改的字段。
② 不是VARCHAR超长字段, 更不能是 TEXT字段。
【推荐】
7. 不建议使用ENUM、SET类型,使用TINYINT来代替。如字段是字符串且长度固定使用CHAR类型。
① ENUM类型会带来不少问题,包括添加新的值要做DDL、默认值问题、索引值问题。
② 如果系统已经使用了MYSQL的ENUM字段类型,请在查询的时候直接查询值(并加上单 引号),这样就不会使用ENUM自身隐藏的索引值来获取结果了。
【推荐】
8. 尽可能不使用TEXT、BLOB类型。
说明:当mysql如需对TEXT、BLOB列进行排序时,它只会使用前缀并忽略剩余的值,其索引排序问题只能使用max_sort_length的长度或者手工指定ORDER BY SUBSTRING(column,length)的长度来排序,会在磁 盘上生成临时表可能浪费更多的空间。
【推荐】
9. 存储精确浮点数建议使用DECIMAL替代FLOAT和DOUBLE。
说明:float 和 double 在存储的时候,存在精度损失问题,很可能在值的比较时,得到不正 确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。
【推荐】
10. 单表行数超过 500万行或者单表容量超过5G,推荐进行分库分表。
说明:如果一个表的记录数太多了,比如上千万条,而且需要经常检索,且数据在逻辑上可以划分也利于程序实现,那么我们可以充分利用水平分表的优势。
【推荐】
11. 可考虑使用INT UNSIGNED 存储IPV4,节省存储空间。
说明:如果用CHAR(15)来存储,则需要15个字节(VARCHAR则需要16个字节)。将IP用无符号 整数存储,只需要4个字节,能节省空间。通过MySQL函数inet_ntoa和inet_aton来进行转 化。Ipv6地址目前没有转化函数,需要使用DECIMAL或者两个BIGINT来存储。例如:
SELECT INET_ATON('192.168.1.1'); 输出结果: 3232235777
SELECT INET_NTOA(3232235777); 输出结果: 192.168.1.1
【推荐】
12. 建议使用TIMESTAMP来存储时间,节省存储空间。
说明:DATETIME和TIMESTAMP都是精确到秒,优先选择TIMESTAMP,因为TIMESTAMP只有4个字 节,而DATETIME要8个字节。同时TIMESTAMP具有自动赋值以及自动更新的特性。对于跨时区 的业务,TIMESTAMP更为合适。 合适的字符存储长度,不但节约数据库表空间,节约索引存储, 更重要的是提升检索速度。
(二)索引规约
2.1命名规约
【强制】
1. 索引使用小写字母并以“_”分割,禁止以数字或大写字母开头,索引长度不超过20个字符。唯一索引必须按照“uk_字段名”进行命名。普通索引必须按照“idx_字段名”进行命名,组合索引必须按照“gk_各字段缩写”进行命名。
正例:uk_product_code, uk_unit_id, idx_version, gk_keywords
反例:U_productCode, Uniq_unit_id, Index_Version, GK_keywords
2.2建索规约
【强制】
1. 对于联合索引(a,b,c) 相当于 (a) 、(a,b) 、(a,b,c),不需再单独创建(a)、(a,b)索引。
说明:对于复合索引 MYSQL从左到右的使用索引中的字段,一个查询可以只使用索引中的一部 份,但只能是最左侧部分,当最左侧字段是常量引用时,索引就十分有效。
【强制】
2. 唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件。
SELECT * FROM t_user WHERE fsex = ‘男’;
【强制】
3. 更新非常频繁的字段不适合创建索引。
SELECT * FROM t_user WHERE floginCount = 1;
【强制】
4. UPDATE、DELETE语句需要根据WHERE条件添加索引,不会出现在WHERE子句中字段不该创建索引。
【推荐】
5. 业务上具有唯一特性的字段, 即使是组合字段, 可考虑建成唯一索引。
说明:唯一索引虽影响了insert速度, 这个速度损耗可以忽略, 但提高查找速度是明显 的; 另外,即使在应用层做了非常完善的校验和控制, 只有没有唯一索引,根据墨菲定律,必然有 脏数据产生。
【推荐】
6. 单张表的索引数量建议在5个以内。
【推荐】
7. 索引字段的顺序需要考虑字段值去重之后的个数,个数多的放在前面。ORDER BY,GROUP BY,DISTINCT的字段需要添加在索引的后面。
【推荐】
8. 使用EXPLAIN判断SQL语句是否合理使用索引,尽量避免extra列出现:Using File Sort,Using Temporary。
说明:当语句有 ORDER BY , GROUP BY 时,注意检查是否有效利用索引。
【推荐】
9. 页面搜索禁止左模糊或者全模糊, 如果需要请走搜索引擎来解决。
说明:索引文件具有B-TREE的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引。不建议使用%前缀模糊查询,例如LIKE “%ABC”。
【推荐】
10. 重复值较少的且较频繁的作为查询条件字段应该创建索引,如下需创建fempno索引字段。
SELECT * FROM t_emp WHERE fempNo = 1;
正例:创建普通索引:CREATE INDEX idx_femp_no ON t_emp(`fempNo`);
【推荐】
11. 在 varchar 字段上建立索引时,必须指定索引长度, 没必要对全字段建立索引,根据实际文本区分度决定索引长度。
说明:索引的长度与区分度是一对矛盾体,一般对字符串类型数据,长度为 20 的索引, 区分度会高达90%以上, 可以使用 count(distinct left(列名, 索引长度))/count(*) 的区分度来确定。
(三)过程规约
3.1命名规约
【强制】
1. 存储过程使用小写字母或数字并以“_”分割,禁止以数字或大写字母开头,长度不超过20个字符,按照“pr_过程作用”进行命名。
正例:pr_bug_fix_17024(), pr_count_dbid(), pr_repaire_data()
反例:Proc_bug_fix17024(),SP_COUNT_DBID(), PrRepaireData()
【强制】
2. 函数使用小写字母或数字并以“_”分割,禁止以数字或大写字母开头,长度不超过20个字符。按照“fn_函数作用”进行命名。
正例:fn_get_code(), fn_get_region(), fn_rand_string()
反例:get_code(),Get_Region(),funcRandString()
3.2操作规约
【强制】
1. 存储过程参数创建时需显式指定参数输入输出类型,如果不显式指定“IN”、“OUT”、“INOUT”,则默认为“IN”。
正例:CREATE PROCEDURE r_count_dbid(IN param1 INT, IN param2 INT, OUT param3 INT);
反例:CREATE PROCEDURE r_count_dbid(param1 INT, param2 INT, param3 INT);
【强制】
2. 存储过程如使用游标,每个游标不再需要时都应该关闭。
说明:CLOSE 释放游标使用的所有内部内存和资源,因此在每个游标不再需要时都应该关闭。
【推荐】
3. 尽量少使用存储过程、触发器、函数等,容易将业务逻辑和DB耦合在一起。
说明:存储过程难以调试和扩展,不同数据库语法差别很大,移植困难;把过多业务逻辑写在存储过程不好维护,不利于分层管理,容易混乱,一般存储过程适用于个别对性能要求较高的业务,其它的必要性不是很大;
【推荐】
4. 存储过程嵌套调用,建议不超过2层。
说明:嵌套过多逻辑不易理解,存储过程建议不嵌套调用。
【推荐】
5. 创建存储过程时不指定DEFINER权限, DEFINER用于指明存储过程是由哪个用户定义的。
说明:definer:在执行存储过程前验证definer对应的用户如:`root`@`192.168.1.1`是否存在,以及是否具有执行存储过程的权限,若没有则报错。
【推荐】
6. 建议存储过程在执行中可适当增加过程信息的输出。
(四) 视图规约
4.1命名规约
【强制】
1. 视图使用小写字母或数字并以“_”分割,禁止以数字或大写字母开头,长度不超过20个字符。按照“v_业务名称_视图作用”进行命名。
正例:v_order_content, v_business_type, v_level3_name
反例:viewCity, vCustomerInfo, v_level_3_name
4.2操作规约
【强制】
1. 视图只提供功能查询,如果由两个以上基本表导出的,禁止对视图进行DML操作。
二、数据库SQL规范
(一) SQL规约
1.1操作规约
【强制】
1. 禁止使用 SELECT * 语句查询表,字符*具有特殊的含义,它将从指定的表中返回每一列。
说明:使用SELECT * 虽然更容易些,但增加很多不必要的消耗(CPU、IO、内存、网络带宽), 当表结构发生改变时,前端也需要更新,显式指定列后就可以很清楚查询中究竟返回了哪些列。
【强制】
2. DELETE操作需要增加WHERE条件,禁止直接使用DELETE语句直接操作表。
说明:不带 WHERE 子句的 DELETE 语句与TRUNCATE TABLE 在功能上与相同。
【强制】
3. 禁止使用TRUNCATE TABLE 语句。
说明:TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少,但TRUNCATE 无 事务且不触发TRIGGER,有可能造成事故,故不建议在开发代码中使用此语句。
【强制】
4. 不要使用 COUNT(列名)或 COUNT(常量)来替代 COUNT(*), COUNT(*) 是 SQL92定义的标准统 计行数的语法,跟数据库无关,跟NULL和非NULL有关。
说明:COUNT(*) 会统计值为NULL的行, 而COUNT(列名)不会统计此列为NULL值的行。
【强制】
5. 使用ISNULL()来判断是否为NULL值,注意NULL与任何值的比较都为NULL。
① NULL<>NULL的返回结果是NULL,不是false
② NULL=NULL的返回结果是NULL,不是true
③ NULL<>1的返回结果是NULL,而不是true
【推荐】
6. 利用延迟关联或者子查询优化超多分布场景。
正例:SELECT a.* FROM 表1 a, (SELECT id FROM 表1 WHERE 条件 LIMIT 100000, 10) b WHERE a.id = b.id;
说明: MySQL 并不是跳过offset行,而是取offset+N行, 然后返回放弃前offset行,返回N行,那当offset特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行SQL改写。先快速定位需要获取的id段,然后再关联。
【参考】
7. SQL语句中IN 包含的值不应该过多,操作能避免则避免,若实在避免不了,需要仔细评估 IN后边的集合元素数量,控制在1000个之内。
说明:在MYSQL中,可以使用连接(JOIN )查询来替代子查询。连接查询不需要建立临时表,其速度比子查询要快,如果查询中使用索引的话,性能会更好。
【参考】
8. 避免在WHERE语句后面使用函数。
说明:避免在SQL语句进行数学运算或者函数运算,容易将业务逻辑和DB耦合在一起。比如 WHERE条件后带 DATEADD 函数等常见查询。
【参考】
9. 避免GROUP BY 排序结果的消耗。
说明:默认情况,MySQL对所有的group by col1,col2进行排序。这与在查询中指定order by col1, col2 类似。如果查询中包括group by但用户想要避免排序结果的消耗,则可以使用order by null 禁止排序。