高性能MySql进化论(七):正确的使用索引

本文详细介绍了数据库索引的使用原则与优化策略,包括保持单纯的列、正确使用前缀索引、组合索引中字段顺序问题、聚合索引与覆盖索引的概念及其应用、索引顺序扫描排序、轻便索引的注意事项等,旨在提高查询效率并减少性能影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数据库引擎利用索引提高查询效率,同时也针对索引增加了很多的优化策略,但是在使用索引的过程中也有很多的细节问题需要注意,如果忽略了这些问题,辛辛苦苦建立的索引可能得不到很好的应用,而且有可能还会对性能有一定的影响,下面列出了在使用索引的过程中需要遵守的原则

1        保持单纯的列

所谓的“单纯的列”指的就是在作为查询条件时,不要使用运算符,函数对字段进行处理,否则相关的索引将不能使用

下面列出两种最常见的错误情况
(1) select wordfrom dictionary where id+1=999;

      不应该使用id+1的方式

(2) selectword from dictionary where to_days(CURRENT_DATE)-to_days(id)<=10;

      不应该使用TO_DAYS(id)的方式

 

2        正确的采用“前缀索引”的前缀长度

在有些时候,需要使用长字符串作为索引,这样的索引由于占用的空间比较大,以及排序的时候值之间的比较会花费很多的时间,效率会比较低。对于这种情况可以只使用这个字段的前N个字符作为索引的值,这种策略就叫做”前缀索引”。比如,在MYSQL中如果需要使用BLOB/TEXT类型的字段作为索引的话,那么必须使用前缀索引,因为这几种类型不允许作为索引。

 

使用“前缀索引”会带来选择性的问题,比如某张表的长字段索引记录如下,如果采用整个字段的值作为索引的值的话虽然效率很低,但是匹配到最后应该只有一条记录与之相符,这种情况下选择性为1,是最高的。如果采用4个字符作为前缀,那么其实这个索引也就失去了价值,因为它的数量和表记录的数量是相等的,不管怎么优化,做的也都是全表扫描。所以前缀长度的选择非常的重要

ABCDEFGHIJKDDD8

ABCDEFFSKDJKJKD7

ABCDKJH65654654K

ABCDEFGHIJKKJKJG

ABCDEFGHIJKFFFFEJ

可以采取以下方式确定前缀长度,应用别的书籍上的例子,这个例子中city是索引字段

         2.1 首先确定整个表索引值的选择率:

 select count(distinct city)/ count(*) from City_Demo;             

              假设执行的结果是0.0312

         2.2 算出几个候选长度的选择率

 

SELECT COUNT(DISTINCTLEFT(city, 3))/COUNT(*) AS sel3,

COUNT(DISTINCT LEFT(city,4))/COUNT(*) AS sel4,

COUNT(DISTINCT LEFT(city,5))/COUNT(*) AS sel5,

COUNT(DISTINCT LEFT(city,6))/COUNT(*) AS sel6,

COUNT(DISTINCT LEFT(city,7))/COUNT(*) AS sel7

FROM city_demo;

+--------+--------+--------+--------+--------+

| sel3 |       sel4 |       sel5 |  sel6|         sel7 |

+--------+--------+--------+--------+--------+

| 0.0239 | 0.0293 | 0.0305 | 0.0309 | 0.0310 |

       2.3   选一个最靠近的值,从比较的结果看,应该选7作为前缀的长度

       2.4  接下来创建这个前缀索引

               ALTER TABLE sakila.city_demo ADD KEY (city(7));

              最后要指出的是“前缀索引”的缺点:MYSQL无法使用前缀索引做orderby,group by, 以及覆盖索引(后面会提到)

 

3        组合索引中字段的顺序问题

在创建B-Tree的组合索引时,由于B-Tree的匹配顺序是按照存储的顺序来比较的,所以说如果前面的字段可以过滤掉更多的记录的话,后面的条件就会比较更少的记录,当然效率也就更高,用下面的组合索引做个简单的解释

NAME

AREA

 

