MySQL性能优化之索引设计
作者:IT王小二
上一篇给小伙伴们讲了关于SQL查询性能优化的相关技巧,一个好的查询SQL离不开合理的索引设计。这篇小二就来唠一唠怎么合理的设计一个索引来优化我们的查询速度,要是有不合理的地方...嗯..
当然啦,开个玩笑,欢迎小伙伴们指正!
一、索引设计基石
索引设计基石是什么呢?
- 小二认为索引设计的基石就是数据表字段类型的合理设计,即选择合适字段类型和设置合适的长度。
- 选择正确的数据类型,那么在字段上建立索引时,一个数据页可以存储更多的索引,一次读取加载到内存的索引个数更多,同时降低B+tree的高度,减少磁盘IO,对提升MySQL的性能提升有着极大的意义。
通常情况下,字段类型的选择是需要根据业务来判断的,通常需要遵循以下几点。
- 确定合适的大类型:数字、字符串、日期和时间、二进制等。
- 确定具体的类型:有无符号、取值范围、变长定长等。
- 尽量选择更小的数据类型,因为它们通常有更好的性能,占用更少的硬件资源。
- 尽量把字段定义为
NOT NULL
,避免使用NULL
。
下列各种类型表格内容来自菜鸟教程,权当备忘。
1. 数值类型
类型 | 大小(bytes) | 范围(有符号) | 范围(无符号) | 用途 |
---|---|---|---|---|
TINYINT | 1 | (-128, 127) | (0, 255) | 小整数值 |
SMALLINT | 2 | (-32768, 32767) | (0, 65535) | 大整数值 |
MEDIUMINT | 3 | (-8388608, 8388 607) | (0, 16777215) | 大整数值 |
INT或INTEGER | 4 | (-2147483648, 2147483647) | (0, 4294967295) | 大整数值 |
BIGINT | 8 | (-9233372036854775808, 9223372036854775807) | (0, 18446744073 709551615) | 极大整数值 |
FLOAT | 4 | (-3.402823466E+38, 1.175494351E-38),0,(1.175494351E-38,3.402823466351E+38) | 0, (1.175494351E-38, 3.402823466E+38) | 单精度浮点数值 |
DOUBLE | 8 | (1.7976931348623157E+308, 2.2250738585072014E-308), 0, (2.2250738585072014E-308, 1.7976931348623157E+308) | 0, (2.2250738585072014E-308, 1.7976931348623157E+308) | 双精度浮点数值 |
DECIMAL | 对DECIMAL(M,D) ,如果M>D,为M+2否则为D+2 | 依赖于M和D的值 | 依赖于M和D的值 | 小数值 |
优化建议:
- 如果整型数据没有负数,如id号,建议指定为UNSIGNED无符号类型,容量可以扩大一倍。
- 整数通常是最佳的数据类型,因为它速度快,并且能使用AUTO_INCREMENT。
- 建议使用TINYINT代替ENUM、BITENUM、SET。
- 建议使用整型类型来运算和存储实数,一种方法是实数乘以相应的倍数后再操作;另外一种方法是使用两个字段来分别存储整数位和小数位。
- DECIMAL最适合保存准确度要求高并且用于计算的数据,比如价格、金额等,但是在使用DECIMAL类型的时候注意长度设置。
- 避免使用整数的显示宽度,也就是说不要用INT(5)类似的方法指定字段显示宽度,直接用INT。
注意: INT(2)设置的为显示宽度,而不是整数的长度,需要配合ZEROFILL
使用 。
CREATE TABLE user(
id TINYINT(2) UNSIGNED
);
例如id
设置为 TINYINT(2) UNSIGNED
,表示无符号,可以存储的最大数值为255,其中TINYINT(2)
没有配合ZEROFILL
实际没有任何意义,例如插入数字200,长度虽然超过了两位,但是这个时候是可以插入成功的,查询结果同样为200;插入数字5时,同样查询结果为5。
CREATE TABLE user(
id TINYINT(2) UNSIGNED ZEROFILL
);
而TINYINT(2)
配合ZEROFILL
后,当插入数字5时,实际存储的还是5,不过在查询是MySQL会在前面补上一个0,即查询出来的实际为05
。
2. 字符串类型
类型 | 大小(bytes) | 用途 |
---|---|---|
CHAR | 0-255 | 定长字符串,char(n)当插入的字符数不足n时(n代表字符数),插入空格进行补充保存。在进行检索时,尾部的空格会被去掉。 |
VARCHAR | 0-65535 | 变长字符串,varchar(n)中的n代表最大字符数,插入的字符数不足n时不会补充空格 |
TINYBLOB | 0-255 | 不超过 255 个字符的二进制字符串 |
TINYTEXT | 0-255 | 短文本字符串 |
BLOB | 0-65535 | 二进制形式的长文本数据 |
TEXT | 0-65535 | 长文本数据 |
MEDIUMBLOB | 0-16777215 | 二进制形式的中等长度文本数据 |
MEDIUMTEXT | 0-16777215 | 中等长度文本数据 |
LONGBLOB | 0-4294967295 | 二进制形式的极大文本数据 |
LONGTEXT | 0-4294967295 | 极大文本数据 |
优化建议:
- 当字符串短,并且所有值都固定一个长度或者接近一个长度时使用CHAR,当然要是如果没有完全可以使用整型来存储;字符串长度相差较大时使用VARCHAR。
- CHAR和VARCHAR适合长度不超过255个字符唱的的任意字母和数字组合,例如人名、电话号码、编码等。用来计算的数字不要用VARCHAR类型保存,因为可能会导致一些与计算相关的问题,同时可能影响到计算的准确性和完整性。
- VARCHAR(255)在建立索引时会占用比较多的存储空间,在不要求保证数据完全精确的境况下可以使用前缀索引。例如
idx_name_age_position(name(20), age, position)
,取前20个字符作为索引,但是这种情况下因为是不完全字段,所以order by name asc
或者group by name
排序过程无法使用索引排序。当然需要保证数据的精确性和查找速度,最优的方案就是使用全文搜索引擎ES了。 - 尽量不用BLOB和TEXT,如果实在要用可以考虑将BLOB和TEXT字段单独存一张表,使用主键id来关联。
- BLOB和TEXT都不能有默认值。BLOB系列存储二进制字符串,与字符集无关;TEXT系列存储非二进制字符串,与字符集相关。
3. 时间类型
类型 | 大小(bytes) | 范围 | 格式 | 用途 |
---|---|---|---|---|
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-01-19 03:14:07 (格林尼时间) | YYYYMMDD HHMMSS | 混合日期和时间值,时间戳 |
优化建议:
- MySQL能存储的最小时间粒度为秒。当然要是需要精确到毫秒级的话,当然也是有办法的,新加一列在另外一列保留毫秒值即可。
- 建议使用MySQL的内建类型DATE、TIME、DATETIME来存储时间,而不是使用字符串或者保存时间戳,这样的话可以通过MySQL的日期函数处理相关逻辑。
- 当不需要具体时间时,建议用DATE数据类型来保存日期,MySQL中默认的日期格式是
YYYY-MM-DD
。 - 当数据格式为TIMESTAMP和DATETIME时,可以用
CURRENT_TIMESTAMP
作为(MySQL5.6以后),MySQL会自动返回记录插入的当前确切时间。不过需要注意的是校准MySQL运行环境的时间和时区,比如Linux时间或者docker容器的时间和时区。 - TIMESTAMP是UTC时间戳,与时区相关;DATETIME的存储格式是一个YYYYMMDD HH:MM:SS的整数,与时区无关,存的什么读出来就是什么。
- 一般的短期项目或者小公司项目小二建议使用TIMESTAMP,因为这种项目生命往往活不到2038年,DATETIME还更节约空间。但是如果是腾讯、阿里、京东一般会用DATETIME,因为不用考虑TIMESTAMP将来的时间上限问题。
二、索引设计原则
1. 索引未建,代码先行
通常来说,考虑好表中每个字段应该使用什么类型和长度,建完表需要做的事情不是马上建立索引,而是先把相关主体业务开发完毕,然后把涉及该表的SQL都拿出来分析之后再建立索引。
2. 联合索引尽量覆盖条件
尽量少建立单值索引(唯一索引除外),应当设计一个或者两三个联合索引,让每一个联合索引都尽量去包含SQL语句中的where、order by、group by
的字段,同时确保联合索引的字段顺序尽量满足SQL查询的最左前缀原则。
3. 不要在小基数字段上建立索引
索引基数是指这个字段在表里总共有多少个不同的值,比如一张表总共100万行记录,其中有个性别字段,性别一共有三个值:男、女、保密,那么该字段的基数就是3。
如果对这种小基数字段建立索引的话,因为索引树中只有男、女、保密三个值,根本没法进行快速的二分查找,同时还需要回表查询,还不如全表扫描嘞。
一般建立索引,尽量使用那些基数比较大的字段,那么才能发挥出B+树快速二分查找的优势来。
4. where与order by冲突时优先where
在where
和order by
出现索引设计冲突时,是优先针对where去设计索引?还是优先针对order by设计索引?
通常情况下都是优先针对where
来设计索引,因为通常情况下都是先where
条件使用索引快速筛选出来符合条件的数据,然后对进行筛选出来的数据进行排序和分组,而where
条件快速筛选出来的的数据往往不会很多。
5. 对慢查询SQL进行优化
对生产实际运行过程中,或者测试环境大数据量测试过程中发现的慢查询SQL进行特定的索引优化、代码优化等策略。
三、索引设计实战
终于轮到实战了,小二最喜欢实战了。
写到这里不得不吐槽一下,这个金三银四的跳槽季节,年前提离职了,结果离职还没办完就封村整整两个礼拜了,呜呜呜...
上节小二就提到会有个很有意思的小案例,那么在疫情当下,门都出不去的日子,感觉这个例子更有意思了,咱们来讨论一下各种社交平台怎么做的用户信息搜索呢。
社交平台有一个小伙伴们都喜欢的功能,搜索好友信息,比如小二熟练的点开省份...城市..性别..年龄..身高...
咳咳咳...小二怎么可能干这种事情,小二的心里只有代码,嗯...没错,就是这样。
这个就可以说是对于用户信息的查询筛选了,通常这种表都是非常大数据量的,在不考虑分库分表的情况下,怎么通过索引配合SQL来优化呢?
通常我们在编写SQL是会写出类似如下的SQL来执行,有where、order by、limit
等条件来查询。
select xx from user where xx=xx and xx=xx order by xx asc limit xx,xx;
那么接下来小二一个一个慢慢增加字段来分析分析,怎么根据业务场景来设计索引。
例如通常小伙伴们都会优先筛选出自己所属城市和性别的人,那么该怎么设计索引呢?
where province = xx and city = xx and sex = xx
针对这种情况,很简单,设计一个联合索引(provice, city, sex)
就完事了。
那么这个时候小伙伴肯定又要瞅瞅年龄段了,嘿嘿
where province = xx and city = xx and sex = xx and age >= 18 and age <= 28
那么这时候有小伙伴就会说了,很简单啊,范围字段放最后咱还是知道的,联合索引改成(provice, city, sex, age)
不就可以了。
嗯,是的,这么干没毛病,但是小伙伴们有没有想过有些人万一既喜欢帅哥又喜欢美女,别想歪了哈...,挺多小姐姐就既喜欢帅哥又喜欢美女的。
那么这个时候小姐姐就不搜索性别了,那么这个时候联合索引只能用到前两个字段了,那么不符合咱们的专业标准啊,咋办呢?这时候还是有办法的,咱们只需要动动小脑袋改改SQL就行了,在没有选择性别时判断一下,改成下面这样就可以了。
province=xx and city=xx and sex in ('male','female') and age >= 18 and age <= 28
那么有爱好之类的其他等值字段。
province=xx and city=xx and sex in ('male','female') and hobby = 1 and xx = xx and age >= 18 and age <= 28
咋办嘞,同样往联合索引里面塞,例如(provice, city, sex, hobby, xx, age)
。
那么如果还有范围查询,比如身高、体重范围和最后登录时间等等。
针对这种多个范围查询的话,为了比较好的利用索引,在业务允许的情况下可以使用固定范围,然后数据库字段存储范围标识就可以了,这样就转化为了等值匹配,就可以很好地利用索引了。
例如最后登录时间字段不记录最后登录时间,而是记录设置字段 is_login_within_seven_days
在7天内有登录则为1,否则为0,最后索引设计成(provice, city, sex, hobby, xx, is_login_within_seven_days, age)
。
那么根据场景最后设计出来的这个索引可能已经可以覆盖大部分的查询流量了,那么如果还有其他一部分热度比较高的查询怎么办呢,办法也很简单啊,再加一两个索引即可。
例如通常会查询这个城市比较受欢迎(评分:score)的小姐姐,这时候添加一个联合索引(provice, city, sex, score)
那么就可以了。
可以看出,索引时必须结合场景来设计的,思路就是尽量用不超过3个复杂的联合索引来抗住大部分的80%以上的常用查询流量,然后再用一两个二级索引来抗下一些非常用查询流量。
以上就是小二要给大家分享的索引设计,如果能动动你发财的小手给小二点个免费的赞就更好啦~
下篇小二就来讲讲MySQL事务和锁机制。
MySQL性能优化之索引设计的更多相关文章
- MySQL性能优化:索引
MySQL性能优化:索引 索引提供指向存储在表的指定列中的数据值的指针,然后根据您指定的排序顺序对这些指针排序.数据库使用索引以找到特定值,然后顺指针找到包含该值的行.这样可以使对应于表的SQL语句执 ...
- mysql性能优化之索引优化
作为免费又高效的数据库,mysql基本是首选.良好的安全连接,自带查询解析.sql语句优化,使用读写锁(细化到行).事物隔离和多版本并发控制提高并发,完备的事务日志记录,强大的存储引擎提供高效查询(表 ...
- MySQL 数据库性能优化之索引优化
接着上一篇 MySQL 数据库性能优化之表结构,这是 MySQL数据库性能优化专题 系列的第三篇文章:MySQL 数据库性能优化之索引优化 大家都知道索引对于数据访问的性能有非常关键的作用,都知道索引 ...
- MySQL性能优化总结___本文乃《MySQL性能调优与架构设计》读书笔记!
一.MySQL的主要适用场景 1.Web网站系统 2.日志记录系统 3.数据仓库系统 4.嵌入式系统 二.MySQL架构图: 三.MySQL存储引擎概述 1)MyISAM存储引擎 MyISAM存储引擎 ...
- MySQL性能优化(二):优化数据库的设计
原文:MySQL性能优化(二):优化数据库的设计 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.n ...
- MySQL性能优化 - 别再只会说加索引了
MySQL性能优化 MySQL性能优化我们可以从以下四个维度考虑:硬件升级.系统配置.表结构设计.SQL语句和索引. 从成本上来说:硬件升级>系统配置>表结构设计>SQL语句及索引, ...
- [MySQL性能优化系列]巧用索引
1. 普通青年的索引使用方式 假设我们有一个用户表 tb_user,内容如下: name age sex jack 22 男 rose 21 女 tom 20 男 ... ... ... 执行SQL语 ...
- mysql性能优化-慢查询分析、优化索引和配置 (慢查询日志,explain,profile)
mysql性能优化-慢查询分析.优化索引和配置 (慢查询日志,explain,profile) 一.优化概述 二.查询与索引优化分析 1性能瓶颈定位 Show命令 慢查询日志 explain分析查询 ...
- MySQL性能优化(三):索引
原文:MySQL性能优化(三):索引 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/vbi ...
随机推荐
- java解洛谷P1003铺地毯问题
此题给出的最大地毯数量为10000,创建[10001][4]长度的二维数组 以稀松数组的方法,[第i个地毯]的 [0][1][2][3]分别保存地毯的坐标和大小 再用需要求的坐标比较即可 public ...
- ICLR2021对比学习(Contrastive Learning)NLP领域论文进展梳理
本文首发于微信公众号「对白的算法屋」,来一起学AI叭 大家好,卷王们and懂王们好,我是对白. 本次我挑选了ICLR2021中NLP领域下的六篇文章进行解读,包含了文本生成.自然语言理解.预训练语言模 ...
- Vue+webpack配置实现多页面应用开发
为什么要配置多页面开发? · 由于单页面应用不利于SEO,对于某些资讯类网站不够友好,而多页面则能够更优的解决此问题. · 传统的多页面开发模式(如java的jsp等) 前后端耦合性大,开发效率低,代 ...
- 「NOI十联测」深邃
「NOI十联测」深邃 要使得最大的连通块最小,显然先二分答案. 先固定1结点为根. 对于一个果实,显然是先处理子树中未分配的点,再向外延伸. 每个结点记录一个\(si[]\),表示子树中未分配的点数, ...
- 一次线上服务高 CPU 占用优化实践 (转)
线上有一个非常繁忙的服务的 JVM 进程 CPU 经常跑到 100% 以上,下面写了一下排查的过程.通过阅读这篇文章你会了解到下面这些知识. Java 程序 CPU 占用高的排查思路 可能造成线上服务 ...
- DbUnit入门实战
原文地址: http://yangzb.iteye.com/blog/947292 相信做过单元测试的人都会对JUnit 非常的熟悉了, 今天要介绍的DbUnit(http://dbunit.sour ...
- Spark RDD学习
RDD(弹性分布式数据集)是Spark的核心抽象.它是一组元素,在集群的节点之间进行分区,以便我们可以对其执行各种并行操作. 创建RDD的两种方式: 并行化驱动程序中的现有数据: 引用外部存储系统中的 ...
- python基础语法_3面向对象
http://www.runoob.com/python3/python3-class.html https://www.imooc.com/learn/317 慕课网:987809563@qq.co ...
- Redis 源码简洁剖析 11 - 主 IO 线程及 Redis 6.0 多 IO 线程
Redis 到底是不是单线程的程序? 多 IO 线程的初始化 IO 线程运行函数 IOThreadMain 如何推迟客户端「读」操作? 如何推迟客户端「写」操作? 如何把待「读」客户端分配给 IO 线 ...
- 《手把手教你》系列技巧篇(六十六)-java+ selenium自动化测试 - 读写excel文件 - 上篇(详细教程)
1.简介 在自动化测试,有些我们的测试数据是放到excel文件中,尤其是在做数据驱动测试的时候,所以需要懂得如何操作获取excel内的内容.由于java不像python那样有直接操作Excle文件的类 ...