MySQL进阶篇(03):合理的使用索引结构和查询
本文源码:GitHub·点这里 || GitEE·点这里
一、高性能索引
1、查询性能问题
在MySQL使用的过程中,所谓的性能问题,在大部分的场景下都是指查询的性能,导致查询缓慢的根本原因是数据量的不断变大,解决查询性能的最常见手段是:针对查询的业务场景,设计合理的索引结构。
2、索引使用原则
索引的使用并不是越多越好,而是针对业务下的查询场景,不断的改进和优化,例如电商系统中用户订单的场景,假设存在如下表结构:
CREATE TABLE `ds_user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`user_name` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表';
CREATE TABLE `ds_order` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`user_id` int(11) NOT NULL COMMENT '用户ID',
`order_no` varchar(60) NOT NULL COMMENT '订单号',
`product_name` varchar(50) DEFAULT NULL COMMENT '产品名称',
`number` int(11) DEFAULT '1' COMMENT '个数',
`unit_price` decimal(10,2) DEFAULT '0.00' COMMENT '单价',
`total_price` decimal(10,2) DEFAULT '0.00' COMMENT '总价',
`order_state` int(2) DEFAULT '1' COMMENT '1待支付,2已支付,3已发货,4已签收',
`order_remark` varchar(50) DEFAULT NULL COMMENT '订单备注',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单表';
用户和订单管理表,在电商的业务中很常见,可以通过对该业务分析,看看常用的索引结构:
用户方:
- 基于用户的查询,多数是基于用户ID(user_id);
- 基于订单号(order_no),查看物流的信息;
运营方:
- 基于时间段的流水明细(create_time)或排序;
- 基于订单状态的筛选(order_state)和统计;
- 基于产品(product_name)的数据统计分析;
这样一个流程分析走下来,即可以在开发初期,确定哪些结构是查询必须用到的,预先做好索引结构,避免数据量庞大到影响性能时再去考虑使用索引。
有些时候会考虑放弃一些查询条件,例如基于产品名称的数据统计,走定时任务的方式,用来缓解表的查询压力,处理的方式是多样的。
优秀的索引设计,都是建立在对业务数据的理解上,考虑业务数据的查询方式,提高查询效率。
二、索引创建
1、单列索引
单列索引,即索引建立在表的一个字段上,一个表可以有多个单列索引,使用起来相对比较简单:
CREATE INDEX user_id_index ON ds_order(user_id) USING BTREE;
主键索引,或者上述的user_id_index都是单列索引。
业务场景:基于用户自己对订单查询,和管理系统,订单和用户的关联查询,所以订单表的user_id需要一个索引。
2、组合索引
组合索引包含两个或两个以上的列,组合索引相比单列索引复杂很多,如何建立组合索引,和业务关联度非常高,在使用组合索引时,还需要考虑查询条件的顺序。
CREATE INDEX state_create_time_index ON `ds_order`(`create_time`,`order_state`);
如上就是组合索引,实际包含的是2个索引 (create_time) (create_time,order_state),这样查询就涉及到最左前缀的原则,必须按照顺序来查询,这里下面详说。
业务场景:首先单说这里组合索引,在业务开发中,常见订单状态的统计,基于统计结果做运营分析,另外就是在运营系统中,基于创建时间段的筛选条件是默认存在的,避免全部数据实时扫描;一些其他的常见查询也都是条件加时间段的查询模式。
3、前缀索引
如果需要加索引的列是很长的字符串,那么索引会变的庞大臃肿,起到的效果可能并不是很明显。这时候可以截取列的前面一部分,创建索引,节省空间,这样可能会出现索引的选择性下降,即基于前缀索引查询出的相似数据可能很多:
ALTER TABLE ds_order ADD KEY (order_no(30)) ;
这里由于订单号太长,所以选择前面30位作为前缀索引,用作订单号的查询,当然这里涉及到一个非常经典的业务场景,订单号机制。
业务场景:前缀索引一个典型的应用场景就是处理订单号,一个看似很长的订单号,其实包含的信息非常多:

- 时间点:就是订单生成的时间,年月日时分秒;
- 标识位:即一个唯一的UID,保证订全单号唯一;
- 埋点一:在很多业务中,在订单号记录产品类目;
- 埋点二:通常会标识产品属性,例如颜色,口味等;
- 错位符:防止订单号被分析,会随机一段错位符号;
如此一段分析下来,实际订单号是非常长的,所以需要引入前缀索引机制,前缀索引期望使用的索引长度可以筛选整个列的基数,例如上面的订单号:
- 大部分业务基于时间节点筛选足够,即索引长度14位;
- 如果是并发业务,很多时间节点相同,则索引长度是时间点+标识位;
注意:如果业务允许的情况下,一般要求前缀索引的长度有唯一性,例如上面的时间和标示位。
4、其他索引
例如全文索引等,这些用到的场景不多,如果数据庞大,又需要检索等,通常会选择强大的搜索中间件来处理。显式唯一索引,这种也会在程序上做规避,避免不友好的异常被抛出。
三、索引查询
如何创建最优的索引,是一件不容易的事情,同样在查询的时候,是否使用索引也是一件难度极大的事情,经验之谈:多数是性能问题暴露的时候,才会回头审视查询的SQL语句,针对性能问题,做相应的查询优化。
1、单列查询
这里直接查询主键索引,MySQL的主键一般选择自增,所以速度非常快。
EXPLAIN SELECT * FROM ds_order WHERE id=2;
EXPLAIN SELECT * FROM ds_order WHERE id=1+1;
EXPLAIN SELECT * FROM ds_order WHERE id+1=1;
这里,id=2,id=1+1,MySQL都可以自动解析,但是id+1是在索引列上执行运算,直接导致主键索引失效。这里有一个基本策略,如果非要在单列索引上做操作,可以将该逻辑放在程序中,到MySQL层面,SQL语句越干净利落越好。
2、前缀索引查询
前缀索引的查询,可以基于Like对特定长度筛选,或者全订单号查询。
EXPLAIN SELECT * FROM ds_order WHERE order_no LIKE '202008011314158723628732871625%';
EXPLAIN SELECT * FROM ds_order WHERE order_no='20200801131415872362873287162572367';
3、组合索引查询
查询最麻烦的就是组合索引,或者说查询条件组合起来,都使用了索引:
EXPLAIN SELECT * FROM ds_order
WHERE create_time>'2020-08-01 00:00:00' AND order_state='1';
上述基于组合索引中列的顺序,使用了组合索引:state_create_time_index。
EXPLAIN SELECT * FROM ds_order WHERE create_time>'2020-08-01 00:00:00';
上述只使用create_time列,也同样使用了索引结构。
EXPLAIN SELECT * FROM ds_order WHERE order_state='1';
上述如果只使用order_state条件,则结果显示全表扫描。
EXPLAIN SELECT * FROM ds_order
WHERE create_time>'2020-08-01 00:00:00' AND order_no LIKE '20200801%';
上述则基于组合索引的create_time列和单列索引order_no保证查询条件都使用了索引。
通过上面几个查询案例,索引组合索引使用的注意事项如下:
- 组合索引必须按索引最左列开始查询;
- 不能跳过组合字段查询,这样无法使用索引;
四、索引其他说明
1、索引的优点
- 基于注解或唯一索引保证数据库表中数据的唯一性;
- 索引通过减少扫描表的行数提高查询的效率;
2、索引的缺点
- 创建索引和维护索引,会耗费空间和实际;
- 查询以外的操作增删改等,都需要动态维护索引;
3、索引使用总结
索引机制在MySQL中真的非常复杂,非专业的DBA(就是指开发人员),基本要熟练常见的索引结构,待过两年所谓的大厂,每个版本开发涉及的核心表SQL都是有专业DBA验收,复杂的查询都是提交需求,DBA直接输出查询SQL,当然在一般公司是没有DBA,需要开发在开发的过程中不断的思考,逐步优化,这需要对业务数据有一定的敏感度,对核心接口有执行监控,当发现稍微出现耗时情况,就可以不断优化,这个积累是个枯燥和进步的过程。
五、源代码地址
GitHub·地址
https://github.com/cicadasmile/mysql-data-base
GitEE·地址
https://gitee.com/cicadasmile/mysql-data-base

推荐阅读:MySQL数据库系列
MySQL进阶篇(03):合理的使用索引结构和查询的更多相关文章
- 【目录】mysql 进阶篇系列
随笔分类 - mysql 进阶篇系列 mysql 开发进阶篇系列 55 权限与安全(安全事项 ) 摘要: 一. 操作系统层面安全 对于数据库来说,安全很重要,本章将从操作系统和数据库两个层面对mysq ...
- (6)MySQL进阶篇SQL优化(MyISAM表锁)
1.MySQL锁概述 锁是计算机协调多个进程或线程并发访问某一资源的机制.在数据库中,除传统的计算资源 (如 CPU.RAM.I/O 等)的抢占以外,数据也是一种供许多用户共享的资源.如何保证数 据并 ...
- MySQL存储引擎MyISAM和InnoDB,索引结构优缺点
MySQL存储引擎MyISAM和InnoDB底层索引结构 深入理解MySQL索引底层数据结构与算法 (各种索引结构优缺点) Myisam和Innodb索引实现的不同(存储结构) 存储引擎作用于什么对象 ...
- MySQL进阶篇(02):索引体系划分,B-Tree结构说明
本文源码:GitHub·点这里 || GitEE·点这里 一.索引简介 1.基本概念 首先要明确索引是什么:索引是一种数据结构,数据结构是计算机存储.组织数据的方式,是指相互之间存在一种或多种特定关系 ...
- (3)MySQL进阶篇SQL优化(索引)
1.索引问题 索引是数据库优化中最常用也是最重要的手段之一,通过索引通常可以帮助用户解决大多数 的SQL性能问题.本章节将对MySQL中的索引的分类.存储.使用方法做详细的介绍. 2.索引的存储分类 ...
- MySQL进阶篇(01):基于多个维度,分析服务器性能
本文源码:GitHub·点这里 || GitEE·点这里 一.服务器性能简介 1.性能定义 服务器性能优化是一项非常艰巨的任务,当然也是很难处理的问题,在写这篇文章的时候,特意请教下运维大佬,硬件工程 ...
- SQL Server调优系列进阶篇(如何维护数据库索引)
前言 上一篇我们研究了如何利用索引在数据库里面调优,简要的介绍了索引的原理,更重要的分析了如何选择索引以及索引的利弊项,有兴趣的可以点击查看. 本篇延续上一篇的内容,继续分析索引这块,侧重索引项的日常 ...
- (4)MySQL进阶篇SQL优化(常用SQL的优化)
1.概述 前面我们介绍了MySQL中怎么样通过索引来优化查询.日常开发中,除了使用查询外,我们还会使用一些其他的常用SQL,比如 INSERT.GROUP BY等.对于这些SQL语句,我们该怎么样进行 ...
- (7)MySQL进阶篇SQL优化(InnoDB锁-事务隔离级别 )
1.概述 在我们在学习InnoDB锁知识点之前,我觉得有必要让大家了解它的背景知识,因为这样才能让我们更系统地学习好它.InnoDB与MyISAM的最大不同有两点:一是支持事务(TRANSACTION ...
随机推荐
- vue中v-model父子组件通信
有这样的需求,父组件绑定v-model,子组件输入更改父组件v-model绑定的数值.是怎么实现的呢? 实际上v-model 只是语法糖而已. <input v-model="inpu ...
- sass-loader安装+Failed to resolve loader: sass-loader You may need to install it.解决方法
方式一: 通过 cnpm 安装node-sass cnpm install node-sass --save 方式二: 通过npm 安装 1.安装sass-loader npm install sas ...
- Mysql基础(七):数据库总结
目录 MySQL数据库06 /数据库总结 1. 数据库/DBMS 2. 数据库分类 3. 修改密码 4. 库操作 5. 表操作 6. 存储引擎 7. 事务 8. 约束 9. 数据类型 10. 单表语句 ...
- 爬虫04 /asyncio、selenium规避检测、动作链、无头浏览器
爬虫04 /asyncio.selenium规避检测.动作链.无头浏览器 目录 爬虫04 /asyncio.selenium规避检测.动作链.无头浏览器 1. 协程asyncio 2. aiohttp ...
- 一个简单的Maven小案例
Maven是一个很好的软件项目管理工具,有了Maven我们不用再费劲的去官网上下载Jar包. Maven的官网地址:http://maven.apache.org/download.cgi 要建立一个 ...
- Database Identifiers - SID
These options include your global database name and system identifier (SID). The SID is a unique ide ...
- 013.Nginx动静分离
一 动静分离概述 1.1 动静分离介绍 为了提高网站的响应速度,减轻程序服务器(Tomcat,Jboss等)的负载,对于静态资源,如图片.js.css等文件,可以在反向代理服务器中进行缓存,这样浏览器 ...
- python学完可以做什么?Python就业方向最全面的解析
乔布斯说过:“每一个人都应该学习如何编程,因为编程会教会你如何思考.”下一个时代是人机交互的时代,学习编程不是要让你成为程序员,而让你理解这个时代. 点击免费领取:全网最全python学习导图+14张 ...
- C++语法小记---如何判断一个变量是不是指针
如何判断一个变量是不是指针? 思路:模板函数 + 可变参数 + sizeof(函数) #include <iostream> #include <string> using n ...
- 题解 洛谷 P3298 【[SDOI2013]泉】
考虑到年份数很小,只有 \(6\),所以可以 \(2^6\) 来枚举子集,确定流量指数对应相同的位置,然后通过哈希和排序来计算相同的方案数. 但是这样计算出的是大于等于子集元素个数的方案数,所以还需要 ...