如果把NAME放在第一个位置,那么在用AREA进行比较时,可能只用10条记录需要比较,如果把AREA放在第一个位置,那么在用NAME进行比较是,可能会有1000条记录需要比较,可以简单的推断出需要把NAME放在索引的第一列,这个原理决定了Hash索引是不适用该规则的。

 

上面的小例子,可以通过字段的选择率来确定索引的字段顺序,转换成SQL的表示方式如下,

 

SELECT COUNT(DISTINCT NAME)/COUNT(*) AS name_selectivity,

COUNT(DISTINCT AREA)/COUNT(*) AS area_selectivity,

COUNT(*)

 FROM student\G

*************************** 1. row***************************

area_selectivity: 0.0001

name_selectivity: 0.0373

COUNT(*): 16049

 

结论:把结果中选择率高的字段放在组合的前面

 

4        聚合索引(Cluster Indexes)

首先要说明的是“聚合索引”并不是指某种具体的索引类型,而是指索引数据的存储方式。其具体的实现细节和数据库引擎实现紧密相关,基本的思想是”将索引和数据行保存在一个数据结构中,且数据行和相邻的键值存储在一起”。如果查询的结果都可以被索引覆盖的话,就不需要再回数据库中进行数据的检索。

 

在Oracle中,可以显示的指定哪些索引为聚合索引。在目前版本的MySQL中,各个存储引擎还不能通过“任意指定”的方式来设置聚合索引。在InnoDB中默认的情况会使用主键作为聚合索引,如果没有建立主键,会选择一个“非空且唯一”的索引来代替,如果 “非空且唯一”的索引也不存在,它会自定义一个主键作为聚合索引。

 

最后需要指出的是,因为这种存储方式中,相邻键值的数据行是存储在一起的,所以应该尽量采用键值递增的插入方式。

如果是使用UUID这种随机的主键,会导致大量的随机IO访问,插入效率会很低

 

 

5        覆盖索引(Converting Indexes)

覆盖索引是指:索引的字段包含了所有要查询的字段,就成为覆盖索引。

例如Student表中在(Name,Area)上建立了索引:

select name,area from student wherename=’Eric’ 这样的查询就成为是被索引覆盖的,从执行计划上看,如果EXPLAIN一条查询的Extra列包含“Using Index”就说明这条查询被索引覆盖了。

 

通常情况下利用索引查询到记录的时候,首先会查询到索引的Node,然后根据索引Node中存储的记录指针再到数据库的中查找记录,这种方式也就相当于进行了两次查询,而且数据库记录查询的效率往往会比较低。覆盖索引由于它本身就包含了要查询的字段值,也就避免了对数据库记录的访问,从而极大的提高了查询的效率。

 

有一点需要注意,因为覆盖索引用到的是索引字段的值,而“Hash/空间/全文” 索引中并没有存储索引字段的实际值,所以他们是不支持覆盖索引的。


关于覆盖索引,除了上面提出的问题外,还需要注意MYSQL的版本问题。MYSQL5.5以后的版本对该特性提供了更好的支持,所以尽可能还是使用最新的版本。

 

6        索引顺序扫描排序

MYSQL 可以按照索引顺序扫描(Explain 中的type为index)以及排序操作来实现排序功能。

 

如果使用了索引顺序扫描以及覆盖索引可以使排序的速度大大的提高,如果索引不能包含所有要查询的列,则在取每条记录值的时候都需要根据记录指针去查找对应的记录,这有可能会带来大量的随机I/O的产生,从而使查询效率下降。所以在设计索引的时候,既要考虑到查询条件,也要考虑到排序操作

 

为了使索引更好的服务于排序动作,在拼写SQL的过程中有以下几个问题需要注意

(1)    使用“覆盖索引”

(2)    索引的顺序和order by的顺序一致,且所有列的排序方向一致

(3)    多表关联排序时,order by的条件字段全部都是第一个表的

(4)    Order by 和where一样,也要遵守“最左原则“

(5)    如果where或者join中指定了某个列为常量,则可以弥补规则(4),

例如  INDEX(Name,Age,ID) 则 ….where name=”Eric” order by age, id 也可以使用索引顺序扫描排序

