MySQL查询为啥慢了?

        平时拿起一本书,多么想徜徉在 知识的海洋中遨游!(想多了~)

         潜意识里总是要告诉自己:你,要看书,要学习,要进步!

        大脑:不想看,好累,好无聊啊!

        此时方能体会“知易行难”的深意啊!

        学贵有恒,人贵有志。面对手机电脑的诱惑,心底的那点脆弱不堪的防线太容易被击破了。虽说“金无足赤人无完人”,可照这样下去,含金量无疑是越来越低啊。

        得,没事还是多刷刷Spring,看看技术栈吧,今天就再来看看MySQL,希望能温故而知新吧!

                MySQL查询为啥慢了?_第1张图片

        说到MySQL查询,肯定少不了索引这个关键之中的关键,当然查询计划也是要看的,看了查询计划才能知道MySQL是怎么查询的,到底查询了多少行,查询快慢的原因不就来了么?

        先看下索引失效的场景吧,有时候感觉自己都知道,但是全说出来,好像又有那么一丢丢的难度?

 索引失效有哪些?    

1、没加索引或者使用like “%张三%”这种,不多说,没记住的赶紧重新温习下 MySQL从入门到放弃。

2、 字符串索引,字段编码不一样,大家在建表的时候,有时候不会注意字段编码,直接使用默认,如果大家使用的工具,如Navicat,workbench等,这种直接生成DDL语句,如果DDL中带有默认编码,而这个默认编码和生产的默认编码又不一致。那么,恭喜你,如果有连表查询,索引直接废掉了~ ~ ~ 

        当然,单表查询时没影响的,毕竟你的索引就是根据表中字段建立的,自然不会有什么编码不一致的问题。如果你公司项目从来不允许连表查询,那么,恭喜你,这种问题直接远离了你的项目。

3、隐式的类型转换。 有些哥们可能是疏忽也可能是图个方便,数据库设置字段的时候,许多字段直接设置为varchar格式,然后,在代码里处理的时候,发现,嗯,好像涉及到一丢丢计算,使用Integer接收吧!这个能接收成功么?当然能啊!一个隐式转换,框架都帮你做好了,嘛问题没有。

        不过如果这个数据库字段建立了索引,而且你又喜欢使用这个字段去查询,那就来乐子啦!索引完全没用!是的,没用!为啥呢? 索引树是字符串,你传来的是个数字,类型不一样啊!需要隐式类型转换,索引走不了了。如果索引是数字类型(tinyInt,int,bigInt之类的),你传来的是字符串,这个应该是会走索引的。具体如何,大家可以看下执行计划,在这里面,一切都会现形,哈哈哈。

4、在索引上使用MySQL内置函数或者使用符号运算,比如Date转换,concat连接或者是对字段进行加减乘除之类的。mysql索引本来是建立好的,你对索引进行一个一个操作,和重新建立索引有啥区别?不就是要扫描全表么,这个如果不懂,拿一本《MySQL高性能书籍_第3版(中文)》去面壁思过去!

        MySQL查询为啥慢了?_第2张图片

5、MySQL查询使用了or ,!= 之类的,比如where name = "张" or age = 18 这种,这个哪怕你name和age都加了索引也不行啊,mysql查询的时候选择索引只会使用一个索引,想要查询出所有符合条件的结果,只能进行全表扫描啊。如果这样写SQL的,也可以去面壁思过了。这种如果两个都有索引,可以考虑分开查询,然后使用 union 合并查询结果,这样就会走索引了,速度应该会快那么一丢丢的。

6、索引选择错误,有时候一个table有四五个索引,查询条件又贼多,mysql可能使用其中一个索引进行查询,然而又不是你希望的那个索引,这样查询也是会很慢的。这时,只能看查询计划了,然后就是考虑调整下查询的条件顺序或者索引的顺序,努力让SQL变快!再不行,那就是查询太复杂,上搜索引擎呗。

7、还有就是联合索引,联合索引主要就是一个顺序问题,三个字段a,b,c建立联合索引,这种想要使用索引,必须包含查询条件a,大家要牢记,MySQL查询对比,都是从左往右进行对比,索引能不能用,就从左边往右看,到了那个位置,索引就能用到哪个位置。再具体点,嗯,大家去看下联合索引,应该就啥都明白了(东西太多,展开太长了)。

8、in的元素太多了,使用in查询的时候,如果in中的条件太多(假设条件不是主键索引),MySQL需要先去查二级索引中的数据,获取到id,然后重新去扫描表数据,这种情况下,查询效率可能还不如直接去扫描全表。

                MySQL查询为啥慢了?_第3张图片

