结合实际开发及网上资料,整理而成。GitHub:https://github.com/tyronczt/java-learn/blob/master/SQL/MySQL.md
一、基础规范
二、命名规范
三、字段设计规范
四、索引设计规范
五、SQL查询规范
参考文章
附录
没有特殊要求(即Innodb无法满足的功能如:列存储,存储空间数据等)的情况下,所有表必须使用Innodb存储引擎(mysql5.5之前默认使用Myisam,5.6以后默认的为Innodb)Innodb 支持事务,支持行级锁,更好的恢复性,高并发下性能更好
默认使用 utf8mb4 字符集,数据库排序规则使用 utf8mb4_general_ci,采用 utf8 编码的 MySQL 无法保存占位是 4 个字节的 Emoji 表情。为了使后端的项目全面支持客户端输入的 Emoji 表情,升级编码为 utf8mb4 是最佳解决方案;
兼容性更好,统一字符集可以避免由于字符集转换产生的乱码,不同的字符集进行比较前需要进行转换会造成索引失效
使用comment从句添加表和列的备注 从一开始就进行数据字典的维护
500万并不是MySQL数据库的限制,过大会造成修改表结构,备份,恢复都会有很大的问题,可以用历史数据归档(应用于日志数据),分库分表(应用于业务数据)等手段来控制数据量大小
通常文件很大,会短时间内造成数据量快速增长,数据库进行数据库读取时,通常会进行大量的随机IO操作,文件很大时,IO操作很耗时 通常存储于文件服务器,数据库只存储文件地址信息
MySQL限制每个表最多存储4096列,并且每一行数据的大小不能超过65535字节 减少磁盘IO,保证热数据的内存缓存命中率(表越宽,把表装载进内存缓冲池时所占用的内存也就越大,也会消耗更多的IO) 更有效的利用缓存,避免读入无用的冷数据 经常一起使用的列放到一个表中(避免更多的关联操作)
-
号库名、表名、字段名支持最多64个字符,但为了统一规范、易于辨识以及减少传输量,禁止超过32个字符
当库名、表名、字段名等属性含有保留字时,SQL语句必须用反引号引用属性名称,这将使得SQL语句书写、SHELL脚本中变量的转义等变得非常复杂。
形如:tmp_user_account_20190313
形如:bak_user_account_20190313
pk_ 即 primary key;uk_ 即 unique key;idx_ 即 index 的简称
一般作为关联列,如果查询时关联列类型不一致会自动进行数据类型隐式转换,会造成列上的索引失效,导致查询效率降低
正例:user_task / force_project / trade_config
列的字段类型越大,建立索引占据的空间就越大,导致一个页中的索引越少,造成IO次数增加,影响性能
status
、类型type
等字段推荐使用tinytint
或者smallint
类型节省存储空间int
的就不用char
或者varchar
tinyint
的就不用int
tinyint
来代替 enum
和boolean
int
存储而非 char(15)
通过MySQL函数inet_ntoa和inet_aton来进行转化。IPv6地址目前没有转化函数,需要使用DECIMAL或两个BIGINT来存储
SELECT INET_ATON('209.207.224.40'); 3520061480
SELECT INET_NTOA(3520061480); 209.207.224.40
auto_increment
属性),推荐使用bigint
类型blob
,text
等类型blob
,text
是为了存储极大的字符串而设计的数据类型,采用二进制与字符串方式存储,该数据类型不能设置默认值、不便于排序、不便于建立索引, varchar
的性能会比 text
高很多,如果非要使用,建议将这种数据分离到单独的拓展表中
datetime
(8字节)和timestamp
(本身是以int存储,占4字节,范围:1970-01-01 00:00:01到2038-01-19 03:14:07)TIMESTAMP
记录经常变化的更新/创建/发布/日志时间等,并且是近来的时间,够用,可免时区处理DATETIME
记录生日、纪念事件、超出 TIMESTAMP
的时间,记得时区处理DECIMAL
代替 FLOAT
和 DOUBLE
存储精确浮点数Decimal
类型为精准浮点数,float
和 double
在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。
建议在应用层实现外键的逻辑, 外键与级联更新不适合高并发场景,降低插入性能,大并发下容易产生死锁
值类型括号后面的数字只是表示宽度而跟存储范围没有关系
核心表(如用户表,金钱相关的表)必须有行数据的创建时间字段create_time
和最后更新时间字段update_time
,便于查问题
索引其实就是一种数据结构,(哈希表、树等等)不同类型的索引有着不同的数据结构和功能。
MySQL的查询速度依赖良好的索引设计,因此索引对于高性能至关重要。合理的索引会加快查询速度,不合理的索引会降低速度
对于加速查询,使用索引不一定是最好的选择。小表就直接全表扫描,中到大表就建索引,超大表就分区分表。其实主要就要索引带来的好处和维护索引的成本之间的权衡。
太多就起不到过滤作用了,索引也占空间,管理起来也耗资源
不要索引blob/text等字段,不要索引大型字段,这样做会让索引占用太多的存储空间
前缀索引就是对文本的前几个字符建立索引,前缀索引能有效减小索引文件的大小,提高索引的速度。但是前缀索引也有它的坏处:MySQL 不能在 ORDER BY 或 GROUP BY 中使用前缀索引,也不能把它们用作覆盖索引(Covering Index)
不要以为唯一索引影响了 insert 速度,这个速度损耗可以忽略,但提高查找速度是明
显的; 另外,即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生
MEMORY表可以根据需要选择 HASH
或者 BTREE
类型索引
select *
innodb_buffer_pool_size
,降低查询命中率在保证数据不会有误的前提下,能确定结果集数量时,多使用limit,尽快的返回结果。
in 的值不要超过 500 个, in 操作可以更有效的利用索引,or 大多数情况下很少能利用到索引
对列进行函数转换或计算时会导致无法使用索引
不推荐:where date(create_time)='20190101'
推荐:where create_time >= '20190101' and create_time < '20190102'
count(*)是 SQL92 定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关
count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行
以学生和成绩的关系为例,学生表中的 student_id是主键,那么成绩表中的 student_id则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新, 即为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群; 级联更新是强阻塞,存在数据库更新风暴的风险; 外键影响数据库的插入速度
若实在避免不了,需要仔细评估 in 后边的集合元素数量,控制在 1000 个之内
需要 join 的字段,数据类型必须绝对一致;多表关联查询时,保证被关联的字段需要有索引;即使双表 join 也要注意表索引、 SQL 性能
UNION
,推荐使用UNION ALL
UNION
子句个数限制在5个以内。因为union all
不需要去重,节省数据库资源,提高性能
不推荐 SELECT * FROM table ORDER BY TIME DESC LIMIT 10000,10;
原因:会导致大量的io,因为MySQL使用的是提前读取策略
推荐:SELECT * FROM table WHERE TIME < last_TIME ORDER BY TIME DESC LIMIT 10.
SELECT * FROM table inner JOIN (SELECT id FROM table ORDER BY TIME LIMIT 10000,10) as t USING(id)
MySQL分页查询的性能优化 — 详细说明
MySQL命名、设计及使用规范
MySQL数据库开发规范-EC
MySQL 数据类型
MySQL数据库设计规范
MySQL 规范
MySQL 表与索引设计攻略
MySQL开发规范与使用技巧总结
阿里巴巴Java开发手册(详尽版).pdf
深入浅出分析MySQL索引设计背后的数据结构
云数据库 MySQL 使用规范
MySQL 索引 | 菜鸟教程
【MySQL优化】1、定位问题
在线 B+树 生成器
MySQL数据类型
MySQL支持多种类型,大致可以分为三类:数值、**日期/时间 **和 **字符串(字符)**类型。
MySQL支持所有标准SQL数值数据类型。
这些类型包括严格数值数据类型(INTEGER
、SMALLINT
、DECIMAL
和NUMERIC
),以及近似数值数据类型(FLOAT
、REAL
和DOUBLE PRECISION
)。
关键字INT
是INTEGER
的同义词,关键字DEC
是DECIMAL
的同义词。
BIT
数据类型保存位字段值,并且支持MyISAM、MEMORY、InnoDB和BDB表。
作为SQL标准的扩展,MySQL也支持整数类型TINYINT
、MEDIUMINT
和BIGINT
。下面的表显示了需要的每个整数类型的存储和范围。
类型 | 大小 | 范围(有符号) | 范围(无符号) | 用途 |
---|---|---|---|---|
TINYINT | 1 字节 | (-128,127) | (0,255) | 小整数值 |
SMALLINT | 2 字节 | (-32 768,32 767) | (0,65 535) | 大整数值 |
MEDIUMINT | 3 字节 | (-8 388 608,8 388 607) | (0,16 777 215) | 大整数值 |
INT或INTEGER | 4 字节 | (-2 147 483 648,2 147 483 647) | (0,4 294 967 295) | 大整数值 |
BIGINT | 8 字节 | (-9,223,372,036,854,775,808,9 223 372 036 854 775 807) | (0,18 446 744 073 709 551 615) | 极大整数值 |
FLOAT | 4 字节 | (-3.402 823 466 E+38,-1.175 494 351 E-38),0,(1.175 494 351 E-38,3.402 823 466 351 E+38) | 0,(1.175 494 351 E-38,3.402 823 466 E+38) | 单精度 浮点数值 |
DOUBLE | 8 字节 | (-1.797 693 134 862 315 7 E+308,-2.225 073 858 507 201 4 E-308),0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308) | 0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308) | 双精度 浮点数值 |
DECIMAL | DECIMAL(M,D) ,如果M>D,为M+2否则为D+2 | 依赖于M和D的值 | 依赖于M和D的值 | 小数值 |
表示时间值的日期和时间类型为DATETIME
、DATE
、TIMESTAMP
、TIME
和YEAR
。
每个时间类型有一个有效值范围和一个"零"值,当指定不合法的MySQL不能表示的值时使用"零"值。
TIMESTAMP
类型有专有的自动更新特性,将在后面描述。
类型 | 大小 (字节) | 范围 | 格式 | 用途 |
---|---|---|---|---|
DATE | 3 | 1000-01-01/9999-12-31 | YYYY-MM-DD | 日期值 |
TIME | 3 | ‘-838:59:59’/‘838:59:59’ | HH:MM:SS | 时间值或持续时间 |
YEAR | 1 | 1901/2155 | YYYY | 年份值 |
DATETIME | 8 | 1000-01-01 00:00:00/9999-12-31 23:59:59 | YYYY-MM-DD HH:MM:SS | 混合日期和时间值 |
TIMESTAMP | 4 | 1970-01-01 00:00:00/2038结束时间是第 2147483647 秒,北京时间 2038-1-19 11:14:07,格林尼治时间 2038年1月19日 凌晨 03:14:07 | YYYYMMDD HHMMSS | 混合日期和时间值,时间戳 |
字符串类型指CHAR
、VARCHAR
、BINARY
、VARBINARY
、BLOB
、TEXT
、ENUM
和SET
类型 | 大小 | 用途 |
---|---|---|
CHAR | 0-255字节 | 定长字符串 |
VARCHAR | 0-65535 字节 | 变长字符串 |
TINYBLOB | 0-255字节 | 不超过 255 个字符的二进制字符串 |
TINYTEXT | 0-255字节 | 短文本字符串 |
BLOB | 0-65 535字节 | 二进制形式的长文本数据 |
TEXT | 0-65 535字节 | 长文本数据 |
MEDIUMBLOB | 0-16 777 215字节 | 二进制形式的中等长度文本数据 |
MEDIUMTEXT | 0-16 777 215字节 | 中等长度文本数据 |
LONGBLOB | 0-4 294 967 295字节 | 二进制形式的极大文本数据 |
LONGTEXT | 0-4 294 967 295字节 | 极大文本数据 |
varchar
的设计4.0版本以下,varchar(20),指的是20字节,如果存放UTF8汉字时,只能存6个(每个汉字3字节)
5.0版本以上,varchar(20),指的是20字符,无论存放的是数字、字母还是UTF8汉字(每个汉字3字节),都可以存放20个,最大大小是65532字节
1)存储限制
varchar 字段是将实际内容单独存储在聚簇索引之外,内容开头用1到2个字节表示实际长度,小于255为1个字节,大于255则要2个字节
2)编码长度限制
字符类型若为gbk,每个字符最多占2个字节,最大长度不能超过32766
字符类型若为utf8,每个字符最多占3个字节,最大长度不能超过21845
字符类型若为utf8mb4,每个字符最多占4个字节,最大长度不能超过16383
3)表长度限制
mysql的一个表总共字段长度不超过65535
保存"hello"时占用的空间是一样的,但是更小的varchar(10)有着更好的性能
比如:MySQL建立索引时如果没有限制索引的大小,索引长度会默认采用的该字段的长度,也就是varchar(100)建立的索引存储大小要比varchar(10)建立索引存储大小大的多,加载索引使用的内存也更多
位(bit
):计算机存储信息的最小单位,二进制的一个“0”或一个“1”叫一位
字节(byte
):是计算机中数据处理的基本单位,习惯上用大写 B 来表示,1B(byte,字节)= 8bit(位)
字符:是指计算机中使用的字母、数字、字和符号,不同的编码里,一个字符对应几个字节是不同的
ASCIIS码:
1个英文字母(不分大小写)= 1个字节的空间
1个中文汉字 = 2个字节的空间
1个ASCII码 = 一个字节
UTF-8编码:
1个英文字符 = 1个字节
英文标点 = 1个字节
1个中文(含繁体) = 3个字节
中文标点 = 3个字节
utf8mb4 :中文 = 3个字节
emoji表情符号 = 4个字节
根据数据库的功能,可以分以下索引类型:
唯一索引是不允许其中任何两行具有相同索引值的索引,加速查询 + 列值唯一(可以有null)
ALTER TABLE `table_name` ADD UNIQUE uk_xxx (`column` )
加速查询 + 列值唯一(不可以有null)+ 表中只有一个
在数据库中为表定义主键将自动创建主键索引,主键索引是唯一索引的特定类型,该索引要求主键中的每个值都唯一
ALTER TABLE `table_name` ADD PRIMARY KEY (`column`)
查询列要被所建的索引覆盖;在聚集索引中,表中行的物理顺序与键值的逻辑(索引)顺序相同,一个表只能包含一个聚集索引,主键就是一个聚集索引
多列值组成一个索引,专门用于组合搜索,其效率大于索引合并
ALTER TABLE `table_name` ADD INDEX idx_xxx (`column1`, `column2`, `column3`)
ALTER TABLE `table_name` ADD INDEX idx_xxx (`column`)
ALTER TABLE `table_name` ADD FULLTEXT (`column`)
InnoDB存储引擎中,secondary index(非主键索引)中没有直接存储行地址,存储主键值。如果用户需要查询secondary index中所不包含的数据列时,需要先通过secondary index查找到主键值,然后再通过主键查询到其他数据列,因此需要查询两次。覆盖索引的概念就是查询可以通过在一个索引中完成,覆盖索引效率会比较高,主键查询是天然的覆盖索引。合理的创建索引以及合理的使用查询语句,当使用到覆盖索引时可以获得性能提升。比如SELECT email,uid FROM user_email WHERE uid=xx,如果uid不是主键,适当时候可以将索引添加为index(uid,email),以获得性能提升
SELECT
DISTINCT <select_list>
FROM <left_table>
<join_type> JOIN <right_table>
ON <join_condition>
WHERE <where_condition>
GROUP BY <group_by_list>
HAVING <having_condition>
ORDER BY <order_by_condition>
LIMIT <limit_number>
CREATE TABLE `user` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`user_id` bigint(11) NOT NULL COMMENT '用户id',
`username` varchar(45) NOT NULL COMMENT '真实姓名',
`email` varchar(30) NOT NULL COMMENT '用户邮箱',
`nickname` varchar(45) NOT NULL COMMENT '昵称',
`avatar` int(11) NOT NULL COMMENT '头像',
`birthday` date NOT NULL COMMENT '生日',
`sex` tinyint(4) DEFAULT '0' COMMENT '性别',
`short_introduce` varchar(150) DEFAULT NULL COMMENT '一句话介绍自己,最多50个汉字',
`user_resume` varchar(300) NOT NULL COMMENT '用户提交的简历存放地址',
`user_register_ip` int(11) NOT NULL COMMENT '用户注册时的源ip',
`create_time` timestamp NOT NULL COMMENT '用户记录创建的时间',
`update_time` timestamp NOT NULL COMMENT '用户资料修改的时间',
`user_review_status` tinyint(4) NOT NULL COMMENT '用户资料审核状态,1为通过,2为审核中,3为未通过,4为还未提交审核',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user_id` (`user_id`),
KEY `idx_username` (`username`),
KEY `idx_create_time` (`create_time`,`user_review_status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='网站用户基本信息'