7        轻便的索引

MYSQL允许在相同的Column上创建多个索引,MYSQL需要单独维护每个重复的索引,而且查询优化器也会在这些重复的索引上花时间,从而可能导致整体性能的下降。

所以应该尽量保持索引的"轻便"

 

·             重复的索引

定义:在相同的列上,按照相同的顺序,创建相同类型的索引。

例如 下面的语句将在ID上创建三个索引,对于Primary KEY而言,UNIQUE以及INDEX都是重复的

Create table test(

 ID int not null PRIMARY KEY,

 UNIQUE(ID),

 INDEX(ID),

)


·             冗余的索引

这里的“冗余“指的是多个索引有着相同的类型,且功能重复。

例如对于B-Tree索引而言,如果创建了索引(A,B),再创建索引(A),则(A)就被认为是重复的,但是如果创建了(B)则不被认为是冗余的。

 

冗余的索引一般是发生在扩展的索引的时候,例如已经存在了一个索引(A),有人为了满足新的查询需求,创建了一个新的索引(A,B),这个时候(A)就成了冗余的索引,应该进行删除,或者是用(A)来扩展成(A,B)

 

有时候冗余的索引也能带来查询性能上的提升,例如当需要向索引中添加了一个超长的字符串字段,因为这个字段会导致索引存储空间的变大,导致了查询效率的降低,所以适当的使用冗余索引对性能会有帮助。

 