使用order by或者group by 不当

         order by 就是排序,如果这个排序的字段有索引,那皆大欢喜,直接使用索引排序,一步搞定!但是如果没有索引呢?比如你索引加的是age(年龄),排序根据的是height(身高),那么你想要去进行排序,要么把身高数据加载到MySQL内存里面排序,如果数据太大,那就要建立一张临时表进行排序了,慢!那是必须滴!

        如果大家使用order by最好是使用索引进行排序或者联合索引的前置部分进行排序。当然如果查询结果不多,放到mysql内存中进行排序问题也不大。如果需要建立临时表进行排序,建议大家慎重啊,那样的话,真的是,不划算啊。

        group by 也就是分组,这个其实比排序还麻烦,哪怕有索引,也要进行二次处理。分组也是先在内存中进行分组,如果数据太多,也会创建临时表,一旦涉及到建表,自然也就有文件IO什么的,肯定慢啊,所以如果进行分组的话,尽量进行少部分数据分组,而不是全表或者几十万数据分组,那样查询时间至少要以秒为单位了。

MySQL锁表

         这种情况其实并不多,一般一个查询或者修改很快就能完成。如果是操作事务死锁,MySQL也有自己的处理机制(一般是需要回滚最少行数的数据进行回滚)。查询的时候如果真的碰到锁表了,而且一直不放开,那你就要检讨自己的代码了,当然,也要尽快查询到锁线程,然后释放掉锁,不然那MySQL直接宕机,不是大发了!

              MySQL查询为啥慢了?_第4张图片

数据库在刷新脏页

        数据库执行SQL,其实就是相当于一个个事务,原子性、一致性、隔离性、持久性,那是必须保持的。而且最终都会将数据写到磁盘中去。

        不过,为了提升读写速度,MySQL中innerDB引擎,在数据库每次进行读写时,特别是更新的时候,都会使用内存。更新的数据一般会先写入内存,然后顺序写入一个顺序文件中,当数据库线程空闲的时候,再将数据彻底刷新到数据库文件中。如果数据库一直处于繁忙状态,当内存,主要是redo log日志写满了,就必须要刷新到磁盘中去了。

        而这个时候,innerDB引擎,就不再接收更新数据请求,这个时候MySQL自然也就慢了。

        还有,InnoDB 用缓冲池(buffer pool)管理内存,而当要读入的数据页没有在内存的时候,就必须到缓冲池中申请一个数据页。这时候只能把最久不使用的数据页从内存中淘汰掉:如果要淘汰的是一个干净页,就直接释放出来复用;但如果是脏页呢,就必须将脏页先刷到磁盘,变成干净页后才能复用。

        MySQL查询为啥慢了?_第5张图片

单表数据量太大

        我们知道,innerDB引擎存储也好,创建的索引也好,都是根据B+树结构。需要按层级进行查找。innerDB存储引擎最小的存储单元是页,一页也就是16KB。也就是说,每次进行IO取出的数据是16KB。

        假设B+树的高度为三层,非叶子节点存储的是主键索引,两层数据之间必然是有指针连接关联。在innerDB中指针的大小是6Byte,如果有有索引大小为8Byte(bigInt类型),那么6+8=14Byte,16KB/14B = 1170个。

        第一层只存储了指针和索引,能存储1170个,第二层自然也是存储指针和索引,存储个数是1170*1170 ,第三层的话,就是存储的就是数据了,如果一条数据的大小是2KB,那么一页就要存储8条数据,三层所存储的数据量也就是1170*1170*8=10951200,也就是一千万左右的数据。

        MySQL查询为啥慢了?_第6张图片

        那如果唯一索引不是bigInt,而是字符串呢?比如32位的uuid,以utf-8编码为例,索引所占的大小是3*32Byte+1Byte(字段长度) = 97Byte,97+6=103Byte。

        如果B+树也是三层,那么第一层存储的数据也就是16KB/103B = 155,也就是每一行存储155个索引,第二层也就是155*155=24025,第三层24025*8 = 192200条数据,也就只有19万左右,这样肯定不能只存储三层,IO自然会增加。

        当然,UUID可以进行转换压缩的。UUID_TO_BIN()函数将UUID从人类可读格式(VARCHAR)转换成用于存储的紧凑格式(BINARY)格式,并且BIN_TO_UUID()函数将UUID从紧凑格式(BINARY)转换为人类可读格式(VARCHAR)。

        即便如此,存储UUID值也至少需要16字节,16KB/(16B+6B) = 727,三层可以存储727*727*8=4228K,也就是400多万。相比于bigInt作为主键索引,存储明显少了许多。

现在主流的雪花算法作为主键id,其类型就是  64 位比特位的 long 类型的唯一 id,存储到mysql,也就是bigInt类型!你是不是想到了什么?嘿嘿。

        对于单表来说,如果数据量超过了1000万,建议是进行分库分表的,当然,如果每行数据量比较小,也可以在扛一段时间,索引使用得当的话,速度也不会很慢,不过还是要考虑后续发展滴,分库分表,尽早最好哈。

        MySQL查询为啥慢了?_第7张图片

        关于mysql查询速度变慢,能想到的也就这么多了,如有疏漏,还请斧正哈!

        人贵有志,学贵有恒!与君共勉!

        等等,点个赞再走呗~

你可能感兴趣的:(数据库,mysql,数据库,java,索引)