问题解决
问题排查
- 找出那个进程出问题,执行top列出系统各个进程的资源占用情况。
![top.png](https://290ff162.telegraph-image-eg9.pages.dev/file/0dcfd52cf39085f7207e5.jpg)
- 找到CPU或者内存高的进程,执行top -Hp 【pid】列出对应进程里面的线程占用资源情况
![tophp.png](https://290ff162.telegraph-image-eg9.pages.dev/file/544496f7094b3e0bf4356.jpg)
java进程CPU 100%
- 打印堆栈信息jstack [pid] | grep -A 10 [tid的十六进制 printf "%x" 线程id]
- 导出该线程的堆栈信息(jstack pid)> log.txt,线程栈分析可使用 VisualVM 插件 TDA。
- 重点关注:WAITING/BLOCKED
- 找到线程中waiting on
<xx>
或 blocked的都有哪些,根据锁的地址找到是哪个线程持有<xx>
这把锁,里面有代码行数
mysql死锁?
- 使用JDK自带的一些性能监控工具进行排查,比如说jps、jstat、jinfo、jmap、jstack、jcmd等等。
- 使用jstack进程号查看当前Java进程的线程堆栈信息,看看是否有线程在等待锁资源
排查死锁步骤:
- 查看死锁日志show engine innodb status;LATESTDETECTED DEADLOCK记录最后的死锁信息,TRANSACTION是事务信息
- 找出死锁sql
- 分析sql加锁情况
- 模拟死锁案发
- 分析死锁日志
- 分析死锁结果
java进程内存Mem 100%
- 一般是因为创建了大量对象所导致,持续飚高说明垃圾回收跟不上对象创建的速度,或者内存泄露导致对象无法回收。
- 先观察垃圾回收的情况,看看是不是因为持久代或年老代满了,产生Full GC,导致CPU利用率和内存持续飙高
- 监控系统:大部分公司都会有,可全方位监控 JVM 的各项指标。
- 查看概况jstat -gcutil PID 1000 5监控gc,每1s 输出一次,一共 5 次。
- S0、S1:Survivor空间0和1的使用率(以百分比表示)。
- E:Eden区的使用率(百分比)。
- O:老年代的使用率(百分比)。
- M:Metaspace的使用率(百分比,Java 8及以后版本)。
- CCS:压缩类空间的使用率(百分比,如果存在)。
- YGC:Minor GC次数。
- YGCT:Minor GC耗时(秒)。
- FGC:Full GC次数。
- FGCT:Full GC耗时(秒)。
- GCT:GC总耗时(秒)。
![jstatgcutil.png](https://290ff162.telegraph-image-eg9.pages.dev/file/f2a965b44585b2777b677.jpg)
- 查询详细jstat -gc -h3 PID 250 10监控gc,每三行输出一次表头 ,每250ms 输出一次,一共 10 次。
- S0C、S1C、S0U、S1U:Survivor空间0和1的容量(Capacity)和使用量(Usage)。
- EC、EU:Eden区的容量和使用量KB。
- OC、OU:老年代的容量和使用量。
- MC、MU:Metaspace元数据区的容量和使用量(在Java 8及以后版本)。
- CCSC、CCSU:压缩类空间的容量和使用量(如果存在)。
- YGC、YGCT:Minor GC(新生代GC)的次数和总耗时。
- FGC、FGCT:Full GC的次数和总耗时。
- GCT:GC的总耗时(Minor GC和Full GC的总和)。
![jstatgc.png](https://290ff162.telegraph-image-eg9.pages.dev/file/bc00ae1d99e53ed81ef0a.jpg)
- jmap -histo PID | head -20 查看堆内存占用空间最大的前 20 个对象类型,可初步查看是哪个对象占用了内存。
![jmaphisto.png](https://290ff162.telegraph-image-eg9.pages.dev/file/007d82138398ee26a1dd7.jpg)
- 如果每次 GC 次数频繁,而且每次回收的内存空间也正常,那说明是因为对象创建速度快导致内存一直占用很高;
- 如果每次回收的内存非常少,那么很可能是因为内存泄露导致内存一直无法被回收。
- 导出堆内存多次
- (推荐)jmap ‐dump:format=b,file=/tmp/dump.hprof pid
- 开启HeapDumpOnOutOfMemoryError会stoptheworld(分布式) ‐Xms8m ‐Xmx8m ‐XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./heapdump.hprof溢出时自动生成 java_pidxxxx.hprof
- 离线分析
- 使用jhat工具:jhat ‐port 9999 /tmp/dump.dat ,访问http://ip:9999 通过OQL查询
- (推荐)下载jvisualvm工具,高版本不自带,需要配置本地etc/visualvm_jdkhome:菜单 > 文件 > 装入 dump 文件。,通过summary查询;或者远程连接别的jvm
‐ Dcom.sun.management.jmxremote #允许使用JMX远程管理
‐ Dcom.sun.management.jmxremote.port=9999 #JMX远程连接端口- Dcom.sun.management.jmxremote.authenticate=false #不进行身份认证,任何用
- Dcom.sun.management.jmxremote.ssl=false #不使用ssl
- jprofiler商业软件,GCViewer 工具。
- 在线分析平台 GCEasy。
- 使用MAT工具(基于Eclipse免费)分析内存溢出,查看Leak Suspects
![matanalyze.png](https://290ff162.telegraph-image-eg9.pages.dev/file/b2f191c4405c1d75c38d4.png)
问题解决
- 频繁 minor gc 怎么办?通常情况下,由于新生代空间较小,Eden 区很快被填满,就会导致频繁Minor GC,可通过增大新生代空间-Xmn来降低 Minor GC频率
- 频繁 Full GC /内存泄漏问题怎么办?
- 程序导致 FGC原因
- 大对象:系统一次性加载了过多数据到内存中(比如 SQL 查询未做分页),导致大对象进入了老年代。
- 内存泄漏:频繁创建了大量对象,但是无法被回收(比如 IO 对象使用完后未调用 close 方法释放资源)
- 程序频繁生成一些长生命周期的对象,当这些对象的存活年龄超过分代年龄时便会进入老年代,
- 程序 BUG
- 代码中显式调用了 gc方法,包括自己的代码甚至框架中的代码。
- JVM 参数设置问题:包括总内存大小、新生代和老年代的大小、Eden区和S区的大小、元空间大小、垃圾回收算法等等
- 查看监控,了解出现问题的时间点以及当前 FGC 的频率(可对比正常情况看频率是否正常)
- 了解该时间点之前有没有程序上线、基础组件升级等情况。
- 再对步骤 1 中列出的可能原因做排除法,其中元空间被打满、内存泄漏、代码显式调用 gc 方法比较容易排查。
- 针对大对象或者长生命周期对象导致的 FGC,可通过 jmap -histo 命令并结合 dump 堆内存文件作进一步分析,需要先定位到可疑对象。
- 通过可疑对象定位到具体代码再次分析,这时候要结合 GC 原理和 JVM 参数设置,弄清楚可疑对象是否满足了进入到老年代的条件才能下结论。
- 了解 JVM 的参数设置,包括:堆空间各个区域的大小设置,新生代和老年代分别采用了哪些垃圾收集器,然后分析 JVM 参数设置是否合理。
- 程序导致 FGC原因
查询有多少台机器连接到这个端口netstat -nat | grep 12200 –c
查看网络流量。cat /proc/net/dev
查看系统平均负载。cat /proc/loadavg
查看系统内存情况。cat /proc/meminfo
查看CPU的利用率。cat /proc/stat
1.3. mysqlCPU100%/内存100%
- showprocesslist,查看session情况,确定是不是有消耗资源的sql在运行。
- 找出消耗高的sql,看看执行计划是否准确,索引是否缺失,数据量是否太大。
- 查看 MySQL 慢查询日志,看是否有慢 SQL 。
- 处理:
- kill掉这些线程(同时观察cpu使用率是否下降),
- 进行相应的调整(比如说加索引、改sql、改内存参数)
- 重新跑这些SQL。
- 优化或者添加索引
2. 微服务高可用配置
- nacos + MySQL 4C 8G 3节点 4000TPS
- gateway 4C 8G 3节点 启用HTTPS+GZIP 1wQPS
- zookeeper 4C 8G 3节点 4000TPS
- alibaba Sentinel + nacos + MySQL 3节点
- rabbitmq 3节点 镜像集群模式
- Elasticsearch 3节点
- mysql MGR 1主2从 3节点
- skywalking + nacos 2节点
- XXL-JOB 2节点
- RocketMQ 1主2从
3. 项目难点
![jvmadjust.png](https://290ff162.telegraph-image-eg9.pages.dev/file/4c4b49aab79ea76a06c7b.png)
- 案例:电商公司的运营后台系统,偶发性的引发 OOM 异常,堆内存溢出。
- 1)因为是偶发性的,所以第一次简单的认为就是堆内存不足导致,单方面的加大了堆内存从 4G 调整到 8G -Xms8g。
- 2)问题依然没有解决,只能从堆内存信息下手,通过开启了-XX:+HeapDumpOnOutOfMemoryError参数 获得堆内存的 dump 文件
- 3)用 JProfiler 对 堆 dump 文件进行分析,通过 JProfiler 查看到占用内存最大的对象是 String 对象,本来想跟踪着 String 对象找到其引用的地方,但 dump 文件太大,跟踪进去的时候总是卡死,而 String 对象占用比较多也比较正常,最开始也没有认定就是这里的问题,于是就从线程信息里面找突破点。
- 4)通过线程进行分析,先找到了几个正在运行的业务线程,然后逐一跟进业务线程看了下代码,有个方法引起了我的注意,导出订单信息
- 5)因为订单信息导出这个方法可能会有几万的数据量,首先要从数据库里面查询出来订单信息,然后把订单信息生成 excel,这个过程会产生大量的 String 对象。
- 6)为了验证自己的猜想,于是准备登录后台去测试下,结果在测试的过程中发现导出订单的按钮前端居然没有做点击后按钮置灰交互事件,后端也没有做防止重复提交,因为导出订单数据本来就非常慢,使用的人员可能发现点击后很久后页面都没反应,然后就一直点,结果就大量的请求进入到后台,堆内存产生了大量的订单对象和 EXCEL 对象,而且方法执行非常慢,导致这一段时间内这些对象都无法被回收,所以最终导致内存溢出。
- 7)知道了问题就容易解决了,最终没有调整任何 JVM 参数,只是做了两个处理:
- 在前端的导出订单按钮上加上了置灰状态,等后端响应之后按钮才可以进行点击
- 后端代码加分布式锁,做防重处理
- 这样双管齐下,保证导出的请求不会一直打到服务端,问题解决!
4. sql优化
4.1. 开启慢查询日志
my.cnf配置或者--log-slow-queries[=file_name]选项启动
slow_query_log = ON
slow_query_log_file = /usr/local/mysql/data/slowselect.log
long_query_time = 1 #超过1秒则为慢查询
# 检查慢查日志是否开启/阈值(默认10s)/位置
mysql> show variables like 'slow_query_log'
mysql> show VARIABLES like 'long_query_time';
mysql> show variables like 'slow_query_log_file';
# 检查慢查日志开启/位置/阈值
mysql> set slow_query_log='ON';
mysql> set long_query_time = 2;
mysql> set slow_query_log_file=' /usr/share/mysql/sql_log/mysql-slow.log'
# 查看慢查询日志。
shell> more localhost-slow.log
# 慢查询日志存储格式
# Time: 180526 1:06:54 查询的执行时间
# User@Host: root[root] @ localhost [] Id:4 执行sql的主机信息
# Query_time:0.000401 SQL的查询时间
# Lock_time:0.000105 锁定时间
# Rows_sent:2 所发送的行数
# Rows_examined:2 扫描了多少行。执行器每次调用引擎获取数据行都会累加。行数越少,访问磁盘数据的次数越少,消耗的CPU资源越少。但引擎扫描行数跟rows_examined并不是完全相同的。
# SET timestamp=1527268014; SQL执行时间
# select * from staff; SQL的执行内容
4.2. 慢SQL如何定位
- 通过工具比如mysqldumpslow去分析对应的慢查询日志。
shell>mysqldumpslow --help
Usage: mysqldumpslow [ OPTS... ] [ LOGS... ]
Parse and summarize the MySQL slow query log. Options are
--verbose verbose
--debug debug
--help write this text to standard output
-v verbose
-d debug
-s ORDER what to sort by (al, at, ar, c, l, r, t), 'at' is default
al: average lock time
ar: average rows sent
at: average query time
c: count
l: lock time
r: rows sent
t: query time
-r reverse the sort order (largest last instead of first)
-t NUM just show the top n queries
-a don't abstract all numbers to N and strings to 'S'
-n NUM abstract numbers with at least n digits within names
-g PATTERN grep: only consider stmts that include this string
-h HOSTNAME hostname of db server for *-slow.log filename (can be wildcard),
default is '*', i.e. match all
-i NAME name of server instance (if using mysql.server startup script)
-l don't subtract lock time from total time
锁定时间最长的前10条
shell>mysqldumpslow -s l -t 10 /var/lib/mysql/mysql-slow.log
返回记录集最多的10个SQL
shell>mysqldumpslow -s r -t 10 mysql_slow.log
访问次数最多的10个SQL
shell>mysqldumpslow -s c -t 10 mysql_slow.log
按照时间排序的前10条里面含有左连接的查询语句
shell>mysqldumpslow -s t -t 10 -g “left join” mysql_slow.log
- 使用show processlist命令查看当前MySQL在进行的线程,state包括线程的状态、是否锁表、SQL的执行情况
- 服务监控:可以在业务的基建中加入对慢SQL的监控,常见的方案有字节码插桩、连接池扩展、ORM框架过程,对服务运行中的慢SQL进行监控和告警
- 使用 EXPLAIN 命令查看 MySQL 是如何执行 SQL 语句
4.3. 怎么看explain执行计划?
查看执行计划explain xxx
id:查询的序列号,表示查询中执行Select子句或操作表的顺序。id值相同,执行顺序由上而下。id值越大越先被执行.id列为null的就表是这是一个结果集,不需要使用它来进行查询。
select_type,查询的类型
- SIMPLE : 查询中不包含子查询或者UNION或者表连接
- PRIMARY: 查询中若包含复杂的子查询或者union,最外层的查询则标记为PRIMARY
- SUBQUERY : 在SELECT或者WHERE列表中包含子查询
- DERIVED : 在from列表中包含子查询被标记为DRIVED衍生,MYSQL会递归执行这些子查询,把结果放到临时表中
- UNION: UNION 中的第二个或者后面的查询语句, 若union包含在from子句的子查询中,外层 select被标记为:derived
- dependent union:与union一样,出现在union 或union all语句中,但是这个查询要受到外部查询的影响
- dependent subquery:与dependent union类似,表示这个subquery的查询要受到外部表查询的影响
- union result:包含union的结果集,在union和union all语句中,因为它不需要参与查询,所以id字段为null
- UNION RESULT: 从union表获取结果的select
table:正在访问的表。null表示不涉及对数据表的操作,
<derived N>
表示临时表,结果来自于查询id为N的结果集。如果是<union M,N>,表示结果来自于union查询id为M,N的结果集。type列:表示关联类型或访问类型,速度system>const>eq_ref>ref>fulltext>ref_or_null>index_merge>unique_subquery>index_subquery>range>index>ALL最好能优化到range级别或则ref级别
- system:表仅有一行记录(系统表),往往不需要进行磁盘IO,速度非常快
- const:表示查询时命中primarykey主键或者unique唯一索引,或者被连接的部分是一个常量(const)值。这类扫描效率极高,返回数据量少,速度快
- eq_ref:多表连接时命中主键primarykey或者uniquekey索引
- ref : 多表连接中使用普通索引
- ref_or_null:与ref方法类似,只是增加了null值的比较
- fulltext:全文索引检索,若全文索引和普通索引同时存在时优先选择使用全文索引
- index_merge:使用了索引合并优化方法,查询使用了两个以上的索引。最后取交集或者并集,常见and ,or的条件使用了不同的索引,官方排序这个在ref_or_null之后,但是实际上由于要读取所个索引,性能可能大部分时间都不如range
- unique_subquery:用于where中的in形式子查询,子查询返回不重复值唯一值
- index_subquery:用于where中的in形式子查询,子查询可能返回重复值
- range:使用索引选择行,仅检索给定范围内的行。简单点说就是针对一个有索引的字段,给定范围检索数据。在where语句中使用bettween...and、<、>、<=、in等条件查询type都是range。
- index:扫描整个索引表, 遍历索引索引文件,从索引中读取数据
- ALL:全表扫描
possible_keys:可能使用哪些索引来查找,
key:真正使用的索引,如果为null,则表示没有使用索引,
key_len:使用的索引的最大可能长度,在不损失精确度的情况下,长度越短越好
ref:显示索引的哪一列被使用了,const使用的常数等值查询,,如果是连接查询,被驱动表的执行计划这里会显示驱动表的关联字段,如果是条件使用了表达式或者函数,或者条件列发生了内部隐式转换,这里可能显示为func,NULL,表示没有使用索引
rows:估算找出所需记录要读取的行数 (有多少行记录被优化器读取) ,越少越好
Extra:额外信息
- distinct:在select部分使用了distinc关键字
- no tables used:不带from字句的查询或者From dual查询、使用not in()形式子查询或not exists运算符的连接查询,这种叫做反连接。即,一般连接查询是先查询内表,再查询外表,反连接就是先查询外表,再查询内表
- Using filesort:说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取,Mysql中无法利用索引完成的排序操作称为文件排序
- Using temporary:使用了临时表保存中间结果,常见于order by 和分 组查询group by
- Using index:使用了覆盖索引,以避免回表。如果同时出现了using where, 表明索引被用来执行索引键值的查找。没有则表明索引用来读取数据而非执行查找动作
- using where : 使用了where条件过滤。没有使用索引覆盖,需要回表
- using join buffer : 表明使用了连接缓存, join次数太多了可能会出现
- impossible where : where子句中的值总是false,不能用来获取任何数据
- using intersect:表示使用and的各个索引的条件时,该信息表示是从处理结果获取交集
- using union:表示使用or连接各个使用索引的条件时,该信息表示从处理结果获取并集
- using sort_union和using sort_intersection:与前面两个对应的类似,只是他们是出现在用and和or查询信息量大时,先查询主键,然后进行排序合并后,才能读取记录并返回。
- firstmatch(tb_name):常见于where字句含有in()类型的子查询。如果内表的数据量比较大,就可能出现这个
- loosescan(m..n):5.6.x之后引入的优化子查询的新特性之一,在in()类型的子查询中,子查询返回的可能有重复记录时,就可能出现这个
filtered:表示存储引擎返回的数据在server层过滤后,剩下多少满足查询的记录数量的比例,注意是百分比,不是具体记录数
4.4. 有哪些方式优化慢SQL?
- 减少请求列(select *)、请求行(limit,where)
- 删除数据时,切分删除:如果一个大的语句一次性完成的话,可能需要一次性锁住很多数据、占满整个事务日志,耗尽系统资源、阻塞其他查询
- where高于having,能写在where限定的条件就不要去having限定了
- 分页优化(数据量大优化)
- 延迟关联:先通过where条件提取出主键,在将该表与原数据表关联,
select a.* from table a,
(select id from table where type = 2 and level = 9 order by id asc limit 190289,10 ) b
where a.id = b.id
- 书签方式:记住上一次查询返回的最后一行的某个值,然后下一次查询从这个值开始,避免了扫描大量不需要的行。
SELECT id, name
FROM users
WHERE id > last_max_id -- 假设last_max_id是上一页最后一行的ID
ORDER BY id
LIMIT 20;
- 索引优化:合理地设计和避免索引失效场景
- 临时添加/修改/删除索引,
- 假设一主一备,主库A、备库B,
- 备库关闭binlog:set sql_log_bin=off,然后执行alter table 语句加上索引
- 主备切换;
- 在原来主库关闭binlog:set sql_log_bin=off,然后执行alter table 语句加上索引
- gh-ost方案
- 假设一主一备,主库A、备库B,
- 修改SQL:MySQL 5.7提供了query_rewrite把输入的一种语句改写成另外一种模式。
mysql> insert into query_rewrite.rewrite_rules(pattern, replacement, pattern_database) values ("select * from t where id + 1 = ?", "select * from t where id = ? - 1", "db1");
mysql> call query_rewrite.flush_rewrite_rules(); - 强制索引 select * from t force index(a) where a=xxx;
- 临时添加/修改/删除索引,
- JOIN优化
- 优化子查询:尽量使用Join语句来替代子查询,因为子查询是嵌套查询,而嵌套查询会新创建一张临时表,而临时表的创建与销毁会占用一定的系统资源以及花费一定的时间,同时对于返回结果集比较大的子查询,其对查询性能的影响更大
- 小表驱动大表:关联查询的时候要拿小表去驱动大表,因为关联的时候,MySQL内部会遍历驱动表,再去连接被驱动表。
- 子查询尽量不要放在被驱动表,有可能使用不到索引
- 若必须用到子查询,可将子查询设置为驱动表,因为驱动表的type 肯定是 all,而子查询返回的结果表没有索引,必定也是all
- 确定on或者using子句的列上有索引
- 适当增加冗余字段;可以减少大量的连表查询,空间换时间的优化策略.因为多张表的连表查询性能很低,
- 避免使用JOIN关联太多的表:《阿里巴巴Java开发手册》规定不要join超过三张表,第一join太多降低查询的速度,第二join的buffer会占用更多的内存。如果不可避免要join多张表,可以考虑使用数据异构的方式异构到ES中查询。
- 排序优化
- 利用索引扫描做排序:MySQL有两种方式生成有序结果:其一是对结果集进行排序的操作,其二是按照索引顺序扫描得出的结果自然是有序的。但是如果索引不能覆盖查询所需列,就不得不每扫描一条记录回表查询一次,这个读操作是随机IO,通常会比顺序全表扫描还慢。因此,在设计索引时,尽可能使用同一个索引既满足排序又用于查找行。只有当索引的列顺序和ORDERBY子句的顺序完全一致,并且所有列的排序方向都一样时,才能够使用索引来对结果做排序
- UNION优化
- 条件下推:MySQL处理union的策略是先创建临时表,然后将各个查询结果填充到临时表中最后再来做查询,很多优化策略在union查询中都会失效,因为它无法利用索引:最好手工将where、limit等子句下推到union的各个子查询中,以便优化器可以充分利用这些条件进行优化
- 除非确实需要服务器去重,一定要使用unionall,如果不加all关键字,MySQL会给临时表加上distinct选项,这会导致对整个临时表做唯一性检查,代价很高。
4.5. sql已经走索引了还是很慢,为什么,怎么优化?√
- 命中索引还得结合explain中的rows扫描行数
- 扫描行数过多可能是走了全表扫描,如果删除数据造成空洞太多可以定期用ANALYZE分析表减少空洞
4.6. 为什么使用索引会加快查询?
减少磁盘I/O操作:数据库通常存储在磁盘上,而磁盘I/O操作是数据库操作中耗时较多的部分。没有索引时,数据库为了找到匹配的行,需要进行全表扫描,即读取表中的每一行数据,这会导致大量的磁盘I/O操作。而使用索引后,数据库可以直接定位到索引指示的数据位置,从而减少了磁盘I/O操作的次数。
优化查询时间复杂度:全表扫描的时间复杂度是O(n),意味着随着数据量的增加,查询时间呈线性增长。而使用索引后,数据库可以利用索引的数据结构(如MySQL的InnoDB存储引擎默认的B+树)来快速定位数据,B+树的查询效率非常高,时间复杂度为O(logN),因此可以显著加快查询速度。
索引文件体积小:索引文件相较于数据库文件,其体积要小得多。因此,查询索引文件所需的磁盘I/O操作也会相应减少。当在索引中找到匹配项后,再映射到实际的数据库记录,整体查询效率会显著提高。
4.7. 如何设计索引?
- 选择合适的列作为索引:经常作为连接(JOIN)列、查询条件(WHERE子句)、分组条件(GROUPBY子句)、排序条件(ORDERBY子句)
- 避免过多的索引(索引的功能相同)
- 利用覆盖索引:InnoDB使用非主键索引查询数据时会回表,但是如果索引的叶节点中已经包含要查询的字段,那它没有必要再回表查询了,这就叫覆盖索引
- 适当使用前缀索引,降低索引的空间占用,提高索引查询效率。但无法做order by和group by操作,也无法作为覆盖索引
- 正确使用联合索引,注意最左匹配原则。索引的顺序应根据列在查询中的使用频率和重要性来安排。
4.9. 索引不适合哪些场景呢?
- 数据量比较少的表不适合加索引
- 更新比较频繁的字段
- 区分度低的字段不适合加索引(如性别)
- 不建议用无序的值(身份证、UUID)作为索引,当主键具有不确定性,会造成叶子节点频繁分裂,出现磁盘存储的碎片化
4.8. 索引哪些情况下会失效呢?√
在索引列上使用内置函数或者表达式
对索引列运算(如,+、-、*、/),
索引字段上使用(!=或者<>,notin)时,可能会导致索引失效。!=、<>在主键字段和唯一索引字段中会走索引,在普通索引的字段上不会走索引。解决方法:通过把不等于操作符改成or,可以使用索引,避免全表扫描
like通配符(%xxx或者_xx)。
查询条件包含or,可能会导致索引失效 or中有一个不用索引就会索引失效,尽量使用union all代替
MySQL优化器估计使用全表扫描要比使用索引快,则不使用索引。
联合索引不满足最左前缀原则时,索引会失效。
如果字段类型是字符串,where时一定要用引号括起来,否则会因为隐式类型转换,索引失效。相当于函数运算。查看数据类型转换系统默认规则:select “10” > 9 =>1表示“将字符串转成数字”,0表示“将数字转成字符串”
索引字段上使用isnull,isnotnull,可能导致索引失效。
左连接查询或者右连接查询关联的字段编码格式不一样,可能导致索引失效(Concat\CAST\CONVERT)。
对于连续的数值,能用 between 就不要用 in 了。如果是子查询,可以用exists代替in,如果是子查询,可以用exists代替in
order by索引失效:where子句出现索引的范围查询,复合条件排序类别desc、asc不一致
4.10. 索引是不是建的越多越好呢?
- 当然不是。
- 索引会占据磁盘空间
- 降低更新表的速度:更新表(INSERT、UPDATE、DELETE操作)时,所有的索引都需要被更新。
- 维护索引文件需要成本;还会导致页分裂,IO次数增多。
- 单表索引不超过5个