可以通过对INFORMATION_SCHEMA中的数据来判断索引是否是重复/冗余,也可以通过专门的工具例如Percona-Toolkit的pt-duplicate-key-checker 来检测(http://www.percona.com/doc/percona-toolkit/2.1/pt-duplicate-key-checker.html)

·             未使用的索引

 

和前面两种情况类似,当系统中存在大量未使用的索引时,同样对查询的效率会有影响,可以通过以下两种方式来判断那些索引时未使用的

 

(1)     在Percona或者是MariaDB中,通过打开userstates服务器变量,然后正常运行一段时间,最后通过INFORMATION_SCHEMA.INDEX_STATISTICS变量来判断

(2)     通过专门的工具,例如Percona-Toolkit的pt-index-usage来判断,该工具不仅可以查出那些索引是没有被使用的,还可以了解查询的执行计划(http://www.percona.com/doc/percona-toolkit/2.1/pt-index-usage.html)

 

需要注意的是,并不是未使用的索引就是没有用的索引,例如有些索引的功能是唯一性约束,虽然该索引一直没有被使用,但是却可以避免产生重复的数据,这种类型的索引在处理的时候需要小心

 

 

 

 

 

 

<think>好的,用户是一位大二网络工程专业的学生,有C语言基础,未来想成为后端开发的架构师,主修C++和Java,辅修Python。需要为他制定一份学习计划,包括需要学习的工具。首先,我得考虑后端架构师需要哪些技能,然后根据他的现有基础来规划路径。 后端架构师通常需要扎实的编程基础,对系统设计、分布式系统、数据库、网络等方面有深入理解。他主修C++和Java,这两种语言在后端开发中都很重要,Java尤其在企业级应用中广泛使用,而C++可能在性能要求高的场景更常见。Python作为辅助,可能在脚本编写、自动化测试或者数据分析方面有帮助。 接下来需要分阶段规划,考虑到他现在大二,时间可能比较充裕,可以分阶段逐步深入。第一阶段应该巩固编程基础,尤其是面向对象和设计模式,同时开始学习数据结构和算法。第二阶段可以深入学习后端开发的核心技术,比如数据库、网络编程、框架使用等。第三阶段转向系统设计、分布式系统、微服务架构等高级主题,同时学习DevOps和云原生技术。第四阶段则是实战和软技能的提升。 工具方面,版本控制Git是必须的,数据库如MySQL、Redis,框架比如Spring Boot、Django,容器技术Docker和Kubernetes,中间件如Kafka、RabbitMQ,监控工具Prometheus等都需要涵盖进去。此外,持续集成工具如Jenkins,云服务平台AWS或阿里云也是必要的。 需要提醒他参与开源项目和实习,积累实战经验。软技能如文档编写、沟通能力也不能忽视。可能还需要补充计算机基础知识,比如操作系统、计算机网络,这些对架构师很重要。另外,算法题练习对于面试和逻辑思维都有帮助。 要注意他的主修语言是C++和Java,可能需要在学习计划中平衡两者,但考虑到后端开发中Java的生态更成熟,可能会侧重Java,同时保持C++在性能优化方面的能力。Python作为辅助,可以用于自动化脚本或快速开发。 时间安排上,每个阶段可能需要一个学期左右,从大二到大四逐步推进。同时,考证如Oracle认证或AWS认证可以提升竞争力,但非必须。最后,持续学习新技术和关注行业动态也是关键。</think>以下是为您量身定制的后端架构师学习计划(分阶段递进): --- ### 一、筑基阶段(大二下学期) 1. **编程基础强化** - C++:深入STL容器/智能指针/模板元编程,阅读《Effective C++》 - Java:掌握JVM内存模型/多线程并发包(java.util.concurrent),完成3个以上控制台项目 - Python:熟练使用Flask/Django框架开发REST API,学习《流畅的Python》 2. **计算机核心课程** - 操作系统:通过MIT 6.S081实验理解进程调度/虚拟内存 - 计算机网络:使用Wireshark分析TCP三次握手/HTTPS握手过程 - 数据结构:LeetCode刷题量达到200+(重点:红黑树/B+树) 3. **开发工具链** - Git:完成Pro Git电子书前6章学习,建立GitHub技术博客 - Linux:在阿里云ECS上部署LNMP环境,掌握sed/awk基础 - IDEA/VSCode:配置Lombok/MyBatis插件,建立代码模板 --- ### 二、进阶阶段(大三学年) 1. **后端技术栈** - Spring Boot:开发电商秒杀系统(整合MyBatis Plus+Redis) - 数据库:MySQL索引优化实战,通过《高性能MySQL》设计分库分表方案 - 中间件:Kafka实现订单异步处理,Redis实现分布式锁 2. **分布式基础** - CAP定理:通过ETCD实现分布式配置中心 - RPC框架:手写简易Dubbo框架,理解ZooKeeper服务注册 - 容器化:使用Docker Compose部署微服务集群 3. **系统设计能力** - 学习《数据密集型应用设计》,设计Twitter级系统架构 - 参与GSOC开源项目,贡献代码量5000+行 - 通过极客时间《后端存储实战课》理解存储引擎设计 --- ### 三、架构师成长阶段(大四及以后) 1. **云原生体系** - Kubernetes:在ACK上部署Istio服务网格 - Serverless:开发AWS Lambda图片处理流水线 - 服务治理:实现全链路灰度发布系统 2. **性能优化** - JVM调优:使用Arthas分析线上OOM问题 - SQL优化:通过Percona Toolkit进行慢查询分析 - 网络优化:实现QUIC协议代理网关 3. **架构方法论** - 学习《演进式架构》设计可进化系统 - 考取AWS/Aliyun架构师认证 - 参与技术大会(如QCon)并做主题分享 --- ### 四、工具链全景图 ```mermaid graph LR A[开发工具] --> B(IntelliJ IDEA/VSCode) A --> C(Postman/Charles) A --> D(Jenkins/GitLab CI) E[运维监控] --> F(Prometheus+Grafana) E --> G(ELK日志系统) E --> H(SkyWalking) I[云平台] --> J(AWS/Aliyun) I --> K(Kubernetes) I --> L(Terraform) ``` --- ### 五、学习路线验证机制 1. 每月产出技术博客(至少3篇) 2. 参与1次Hackathon比赛(如阿里天池) 3. 大四前完成3个完整项目: - 分布式文件存储系统(Go实现) - 电商平台秒杀系统(Spring Cloud Alibaba) - 物联网消息中台(MQTT+Kafka) 建议每周保持30小时有效学习时间,重点关注系统瓶颈分析能力培养。架构师成长是持续过程,建议工作后前3年深入某个垂直领域(如高并发/大数据),5年后拓展技术视野宽度。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值