- 浏览: 498846 次
- 性别:
- 来自: 广州
文章分类
- 全部博客 (502)
- Java (70)
- Linux (10)
- 数据库 (38)
- 网络 (10)
- WEB (13)
- JSP (4)
- 互联网 (71)
- JavaScript (30)
- Spring MVC (19)
- HTML (13)
- CSS (3)
- AngularJS (18)
- Redis (5)
- Bootstrap CSS (1)
- ZooKeeper (4)
- kafka (6)
- 服务器缓存 (4)
- Storm (1)
- MongoDB (9)
- Spring boot (16)
- log4j (2)
- maven (3)
- nginx (5)
- Tomcat (2)
- Eclipse (4)
- Swagger (2)
- Netty (5)
- Dubbo (1)
- Docker (7)
- Hadoop (12)
- OAuth (1)
- webSocket (4)
- 服务器性能 (7)
- Session共享 (1)
- tieye修改 (1)
- 工作 (1)
- 有用的语录 (0)
- https (2)
- common (5)
- 产品开发管理 (1)
- CDN 工作原理 (1)
- APNS、GCM (1)
- 架构图 (3)
- 功能实现分析 (1)
- JMX (1)
- 服务器相关操作命令 (1)
- img02 (0)
- 服务器环境搭建 (9)
- goodMenuBook (1)
- CEInstantPot (0)
- 有用数据 (1)
- 百度地图WEB API (2)
- 正则表达式 (1)
- 样式例子 (2)
- staticRecipePressureCooker.zip (1)
- jCanvas (1)
- 网站攻击方法原理 (1)
- 架构设计 (3)
- 物联网相关 (3)
- 研发管理 (7)
- 技术需求点 (1)
- 计划 (1)
- spring cloud (11)
- 服务器开发的一些实用工具和方法 (1)
- 每天学到的技术点 (4)
- Guava (1)
- ERP 技术注意要点 (2)
- 微信小程序 (1)
- FineRepor (1)
- 收藏夹 (1)
- temp (5)
- 服务架构 (4)
- 任职资格方案 (0)
- osno_test (1)
- jquery相关 (3)
- mybatis (4)
- ueditor (1)
- VueJS (7)
- python (10)
- Spring EL (1)
- shiro (1)
- 前端开发原理与使用 (7)
- YARN (1)
- Spark (1)
- Hbase (2)
- Pig (2)
- 机器学习 (30)
- matplotlib (1)
- OpenCV (17)
- Hystrix (1)
- 公司 (1)
- miniui (4)
- 前端功能实现 (3)
- 前端插件 (1)
- 钉钉开发 (2)
- Jenkins (1)
- elasticSearch使用 (2)
- 技术规范 (4)
- 技术实现原理 (0)
最新评论
MySQL开发规范
设计规范
1.【推荐】字段允许适当冗余,以提高查询性能,但必须考虑数据一致。冗余字段应遵循:
不是频繁修改的字段。
不是 varchar 超长字段,更不能是 text 字段。
正例: 商品类目名称使用频率高,字段长度短,名称基本一成不变,可在相关联的表中冗余存 储类目名称,避免关联查询。
【推荐】单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。
说明:如果预计3年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。
【推荐】一个表的字段个数控制在30-50个字段以内;如果字段超过50个,可考虑将字段按冷热程度分表。
这样做虽然会给应用带来更多的代码开发量,但对于热表来说,这样做可以提升buffer利用率,减少IO,提升查询的效率。
【强制】字段字符集与表保持一致,不单独设置字符集。
相同含义的字段在不同表中应使用相同的名称,数据类型及长度必须保持一致。
【推荐】id必须是主键,每个表必须有主键,且保持增长趋势的, 小型系统可以依赖于 MySQL 的自增主键,大型系统或者需要分库分表时才使用 ID 生成器,如雪花生成器等。
【推荐】id类型没有特殊要求,必须使用bigint unsigned,禁止使用int,即使现在的数据量很小。id如果是数字类型的话,必须是8个字节。
方便对接外部系统,还有可能产生很多废数据
避免废弃数据对系统id的影响
未来分库分表,自动生成id,一般也是8个字节
【推荐】字段尽量设置为 NOT NULL, 为字段提供默认值。
如字符型的默认值为一个空字符值串'';
数值型默认值为数值 0;
逻辑型的默认值为数值 0;
【强制】每个字段和表必须提供清晰的注释。同时,如果修改字段含义或对字段表示的状态追加时,需要及时更新字段注释。
【推荐】关于时间的格式,推荐统一使用Unix时间戳格式
unix时间戳(Unix Timestamp)是从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数,不考虑闰秒。
因为MySQL 关于日期的类型有Date/ Datetime/ Timestamp三种类型。对于时间的随便选择会导致数据库开发和业务逻辑中遇到不同类型的转换时经常出现混乱或者难以察觉的bug.
unix时间戳在MySQL上可以直接使用bigint unsigned存储,避免各种时间类型的不一致。也方便后台和数据库关于时间的比较。
前端业务可以根据unix时间戳转换成自己需要的时间格式。
【推荐】更新数据表记录时,必须同时更新记录对应的gmt_modified字段值为当前时间。
Gmt: 格林威治时间
gmt_modified建议使用unix时间戳格式保存
【推荐】表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint( 1 表示是, 0 表示否)。
任何字段如果为非负数,必须是 unsigned。
注意: POJO 类中的任何布尔类型的变量,都不要加 is 前缀,所以,需要在<resultMap>设置从 is_xxx到 Xxx 的映射关系。数据库表示是与否的值,使用 tinyint 类型,坚持 is_xxx 的命名方式是为了明确其取值含义与取值范围。
正例: 表达逻辑删除的字段名 is_deleted, 1 表示删除, 0 表示未删除。
【推荐】表名、字段名必须使用小写字母或数字, 禁止出现数字开头,禁止两个下划线中间只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。
正例: midea_admin,rdc_config,level3_name 反例: MideaAdmin, rdcConfig, level_3_name
【推荐】表名不使用复数名词。
表名应该仅仅表示表里面的实体内容,不应该表示实体数量,对应于 DO 类名也是单数 形式,符合表达习惯。
正例: midea_user,rdc_config 反例: midea_users,rdc_configs
【强制】禁用保留字,如 desc、 range、 match、 delayed 等, 请参考 MySQL 官方保留字。
【推荐】 主键索引名为 pk关键字开头; 唯一索引名为 uk关键字开头; 普通索引名则为 idx关键字开头。
说明: pk 即 primary key; uk 即 unique key; idx 即 index 的简称。
【推荐】小数类型为 decimal,禁止使用 float 和 double。
说明: float 和 double 在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不 正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。
【推荐】如果存储的字符串长度几乎相等,使用 char 定长字符串类型。
【推荐】varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长度大于此值,定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索引效率。
【推荐】表必备五字段:id,创建人,创建时间,修改人,修改时间。
其中ID必为主键,类型为 bigint unsigned。 gmt_create为创建时间, gmt_modified为修改时间,两者的类型均unix时间戳,前者现在时表示主动创建,后者过去分词表示被动更新。Creator为创建人,modifier为修改人。
【推荐】表的命名最好是加上"业务名称_表的作用"。
正例: midea_task / force_project/trade_config
【推荐】库名与应用名称尽量一致。
【推荐】所有命名必须使用全名,有默认约定的除外,如果超过 30 个字符,使用缩写,请尽量名字易懂简短。如
description--> desc
information --> info;
address --> addr 等
【强制】业务应用禁止有 super 账号的存在
super权限很大,一般是DBA才会用到,会导致read only失效,原则上不提供给应用账号使用。
【推荐】IP地址的存储,ipv4尽量使用int unsigned来存储,而不要使用varchar(15)来存储,可以节省11字节,如果包括索引,可以节省22字节。
【推荐】不允许不同业务模块的表连接查询。
【强制】禁止在数据库中存储明文密码。
【推荐】比较重要的数据删除操作使用逻辑删除(UPDATE table SET is_deleteflag=1)代替物理删除(DELETE FROM table WHERE ..)
【推荐】InnoDB表数据量特别大的避免使用COUNT(*)操作,推荐计数统计实时要求较强可以使用redis,非实时统计可以使用单独统计表,定时更新。
【强制】事务语句执行完成之后必须及时提交。
事务长时间不提交,如果这时对事务相关表执行DDL操作,会出现等待元数据锁的提示。DDL语句被阻塞后,其他所有表上的正常操作(DML、SELECT)都会被阻塞。
【推荐】尽量避免或者拆分执行大事务。
无论在业务程序中,还是手动数据调整中都需要尽量避免执行大事务。对于影响行数过万的记录建议DBA审核后通过工具或脚本分批执行。
大事务的执行会给数据库稳定性带来很多问题。例如引起从库复制延迟、导致锁等待、系统脏数据 checkpoint 写入的性能抖动。
【推荐】对于历史数据、日志数据等数据量特别大的表(通常数据量超过千万,占用空间超过10G),需要应用根据业务特点,预估业务数据增涨速度,提前在应用代码中进行分表操作(同一MySQL实例下的分表操作实现不难)。
通常可以按照时间(按年、按月)进行数据分表,存在明显的数据热点,比如最近一年或者最近三个月的数据是热点数据。其他数据是历史数据。历史数据很少进行修改,一般只要提供给业务进行分析查询即可。这样可以把历史数据归档到其他实例上,提供可供实时查询的接口便可。
DBA有成熟的实现方案,主要是减少业务库中大量历史数据造成的磁盘空间紧张、数据备份、恢复慢的运维问题。
对于大表按规则进行分表也可提升业务并发读写性能。
【推荐】严格禁止单条记录超过8K
目前我们的DB一个page大小都设置为16K,当一条记录超过page的一半(8K)时,记录中的blob/varchar会在行外存储。存取时会有额外IO消耗。插入操作会锁住整个聚簇索引(X Index Lock),直到插入完成才释放。
【推荐】字段约束,对于字典类型的表,因数据量小,修改少,影响面大,应依赖数据库约束来确保数据质量。对于日志或流水型表,为了提升效率,可以释放放宽限制。
【强制】库名、表名、字段名禁止超过32个字符。
库名、表名、字段名支持最多64个字符,但为了统一规范、易于辨识以及减少传输量,禁止超过32个字符。
【推荐】Mysql数据库统一使用innodb存储引擎
原则上,业务范畴内的表都统一使用innodb存储引擎;如需使用其它存储引擎,需说明原因,并征得DBA同意。集团标准化部署设置默认使用innodb引擎。
【推荐】数据库设计不建议使用这样扩展字段:attr1,attr2,attr3…,业务需要什么字段使用时再添加。
使用扩展字段,扩展字段名字和注释不好维护。
新版本的数据库新增和删除字段成本相对比较低。
【推荐】关于建模中遇到字段命名规范有疑问请参考数字办已经制定规范。
数字办已经有规范相关字段命名的工具
类型规范
【推荐】表示状态字段(0-255)的使用 TINYINT UNSIGNED,禁止使用枚举类型,注释必须清晰地说明每个枚举的含义,以及是否支持多选等。
【推荐】表示boolean类型的都使用TINYINT(1)
因为MySQL本身是没有boolean类型的,在自动生成代码的时候,DO对象的字段就是boolean类型,例如 is_delete;其余所有时候都使用TINYINT(4)。
TINYINT(4),这个括号里面的数值并不是表示使用多大空间存储,而是最大显示宽度,并且只有字段指定zerofill时有用,没有zerofill,(m)就是无用的,例如id BIGINT ZEROFILL NOT NULL,所以建表时就使用默认就好了,不需要加括号了,除非有特殊需求,例如TINYINT(1)代表boolean类型。
TINYINT(1),TINYINT(4)都是存储一个字节,并不会因为括号里的数字改变。例如TINYINT(4)存储22则会显示0022,因为最大宽度为4,达不到的情况下用0来补充。
【参考】合适的字符存储长度,不但节约数据库表空间节约索引存储,更重要的是提升检索速度。
tinyint
1
无符号值: 0~255;有符号值: -128~127
smallint
2
无符号值: 0~65536;有符号值: -32768~32767
mediumint
3
无符号值: 0~16777215;有符号值: -8388608~8388607
int
4
无符号值: 0~4294967295;有符号值: -2147483648~2147483647
bigint
8
无符号值: 0~((2³²×²)-1);有符号值: -(2³²×²)/2 ~ (2³²×²)/2-1
【参考】非负的数字类型字段,都添加上UNSIGNED.
如可以使用INTUNSIGNED字段存IPV4
【参考】时间字段存储首先推荐1.7,如果确实要使用其他时间日期类型,不要使用字符串类型存储,日期使用DATE类型,年使用YEAR类型,日期时间使用DATETIME.
【参考】字符串VARCHAR(N), 其中 N表示字符个数,请尽量减少 N 的大小,参考:code VARCHAR(32);name VARCHAR(32);memo VARCHAR(512);
7.【参考】Blob和Text类型所存储的数据量大,删除和修改操作容易在数据表里产生大量的碎片,如果可以,应该尽量避免使用Blob或Text类型
索引规范
1.【推荐】业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引。
说明:不要以为唯一索引影响了insert速度,这个速度损耗可以忽略,但提高查找速度是明显的;另外,即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。
【推荐】主键最好由一个字段构成,最多不要超过3个,禁止超过3个字段的组合主键。
多列主键会导致其他索引体积膨胀,占用更多的空间,并降低性能。
使用多列业务主键的话,对主键来说,一般可认为是随机插入。
【强制】多表关联查询中,关联字段必须创建索引,关联字段的字段类型、字符集、校验集 保持一致。where 子句后面必须带上关联条件。
多表关联查询,关联字段如果没有索引,或者关联字段类型不一致,则会对多表进行全表扫描。这样对CPU和磁盘IO消耗是比较大的。如果是百万级以上的大表关联,则会严重影响数据库性能。where 子句后不带关联条件,则会导致 cross-join, MySQL 将使用大量的磁盘空间进行数据中间结果处理。严重情况下,/tmp 目录下的临时表空间文件会直接把所有数据盘占满,导致数据库无法写入、进而崩溃。
【强制】在varchar字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度即可。
索引的长度与区分度是一对矛盾体,一般对字符串类型数据,长度为20的索引,区分度会高达90%以上,可以使用count(distinct left(列名, 索引长度))/count(*)的区分度来确定。
【推荐】页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。
索引文件具有 B-Tree 的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引。
【强制】禁止冗余索引。
索引是双刃剑,会增加维护负担,增大IO压力。(a,b,c)、(a,b),后者为冗余索引。可以利用前缀索引来达到加速目的,减轻维护负担。
【推荐】如果有order by 的场景,请注意利用索引的有序性。order by 最后的字段是组合索引的一部分,并且放在索引组合顺序的最后,避免出现 file_sort 的情况,影响查询性能。
正例:where a=? and b=? order by c; 索引:a_b_c 反例:索引中有范围查找,那么索引有序性无法利用,如:WHERE a>10 ORDER BY b; 索引 a_b 无法排序。
【推荐】利用覆盖索引来进行查询操作,避免回表
如果一本书需要知道第11章是什么标题,会翻开第11章对应的那一页吗?目录浏览一下就好,这个目录就是起到覆盖索引的作用。
正例:能够建立索引的种类:主键索引、唯一索引、普通索引,而覆盖索引是一种查询的效果,用explain的结果,extra列会出现:using index。
【推荐】利用延迟关联或者子查询优化超多分页场景。
MySQL并不是跳过 offset 行,而是取 offset+N 行,然后返回放弃前 offset 行,返回 N 行,那当 offset 特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行 SQL 改写。
正例:先快速定位需要获取的 id 段,然后再关联: SELECT a.* FROM 表 1 a, (select id from 表 1 where 条件 LIMIT 100000,20 ) b where a.id=b.id
【推荐】SQL 性能优化的目标:至少要达到 range 级别,要求是 ref 级别,如果可以是 consts 最好。
range 对索引进行范围检索。
ref 指的是使用普通的索引(normal index)。
consts 单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取到数据。
反例: explain 表的结果,type=index,索引物理文件全扫描,速度非常慢,这个 index 级 别比较 range 还低,与全表扫描是小巫见大巫。
【推荐】建组合索引的时候,区分度最高的在最左边。
存在非等号和等号混合判断条件时,在建索引时,请把等号条件的列前置。如:where a>? and b=? 那么即使 a 的区分度更高,也必须把 b 放在索引的最前列。
正例:如果 where a=? and b=? ,a 列的几乎接近于唯一值,那么只需要单建 idx_a 索引即可
【推荐】防止因字段类型不同造成的隐式转换,导致索引失效。
反例:为了说明这个问题我们制造一个反例:创建一张用户表test_user,其中USER_ID我们设置为varchar类型并加上唯一索引。
CREATE TABLE `test_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` varchar(11) DEFAULT NULL COMMENT '用户账号',
`user_name` varchar(255) DEFAULT NULL COMMENT '用户名',
`age` int(5) DEFAULT NULL COMMENT '年龄',
`comment` varchar(255) DEFAULT NULL COMMENT '简介',
PRIMARY KEY (`id`),
UNIQUE KEY `UNIQUE_USER_ID` (`user_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
插入几条测试数据:
INSERT INTO test_user (user_id, user_name, age, comment) VALUES ('111', '张三',18,'法外狂徒' ), ('222', '李四',19,'燕南天' ) ,('333', '王五',145,'隔壁老王' ) ;
然后我们执行EXPLAIN SQL 看一下索引命中情况:
EXPLAIN SELECT * FROM test_user WHERE user_id = 1111;
得到如下结果:
我们会发现,虽然user_id字段加了索引,但是当我们使用数值类型进行查询时没走索引,而是进行了全表扫描。
【参考】创建索引时避免有如下极端误解
宁滥勿缺。认为一个查询就需要建一个索引。
宁缺勿滥。认为索引会消耗空间、严重拖慢更新和新增速度。
抵制惟一索引。认为业务的惟一性一律需要在应用层通过"先查后插"方式解决。
【参考】其他注意事项
索引占磁盘空间,不要重复的索引,尽量短 。
只给常用的查询条件加索引。
过滤性高的列建索引,区分度不高的列不建索引。
唯一的记录添加唯一索引。
频繁更新的列不要建索引。
不要对索引列运算。
同样过滤效果下,保持索引长度最小。
合理利用组合索引,注意索引字段先后顺序。
多列组合索引,过滤性高的字段最前。
order by 字段建立索引,避免 filesort。
组合索引,不同的排序顺序不能使用索引。
<>!=无法使用索引。
SQL语句规范
1. 【强制】不要使用 count(列名)或 count(常量)来替代 count(),count()是 SQL92 定义的 标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。
count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。
2.【强制】count(distinct col) 计算该列除 NULL 之外的不重复行数,
count(distinct col1, col2) 如果其中一列全为NULL,那么即使另一列有不同的值,也返回为0。
【强制】当某一列col的值全是 NULL 时,count(col)的返回结果为 0,但 sum(col)的返回结果为 NULL,因此使用 sum()时需注意 NPE 问题。
正例:可以使用如下方式避免sum的NPE问题:
SELECT IF(ISNULL(SUM(g)), 0, SUM(g)) FROM table;
【强制】使用 ISNULL()来判断是否为 NULL 值。 说明:NULL 与任何值的直接比较都为 NULL。
NULL<>NULL的返回结果是NULL,而不是false。
NULL=NULL的返回结果是NULL,而不是true。
NULL<>1的返回结果是NULL,而不是true。
【强制】 在代码中写分页查询逻辑时,若 count 为 0 应直接返回,避免执行后面的分页语句。
【强制】不得使用外键与级联,一切外键概念必须在应用层解决。
说明:以学生和成绩的关系为例,学生表中的 studentid 是主键,那么成绩表中的 studentid 则为外键。如果更新学生表中的 studentid,同时触发成绩表中的 studentid 更新,即为 级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻 塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。
【强制】在MySQL中禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。
【推荐】数据订正时,删除和修改记录时,要先 select,避免出现误删除,确认无误才能执行更新语句。
【推荐】逻辑运算如果能在业务层解决,尽量不在数据库做运算,尽可能简单使用MySQL。
md5() 或Order by Rand()或计算字段等操作不在数据库表上进行
【推荐】不建议将数据字典表进行连接查询
数据字典表可以查询到cache或者在内存中对相关列进行替换。
【推荐】用UNION ALL代替UNION
UNION ALL不需要对结果集再进行排序。
【推荐】in操作能避免则避免,若实在避免不了,需要仔细评估 in 后边的集合元素数量,控制在 1000 个之内。
【推荐】减少与数据库交互次数,尽量采用批量SQL语句
使用下面的语句来减少和db的交互次数:
1.INSERT ... ON DUPLICATE KEY UPDATE
2.REPLACE INTO
3.INSERT IGNORE
4.INSERT INTO VALUES()
【推荐】拆分复杂SQL为多个小SQL,避免大事务
简单的SQL容易使用到MySQL的QUERY CACHE;减少锁表时间特别是MyISAM;可以使用多核CPU。
【强制】禁止使用HINT
HINT给DBA维护带来很多的不便,MySQL使用CBO优化器,会综合考虑如果执行SQL。
【推荐】禁止使用分区表
分区表对分区键有严格要求;分区表在表变大后,执行DDL、SHARDING、单表恢复等都变得更加困难。因此禁止使用分区表,并建议业务端手动SHARDING。
【参考】如果有全球化需要,所有的字符存储与表示,均以 utf8mb4 编码,注意字符统计函数 的区别。
SELECT LENGTH("轻松工作"); 返回为12
SELECT CHARACTER_LENGTH("轻松工作"); 返回为4
utf8mb4字符集可以存储表情等字符。
【参考】TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少,但 TRUNCATE 无事务且不触发trigger,有可能造成事故,故不建议在开发代码中使用此语句。
说明:TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同。
【推荐】不要写一个大而全的数据更新接口。
传入为 POJO 类,不管是不是自己的目标更新字 段,都进行 update table set c1=value1,c2=value2,c3=value3; 这是不对的。执行 SQL 时,不要更新无改动的字段,一是易出错;二是效率低;三是增加 binlog 存储。
【推荐】如果可以放到业务逻辑里面,避免使用 GROUP BY、DISTINCT 、ORDER BY等语句的使用,避免联表查询和子查询,以GROUP BY为列:
order by的实现有两种方式,主要就是按用没用到索引来区分,
根据索引字段排序,利用索引取出的数据已经是排好序的,直接返回给客户端;
没有用到索引,将取出的数据进行一次排序操作后返回给客户端。这时会大量耗费数据库服务器的计算性能。
【推荐】针对索引字段使用 >, >=, =, <, <=, IF NULL 和 BETWEEN 将会 使用索引,如果对某个索引字段进行 LIKE 查询,使用 LIKE '%abc%' 不能使用索引,使用 LIKE 'abc%' 将能够使用索引
【推荐】如果在 SQL 里使用了 MySQL部分自带函数,而且自带函数使用到索引字段,索引将失效
【推荐】避免直接使用 select *,只取需要的字段,增加使用覆盖索引使用的可能。
【推荐】连表查询的情况下,要确保关联条件的数据类型一致,避免嵌套子查询。
<span style="color: #ff0000"><strong>【</strong></span><span style="color: #ffc000">推荐</span><span style="color: #ff0000"><strong>】</strong></span>where 语句中尽量不要使用 CASE [WHEN]条件,
反例:SELECT * FROM logs WHERE CASE WHEN in_username = "" THEN true ELSE username LIKE CONCAT("%",in_username,"%") END AND CASE WHEN time = "" THEN true ELSE time = in_time END ORDER BY id DESC LIMIT in_page,in_limit;
其中in_username,in_time是传入的参数,这样写的后果是当数据量很大的情况下查询会很慢,原因是WHERE条件语句中case when后面的username和time字段的索引将失效!
【推荐】当只要一行数据时使用 LIMIT 1,因为LIMIT 1能有效缩短查询时间。下面是查询的结果对比。
【强制】不能使用触发器
MySQL中触发器是行触发的,每次增加、修改或者删除记录都会触发进行处理,编写过于复杂的触发器或者增加过多的触发器对记录的插入、更新、删除操作会有比较严重的影响,因此不要将应用的处理逻辑过多地依赖于触发器来处理。触发器的功能通常可以用其他方式实现,确实需要采用触发器,请联系DBA进行确认。
【推荐】避免使用视图
视图可能导致执行计划错乱,影响SQL运行效率。对视图的修改,数据库必须把它转化为对基本表的信息修改,不便于维护。
【推荐】对同一个表的多次alter操作必须合并为一次操作
MySQL对表的修改绝大部分操作都需要锁表并重建表,而锁表则会对线上业务造成影响。为减少这种影响,必须把对表的多次alter操作合并为一次操作。例如,要给表t增加一个字段b,同时给已有的字段aa建立索引,
通常的做法分为两步: alter table t add column b varchar(10);
然后增加索引: alter table t add index idx_aa(aa);
正确的做法是: alter table t add column b varchar(10),add index idx_aa(aa);
【推荐】避免多余的排序。使用GROUP BY 时,默认会进行排序,当你不需要排序时,可以使用order by null。
例如Select a.OwnerUserID,count(星星) cnt from DP_MessageList a group by a.OwnerUserID order by null;
Sharding-jdbc开发规范
【强制】设计、类型、索引和SQL上首先应该遵循MySQL规范所提内容
【推荐】如果数据有时效性,则建议按时间分表或者分库 。如果所有数据热度相同,则建议根据hash或者其他手段分库分表。
【推荐】如果多个独立业务需要共用一台DB的话,不同业务的表必须放在不同的database(schema)里。
主要考虑管理便捷和安全。
【强制】对于分库分表,每一个表的索引结构及名称都必须一致。
【推荐】分库分表数量必须按2的N次方,便于未来实现秒级扩容方案。
【强制】设计阶段,必须使用能路由至多节点(即支持分片)的方式进行数据库设计。
sharding-jdbc支持路由至单数据节点和路由至多数据节点的方式进行路由连接,路由致单数据节点的方式能100%兼容MySQL, 而路由至多数据节点则无法做到100%兼容。但是,在使用sharding-jdbc时,在设计阶段,必须使用路由至多节点的方式进行数据库设计。
正例:自定义的多个真实表,例如:
db00├── t_order_00└── t_order_01 db01├── t_order_02├── t_order_03└── t_order_04
数据节点配置如下:
db00.t_order_00, db00.t_order_01, db01.t_order_02, db01.t_order_03,db01.t_order_04
【推荐】分库分表的数据库或者表的命名规范,建议后缀采用统一的以数字结尾的格式,如位数不足,通过补充0保证位数一致。如:t_user 表分512 个表。
正例:t_user_001, t_user_002 ….t_user_512.
反例:t_user_1, t_user_2 …. t_user_512.
【推荐】在考虑sharding-jdbc进行数据库设计时,请仔细考虑如何分片,并最好使用单一键作为分片键
单一键作为分片键,其对应的分片算法是标准分片算法( StandardShardingAlgorithm)。单一分片算法有个好处是它可以使用SQL的 =、IN、BETWEEN AND、>、<、>=、<=等关键进行快速匹配和运算。
可以考虑通过在分片键之外其他列包含分片信息,通过自定义ComplexShardingAlgorithm进行特殊处理,比如分片键是user_id,可以将Sharding信息作为order_num 的前缀来实现分片逻辑。
【推荐】查询分页充分利用ID的连续性或者区间性进行优化。
在sharding-jdbc中偏移量过大的分页会,查询偏移量过大的分页会导致数据库获取数据性能低下。
由于LIMIT并不能通过索引查询数据,因此如果可以保证ID的连续性,通过ID进行分页是比较好的解决方案
或通过记录上次查询结果的最后一条记录的ID进行下一页的查询:
反例: SELECT * FROM t_order ORDER BY id LIMIT 1000000, 10
正例: SELECT * FROM t_order WHERE id > 100000 AND id <= 100010 ORDER BY id
正例: SELECT * FROM t_order WHERE id > 100000 LIMIT 10
【强制】除了使用直接指定单表的直接路由,不要使用hint. 随便使用hint会使代码变得复杂和难以理解。
原因是这样,把处理的逻辑一半写在自己的业务逻辑里,一半依靠sharding-jdbc的hint, 只会是系统更难理解和复杂,建议分多个SQL在业务逻辑,也可以跨微服务处理。
【强制】访问分片表的SQL必须包含分片键。
【推荐】分片规则一致的主表和子表建议配置为绑定表,这样联表查询不会笛卡尔积现象,关联查询效率将大大提升。
设计规范
1.【推荐】字段允许适当冗余,以提高查询性能,但必须考虑数据一致。冗余字段应遵循:
不是频繁修改的字段。
不是 varchar 超长字段,更不能是 text 字段。
正例: 商品类目名称使用频率高,字段长度短,名称基本一成不变,可在相关联的表中冗余存 储类目名称,避免关联查询。
【推荐】单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。
说明:如果预计3年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。
【推荐】一个表的字段个数控制在30-50个字段以内;如果字段超过50个,可考虑将字段按冷热程度分表。
这样做虽然会给应用带来更多的代码开发量,但对于热表来说,这样做可以提升buffer利用率,减少IO,提升查询的效率。
【强制】字段字符集与表保持一致,不单独设置字符集。
相同含义的字段在不同表中应使用相同的名称,数据类型及长度必须保持一致。
【推荐】id必须是主键,每个表必须有主键,且保持增长趋势的, 小型系统可以依赖于 MySQL 的自增主键,大型系统或者需要分库分表时才使用 ID 生成器,如雪花生成器等。
【推荐】id类型没有特殊要求,必须使用bigint unsigned,禁止使用int,即使现在的数据量很小。id如果是数字类型的话,必须是8个字节。
方便对接外部系统,还有可能产生很多废数据
避免废弃数据对系统id的影响
未来分库分表,自动生成id,一般也是8个字节
【推荐】字段尽量设置为 NOT NULL, 为字段提供默认值。
如字符型的默认值为一个空字符值串'';
数值型默认值为数值 0;
逻辑型的默认值为数值 0;
【强制】每个字段和表必须提供清晰的注释。同时,如果修改字段含义或对字段表示的状态追加时,需要及时更新字段注释。
【推荐】关于时间的格式,推荐统一使用Unix时间戳格式
unix时间戳(Unix Timestamp)是从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数,不考虑闰秒。
因为MySQL 关于日期的类型有Date/ Datetime/ Timestamp三种类型。对于时间的随便选择会导致数据库开发和业务逻辑中遇到不同类型的转换时经常出现混乱或者难以察觉的bug.
unix时间戳在MySQL上可以直接使用bigint unsigned存储,避免各种时间类型的不一致。也方便后台和数据库关于时间的比较。
前端业务可以根据unix时间戳转换成自己需要的时间格式。
【推荐】更新数据表记录时,必须同时更新记录对应的gmt_modified字段值为当前时间。
Gmt: 格林威治时间
gmt_modified建议使用unix时间戳格式保存
【推荐】表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint( 1 表示是, 0 表示否)。
任何字段如果为非负数,必须是 unsigned。
注意: POJO 类中的任何布尔类型的变量,都不要加 is 前缀,所以,需要在<resultMap>设置从 is_xxx到 Xxx 的映射关系。数据库表示是与否的值,使用 tinyint 类型,坚持 is_xxx 的命名方式是为了明确其取值含义与取值范围。
正例: 表达逻辑删除的字段名 is_deleted, 1 表示删除, 0 表示未删除。
【推荐】表名、字段名必须使用小写字母或数字, 禁止出现数字开头,禁止两个下划线中间只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。
正例: midea_admin,rdc_config,level3_name 反例: MideaAdmin, rdcConfig, level_3_name
【推荐】表名不使用复数名词。
表名应该仅仅表示表里面的实体内容,不应该表示实体数量,对应于 DO 类名也是单数 形式,符合表达习惯。
正例: midea_user,rdc_config 反例: midea_users,rdc_configs
【强制】禁用保留字,如 desc、 range、 match、 delayed 等, 请参考 MySQL 官方保留字。
【推荐】 主键索引名为 pk关键字开头; 唯一索引名为 uk关键字开头; 普通索引名则为 idx关键字开头。
说明: pk 即 primary key; uk 即 unique key; idx 即 index 的简称。
【推荐】小数类型为 decimal,禁止使用 float 和 double。
说明: float 和 double 在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不 正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。
【推荐】如果存储的字符串长度几乎相等,使用 char 定长字符串类型。
【推荐】varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长度大于此值,定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索引效率。
【推荐】表必备五字段:id,创建人,创建时间,修改人,修改时间。
其中ID必为主键,类型为 bigint unsigned。 gmt_create为创建时间, gmt_modified为修改时间,两者的类型均unix时间戳,前者现在时表示主动创建,后者过去分词表示被动更新。Creator为创建人,modifier为修改人。
【推荐】表的命名最好是加上"业务名称_表的作用"。
正例: midea_task / force_project/trade_config
【推荐】库名与应用名称尽量一致。
【推荐】所有命名必须使用全名,有默认约定的除外,如果超过 30 个字符,使用缩写,请尽量名字易懂简短。如
description--> desc
information --> info;
address --> addr 等
【强制】业务应用禁止有 super 账号的存在
super权限很大,一般是DBA才会用到,会导致read only失效,原则上不提供给应用账号使用。
【推荐】IP地址的存储,ipv4尽量使用int unsigned来存储,而不要使用varchar(15)来存储,可以节省11字节,如果包括索引,可以节省22字节。
【推荐】不允许不同业务模块的表连接查询。
【强制】禁止在数据库中存储明文密码。
【推荐】比较重要的数据删除操作使用逻辑删除(UPDATE table SET is_deleteflag=1)代替物理删除(DELETE FROM table WHERE ..)
【推荐】InnoDB表数据量特别大的避免使用COUNT(*)操作,推荐计数统计实时要求较强可以使用redis,非实时统计可以使用单独统计表,定时更新。
【强制】事务语句执行完成之后必须及时提交。
事务长时间不提交,如果这时对事务相关表执行DDL操作,会出现等待元数据锁的提示。DDL语句被阻塞后,其他所有表上的正常操作(DML、SELECT)都会被阻塞。
【推荐】尽量避免或者拆分执行大事务。
无论在业务程序中,还是手动数据调整中都需要尽量避免执行大事务。对于影响行数过万的记录建议DBA审核后通过工具或脚本分批执行。
大事务的执行会给数据库稳定性带来很多问题。例如引起从库复制延迟、导致锁等待、系统脏数据 checkpoint 写入的性能抖动。
【推荐】对于历史数据、日志数据等数据量特别大的表(通常数据量超过千万,占用空间超过10G),需要应用根据业务特点,预估业务数据增涨速度,提前在应用代码中进行分表操作(同一MySQL实例下的分表操作实现不难)。
通常可以按照时间(按年、按月)进行数据分表,存在明显的数据热点,比如最近一年或者最近三个月的数据是热点数据。其他数据是历史数据。历史数据很少进行修改,一般只要提供给业务进行分析查询即可。这样可以把历史数据归档到其他实例上,提供可供实时查询的接口便可。
DBA有成熟的实现方案,主要是减少业务库中大量历史数据造成的磁盘空间紧张、数据备份、恢复慢的运维问题。
对于大表按规则进行分表也可提升业务并发读写性能。
【推荐】严格禁止单条记录超过8K
目前我们的DB一个page大小都设置为16K,当一条记录超过page的一半(8K)时,记录中的blob/varchar会在行外存储。存取时会有额外IO消耗。插入操作会锁住整个聚簇索引(X Index Lock),直到插入完成才释放。
【推荐】字段约束,对于字典类型的表,因数据量小,修改少,影响面大,应依赖数据库约束来确保数据质量。对于日志或流水型表,为了提升效率,可以释放放宽限制。
【强制】库名、表名、字段名禁止超过32个字符。
库名、表名、字段名支持最多64个字符,但为了统一规范、易于辨识以及减少传输量,禁止超过32个字符。
【推荐】Mysql数据库统一使用innodb存储引擎
原则上,业务范畴内的表都统一使用innodb存储引擎;如需使用其它存储引擎,需说明原因,并征得DBA同意。集团标准化部署设置默认使用innodb引擎。
【推荐】数据库设计不建议使用这样扩展字段:attr1,attr2,attr3…,业务需要什么字段使用时再添加。
使用扩展字段,扩展字段名字和注释不好维护。
新版本的数据库新增和删除字段成本相对比较低。
【推荐】关于建模中遇到字段命名规范有疑问请参考数字办已经制定规范。
数字办已经有规范相关字段命名的工具
类型规范
【推荐】表示状态字段(0-255)的使用 TINYINT UNSIGNED,禁止使用枚举类型,注释必须清晰地说明每个枚举的含义,以及是否支持多选等。
【推荐】表示boolean类型的都使用TINYINT(1)
因为MySQL本身是没有boolean类型的,在自动生成代码的时候,DO对象的字段就是boolean类型,例如 is_delete;其余所有时候都使用TINYINT(4)。
TINYINT(4),这个括号里面的数值并不是表示使用多大空间存储,而是最大显示宽度,并且只有字段指定zerofill时有用,没有zerofill,(m)就是无用的,例如id BIGINT ZEROFILL NOT NULL,所以建表时就使用默认就好了,不需要加括号了,除非有特殊需求,例如TINYINT(1)代表boolean类型。
TINYINT(1),TINYINT(4)都是存储一个字节,并不会因为括号里的数字改变。例如TINYINT(4)存储22则会显示0022,因为最大宽度为4,达不到的情况下用0来补充。
【参考】合适的字符存储长度,不但节约数据库表空间节约索引存储,更重要的是提升检索速度。
tinyint
1
无符号值: 0~255;有符号值: -128~127
smallint
2
无符号值: 0~65536;有符号值: -32768~32767
mediumint
3
无符号值: 0~16777215;有符号值: -8388608~8388607
int
4
无符号值: 0~4294967295;有符号值: -2147483648~2147483647
bigint
8
无符号值: 0~((2³²×²)-1);有符号值: -(2³²×²)/2 ~ (2³²×²)/2-1
【参考】非负的数字类型字段,都添加上UNSIGNED.
如可以使用INTUNSIGNED字段存IPV4
【参考】时间字段存储首先推荐1.7,如果确实要使用其他时间日期类型,不要使用字符串类型存储,日期使用DATE类型,年使用YEAR类型,日期时间使用DATETIME.
【参考】字符串VARCHAR(N), 其中 N表示字符个数,请尽量减少 N 的大小,参考:code VARCHAR(32);name VARCHAR(32);memo VARCHAR(512);
7.【参考】Blob和Text类型所存储的数据量大,删除和修改操作容易在数据表里产生大量的碎片,如果可以,应该尽量避免使用Blob或Text类型
索引规范
1.【推荐】业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引。
说明:不要以为唯一索引影响了insert速度,这个速度损耗可以忽略,但提高查找速度是明显的;另外,即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。
【推荐】主键最好由一个字段构成,最多不要超过3个,禁止超过3个字段的组合主键。
多列主键会导致其他索引体积膨胀,占用更多的空间,并降低性能。
使用多列业务主键的话,对主键来说,一般可认为是随机插入。
【强制】多表关联查询中,关联字段必须创建索引,关联字段的字段类型、字符集、校验集 保持一致。where 子句后面必须带上关联条件。
多表关联查询,关联字段如果没有索引,或者关联字段类型不一致,则会对多表进行全表扫描。这样对CPU和磁盘IO消耗是比较大的。如果是百万级以上的大表关联,则会严重影响数据库性能。where 子句后不带关联条件,则会导致 cross-join, MySQL 将使用大量的磁盘空间进行数据中间结果处理。严重情况下,/tmp 目录下的临时表空间文件会直接把所有数据盘占满,导致数据库无法写入、进而崩溃。
【强制】在varchar字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度即可。
索引的长度与区分度是一对矛盾体,一般对字符串类型数据,长度为20的索引,区分度会高达90%以上,可以使用count(distinct left(列名, 索引长度))/count(*)的区分度来确定。
【推荐】页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。
索引文件具有 B-Tree 的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引。
【强制】禁止冗余索引。
索引是双刃剑,会增加维护负担,增大IO压力。(a,b,c)、(a,b),后者为冗余索引。可以利用前缀索引来达到加速目的,减轻维护负担。
【推荐】如果有order by 的场景,请注意利用索引的有序性。order by 最后的字段是组合索引的一部分,并且放在索引组合顺序的最后,避免出现 file_sort 的情况,影响查询性能。
正例:where a=? and b=? order by c; 索引:a_b_c 反例:索引中有范围查找,那么索引有序性无法利用,如:WHERE a>10 ORDER BY b; 索引 a_b 无法排序。
【推荐】利用覆盖索引来进行查询操作,避免回表
如果一本书需要知道第11章是什么标题,会翻开第11章对应的那一页吗?目录浏览一下就好,这个目录就是起到覆盖索引的作用。
正例:能够建立索引的种类:主键索引、唯一索引、普通索引,而覆盖索引是一种查询的效果,用explain的结果,extra列会出现:using index。
【推荐】利用延迟关联或者子查询优化超多分页场景。
MySQL并不是跳过 offset 行,而是取 offset+N 行,然后返回放弃前 offset 行,返回 N 行,那当 offset 特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行 SQL 改写。
正例:先快速定位需要获取的 id 段,然后再关联: SELECT a.* FROM 表 1 a, (select id from 表 1 where 条件 LIMIT 100000,20 ) b where a.id=b.id
【推荐】SQL 性能优化的目标:至少要达到 range 级别,要求是 ref 级别,如果可以是 consts 最好。
range 对索引进行范围检索。
ref 指的是使用普通的索引(normal index)。
consts 单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取到数据。
反例: explain 表的结果,type=index,索引物理文件全扫描,速度非常慢,这个 index 级 别比较 range 还低,与全表扫描是小巫见大巫。
【推荐】建组合索引的时候,区分度最高的在最左边。
存在非等号和等号混合判断条件时,在建索引时,请把等号条件的列前置。如:where a>? and b=? 那么即使 a 的区分度更高,也必须把 b 放在索引的最前列。
正例:如果 where a=? and b=? ,a 列的几乎接近于唯一值,那么只需要单建 idx_a 索引即可
【推荐】防止因字段类型不同造成的隐式转换,导致索引失效。
反例:为了说明这个问题我们制造一个反例:创建一张用户表test_user,其中USER_ID我们设置为varchar类型并加上唯一索引。
CREATE TABLE `test_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` varchar(11) DEFAULT NULL COMMENT '用户账号',
`user_name` varchar(255) DEFAULT NULL COMMENT '用户名',
`age` int(5) DEFAULT NULL COMMENT '年龄',
`comment` varchar(255) DEFAULT NULL COMMENT '简介',
PRIMARY KEY (`id`),
UNIQUE KEY `UNIQUE_USER_ID` (`user_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
插入几条测试数据:
INSERT INTO test_user (user_id, user_name, age, comment) VALUES ('111', '张三',18,'法外狂徒' ), ('222', '李四',19,'燕南天' ) ,('333', '王五',145,'隔壁老王' ) ;
然后我们执行EXPLAIN SQL 看一下索引命中情况:
EXPLAIN SELECT * FROM test_user WHERE user_id = 1111;
得到如下结果:
我们会发现,虽然user_id字段加了索引,但是当我们使用数值类型进行查询时没走索引,而是进行了全表扫描。
【参考】创建索引时避免有如下极端误解
宁滥勿缺。认为一个查询就需要建一个索引。
宁缺勿滥。认为索引会消耗空间、严重拖慢更新和新增速度。
抵制惟一索引。认为业务的惟一性一律需要在应用层通过"先查后插"方式解决。
【参考】其他注意事项
索引占磁盘空间,不要重复的索引,尽量短 。
只给常用的查询条件加索引。
过滤性高的列建索引,区分度不高的列不建索引。
唯一的记录添加唯一索引。
频繁更新的列不要建索引。
不要对索引列运算。
同样过滤效果下,保持索引长度最小。
合理利用组合索引,注意索引字段先后顺序。
多列组合索引,过滤性高的字段最前。
order by 字段建立索引,避免 filesort。
组合索引,不同的排序顺序不能使用索引。
<>!=无法使用索引。
SQL语句规范
1. 【强制】不要使用 count(列名)或 count(常量)来替代 count(),count()是 SQL92 定义的 标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。
count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。
2.【强制】count(distinct col) 计算该列除 NULL 之外的不重复行数,
count(distinct col1, col2) 如果其中一列全为NULL,那么即使另一列有不同的值,也返回为0。
【强制】当某一列col的值全是 NULL 时,count(col)的返回结果为 0,但 sum(col)的返回结果为 NULL,因此使用 sum()时需注意 NPE 问题。
正例:可以使用如下方式避免sum的NPE问题:
SELECT IF(ISNULL(SUM(g)), 0, SUM(g)) FROM table;
【强制】使用 ISNULL()来判断是否为 NULL 值。 说明:NULL 与任何值的直接比较都为 NULL。
NULL<>NULL的返回结果是NULL,而不是false。
NULL=NULL的返回结果是NULL,而不是true。
NULL<>1的返回结果是NULL,而不是true。
【强制】 在代码中写分页查询逻辑时,若 count 为 0 应直接返回,避免执行后面的分页语句。
【强制】不得使用外键与级联,一切外键概念必须在应用层解决。
说明:以学生和成绩的关系为例,学生表中的 studentid 是主键,那么成绩表中的 studentid 则为外键。如果更新学生表中的 studentid,同时触发成绩表中的 studentid 更新,即为 级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻 塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。
【强制】在MySQL中禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。
【推荐】数据订正时,删除和修改记录时,要先 select,避免出现误删除,确认无误才能执行更新语句。
【推荐】逻辑运算如果能在业务层解决,尽量不在数据库做运算,尽可能简单使用MySQL。
md5() 或Order by Rand()或计算字段等操作不在数据库表上进行
【推荐】不建议将数据字典表进行连接查询
数据字典表可以查询到cache或者在内存中对相关列进行替换。
【推荐】用UNION ALL代替UNION
UNION ALL不需要对结果集再进行排序。
【推荐】in操作能避免则避免,若实在避免不了,需要仔细评估 in 后边的集合元素数量,控制在 1000 个之内。
【推荐】减少与数据库交互次数,尽量采用批量SQL语句
使用下面的语句来减少和db的交互次数:
1.INSERT ... ON DUPLICATE KEY UPDATE
2.REPLACE INTO
3.INSERT IGNORE
4.INSERT INTO VALUES()
【推荐】拆分复杂SQL为多个小SQL,避免大事务
简单的SQL容易使用到MySQL的QUERY CACHE;减少锁表时间特别是MyISAM;可以使用多核CPU。
【强制】禁止使用HINT
HINT给DBA维护带来很多的不便,MySQL使用CBO优化器,会综合考虑如果执行SQL。
【推荐】禁止使用分区表
分区表对分区键有严格要求;分区表在表变大后,执行DDL、SHARDING、单表恢复等都变得更加困难。因此禁止使用分区表,并建议业务端手动SHARDING。
【参考】如果有全球化需要,所有的字符存储与表示,均以 utf8mb4 编码,注意字符统计函数 的区别。
SELECT LENGTH("轻松工作"); 返回为12
SELECT CHARACTER_LENGTH("轻松工作"); 返回为4
utf8mb4字符集可以存储表情等字符。
【参考】TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少,但 TRUNCATE 无事务且不触发trigger,有可能造成事故,故不建议在开发代码中使用此语句。
说明:TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同。
【推荐】不要写一个大而全的数据更新接口。
传入为 POJO 类,不管是不是自己的目标更新字 段,都进行 update table set c1=value1,c2=value2,c3=value3; 这是不对的。执行 SQL 时,不要更新无改动的字段,一是易出错;二是效率低;三是增加 binlog 存储。
【推荐】如果可以放到业务逻辑里面,避免使用 GROUP BY、DISTINCT 、ORDER BY等语句的使用,避免联表查询和子查询,以GROUP BY为列:
order by的实现有两种方式,主要就是按用没用到索引来区分,
根据索引字段排序,利用索引取出的数据已经是排好序的,直接返回给客户端;
没有用到索引,将取出的数据进行一次排序操作后返回给客户端。这时会大量耗费数据库服务器的计算性能。
【推荐】针对索引字段使用 >, >=, =, <, <=, IF NULL 和 BETWEEN 将会 使用索引,如果对某个索引字段进行 LIKE 查询,使用 LIKE '%abc%' 不能使用索引,使用 LIKE 'abc%' 将能够使用索引
【推荐】如果在 SQL 里使用了 MySQL部分自带函数,而且自带函数使用到索引字段,索引将失效
【推荐】避免直接使用 select *,只取需要的字段,增加使用覆盖索引使用的可能。
【推荐】连表查询的情况下,要确保关联条件的数据类型一致,避免嵌套子查询。
<span style="color: #ff0000"><strong>【</strong></span><span style="color: #ffc000">推荐</span><span style="color: #ff0000"><strong>】</strong></span>where 语句中尽量不要使用 CASE [WHEN]条件,
反例:SELECT * FROM logs WHERE CASE WHEN in_username = "" THEN true ELSE username LIKE CONCAT("%",in_username,"%") END AND CASE WHEN time = "" THEN true ELSE time = in_time END ORDER BY id DESC LIMIT in_page,in_limit;
其中in_username,in_time是传入的参数,这样写的后果是当数据量很大的情况下查询会很慢,原因是WHERE条件语句中case when后面的username和time字段的索引将失效!
【推荐】当只要一行数据时使用 LIMIT 1,因为LIMIT 1能有效缩短查询时间。下面是查询的结果对比。
【强制】不能使用触发器
MySQL中触发器是行触发的,每次增加、修改或者删除记录都会触发进行处理,编写过于复杂的触发器或者增加过多的触发器对记录的插入、更新、删除操作会有比较严重的影响,因此不要将应用的处理逻辑过多地依赖于触发器来处理。触发器的功能通常可以用其他方式实现,确实需要采用触发器,请联系DBA进行确认。
【推荐】避免使用视图
视图可能导致执行计划错乱,影响SQL运行效率。对视图的修改,数据库必须把它转化为对基本表的信息修改,不便于维护。
【推荐】对同一个表的多次alter操作必须合并为一次操作
MySQL对表的修改绝大部分操作都需要锁表并重建表,而锁表则会对线上业务造成影响。为减少这种影响,必须把对表的多次alter操作合并为一次操作。例如,要给表t增加一个字段b,同时给已有的字段aa建立索引,
通常的做法分为两步: alter table t add column b varchar(10);
然后增加索引: alter table t add index idx_aa(aa);
正确的做法是: alter table t add column b varchar(10),add index idx_aa(aa);
【推荐】避免多余的排序。使用GROUP BY 时,默认会进行排序,当你不需要排序时,可以使用order by null。
例如Select a.OwnerUserID,count(星星) cnt from DP_MessageList a group by a.OwnerUserID order by null;
Sharding-jdbc开发规范
【强制】设计、类型、索引和SQL上首先应该遵循MySQL规范所提内容
【推荐】如果数据有时效性,则建议按时间分表或者分库 。如果所有数据热度相同,则建议根据hash或者其他手段分库分表。
【推荐】如果多个独立业务需要共用一台DB的话,不同业务的表必须放在不同的database(schema)里。
主要考虑管理便捷和安全。
【强制】对于分库分表,每一个表的索引结构及名称都必须一致。
【推荐】分库分表数量必须按2的N次方,便于未来实现秒级扩容方案。
【强制】设计阶段,必须使用能路由至多节点(即支持分片)的方式进行数据库设计。
sharding-jdbc支持路由至单数据节点和路由至多数据节点的方式进行路由连接,路由致单数据节点的方式能100%兼容MySQL, 而路由至多数据节点则无法做到100%兼容。但是,在使用sharding-jdbc时,在设计阶段,必须使用路由至多节点的方式进行数据库设计。
正例:自定义的多个真实表,例如:
db00├── t_order_00└── t_order_01 db01├── t_order_02├── t_order_03└── t_order_04
数据节点配置如下:
db00.t_order_00, db00.t_order_01, db01.t_order_02, db01.t_order_03,db01.t_order_04
【推荐】分库分表的数据库或者表的命名规范,建议后缀采用统一的以数字结尾的格式,如位数不足,通过补充0保证位数一致。如:t_user 表分512 个表。
正例:t_user_001, t_user_002 ….t_user_512.
反例:t_user_1, t_user_2 …. t_user_512.
【推荐】在考虑sharding-jdbc进行数据库设计时,请仔细考虑如何分片,并最好使用单一键作为分片键
单一键作为分片键,其对应的分片算法是标准分片算法( StandardShardingAlgorithm)。单一分片算法有个好处是它可以使用SQL的 =、IN、BETWEEN AND、>、<、>=、<=等关键进行快速匹配和运算。
可以考虑通过在分片键之外其他列包含分片信息,通过自定义ComplexShardingAlgorithm进行特殊处理,比如分片键是user_id,可以将Sharding信息作为order_num 的前缀来实现分片逻辑。
【推荐】查询分页充分利用ID的连续性或者区间性进行优化。
在sharding-jdbc中偏移量过大的分页会,查询偏移量过大的分页会导致数据库获取数据性能低下。
由于LIMIT并不能通过索引查询数据,因此如果可以保证ID的连续性,通过ID进行分页是比较好的解决方案
或通过记录上次查询结果的最后一条记录的ID进行下一页的查询:
反例: SELECT * FROM t_order ORDER BY id LIMIT 1000000, 10
正例: SELECT * FROM t_order WHERE id > 100000 AND id <= 100010 ORDER BY id
正例: SELECT * FROM t_order WHERE id > 100000 LIMIT 10
【强制】除了使用直接指定单表的直接路由,不要使用hint. 随便使用hint会使代码变得复杂和难以理解。
原因是这样,把处理的逻辑一半写在自己的业务逻辑里,一半依靠sharding-jdbc的hint, 只会是系统更难理解和复杂,建议分多个SQL在业务逻辑,也可以跨微服务处理。
【强制】访问分片表的SQL必须包含分片键。
【推荐】分片规则一致的主表和子表建议配置为绑定表,这样联表查询不会笛卡尔积现象,关联查询效率将大大提升。
相关推荐
MySQL开发规范是数据库管理和开发中的重要指南,尤其对于去哪网这样的在线旅游服务平台,数据的高效、稳定和安全至关重要。本规范旨在确保开发人员和运维人员在使用MySQL时遵循最佳实践,以提升系统的整体性能、可...
### 阿里巴巴MySQL开发规范详解 #### 一、概述 阿里巴巴MySQL开发规范是一套针对MySQL数据库设计、实现及优化的最佳实践指南。这套规范旨在提高数据库应用的稳定性、性能和可维护性,同时降低潜在的风险。规范主要...
mysql的开发规范文档,这里可能记录的并不是很全也算是给自己一个提醒
### MySQL开发规范详解 #### 一、概述 MySQL作为一种广泛使用的开源关系型数据库管理系统,在企业级应用中扮演着至关重要的角色。为了确保系统的稳定性和可维护性,制定一套科学合理的MySQL开发规范至关重要。本文...
MySQL开发规范总结是针对数据库设计和管理的一套标准,旨在提升效率、标准化开发流程并方便数据库的统一管理。本规范适用于平安科技所有涉及MySQL的开发人员、DBA和运营人员。 1. 引言 - 背景与目的:随着业务的...
根据给定文件的信息,我们可以详细地探讨去哪儿 MySQL 开发规范中的关键知识点,这些知识点主要集中在命名规范、基础规范、库表设计、字段设计等方面。 ### 命名规范 命名规范是任何数据库设计中非常重要的一环,...
MySQL开发规范和优化指南为数据库开发人员和管理员提供了一套详细的标准和建议,旨在规范化和标准化MySQL开发设计,以及指导合理使用MySQL以获得最优性能。本文档适用于MySQL 5.0至MySQL 5.6版本,并详细介绍了权限...
去哪儿MySQL开发规范-完整版,MySQL DBA必须看看
对于后端开发人员来说,遵循良好的MySQL开发规范至关重要,这不仅有助于提高代码的可读性和可维护性,还能提升数据库性能,确保数据安全。以下是关于MySQL开发规范的一些核心要点: 1. **设计规范**: - **数据库...
MySQL开发规范旨在确保数据库设计的一致性、稳定性和高效性,遵循这些规范可以显著减少错误和维护难度。以下是一些关键点的详细说明: 1. **命名规范**: - **大小写规则**:库名、表名和字段名应全部使用小写字母...
MySQL开发规范和原则大全
MySQL开发规范旨在为开发者提供一套清晰、一致的指导原则,以提高代码的可读性、可维护性和数据库系统的性能。以下是对这些规范的详细解读: 1. **目的**: 规范的主要目的是确保MySQL数据库的开发过程标准化,...
MySQL数据库开发规范MySQL数据库开发规范MySQL数据库开发规范MySQL数据库开发规范
MySQL开发规范旨在优化数据库设计,提高查询性能,避免因不当的SQL使用对业务造成严重影响。以下是一些关键的规范要点: 1. **避免大事务**:大事务可能导致长时间锁定资源,影响其他并发操作。应将大事务拆分成小...