本文转自公众号:鄙人薛某

原文:https://www.cnblogs.com/yeya/p/13200655.html

一、反常的SQL语句

某周四午休时分,我正在工位上小憩,睡梦中仿佛看到了自己拿着李白在荣耀峡谷里大杀四方的情景,就在我刚拿完五杀准备带领队友推对面水晶的时候,一句慌乱急促的“糟了”把我从睡梦中惊醒。

我眯开朦胧的双眼,才发现刚才的发声来源于我的组长庄哥,看到他在紧张的点开日志系统查看日志,我预感到有什么不妙的事情发生,仔细一问才知道,原来就在我眯眼的期间,线上数据库服务器的CPU被打满,同时触发了生产数据库只读延迟的限定时间并且发出告警,而且告警的过程持续了半个小时。

这让我倒吸了一口凉气,因为我们组做的系统很多都用的是同一个数据库服务器,日用户活跃量有好几十万,如果服务器崩溃了将会使所有的系统服务都不可用,于是我们赶紧通过SQL日志进行问题查找,最后排查出来是因为一张SQL的高量查询没有走索引导致,日志列表显示,这条sql语句的扫描行数达到了上百万,基本就是全表扫描的情况,而且半个小时的时间查询了达上万次,每条SQL查询的耗时都在3000ms以上。

我的天啊,难怪服务器会CPU打满,这么一条耗时的SQL语句查询量这么大,数据库的资源当然是直接就崩溃了,这是当时那条SQL的查询情况:

二、临时处理

看了这条语句,我又倒吸一口凉气,这不就是我写的系统调用的SQL语句吗?完了,这回逃不掉了,真是人在睡梦里,锅从天上来。

当然,因为是我自己写的SQL,所以我一看就知道这条语句是有问题的。

根据我的代码处理,这条sql的调用还少了个重要的参数user_fruit_id,这个参数没有传的话是不应该走这条sql查询的,在我的设计里,该参数是数据表里一个联合索引的最左侧字段,如果该字段没有传值的话,那么索引就不会生效了。

KEY `idx_userfruitid_type` (`user_fruit_id`,`task_type`,`receive_start_time`,`receive_end_time`
) USING BTREE

虽然定位到了SQL语句,但是线上的问题刻不容缓,总不可能找出bug改完再上线吧,所以,我们只能做了一个临时处理,就是在原来的表上多加了一个联合索引,其实就是去掉了user_fruit_id 字段,让这些高量的查询都能走新的索引,就像下面这样:

KEY `idx_task_type_receive_start_time` (`task_type`,`receive_start_time`,`receive_end_time`,`created_time`
) USING BTREE

加上索引后,sql的扫描行数就大幅度的降低了,重启实例后就又能正常运行了。点击这里获取一份 MySQL 开发军规,关注公众号互联网架构师获取更多 MySQL 系列教程。

三、最左匹配原则

那么为什么最左侧的字段没传索引就不生效了,这是因为MySQL的联合索引是基于“最左匹配原则”匹配的。

我们都知道,索引的底层是B+树结构,联合索引的结构也是B+树,只不过键值数量不是一个,而是多个,构建一颗B+树只能根据一个值来构建,因此数据库依据联合索引最左的字段来构建B+树。

例如我们用两个字段(name,age)这个联合索引来分析,

图片来源于林晓斌老师的《MySQL实战45讲》

当我们在where条件中查找name为“张三”的所有记录的时候,可以快速定位到ID4,并且查出所有包含“张三”的记录,而如果要查找“张三,10”这一条特定的数据,就可以用 name = "张三" and age = 10 获取,因为联合索引的键值对是两个,所以只要前面的name确定的情况下就可以进一步定位到具体的age记录,但是如果你的查询条件只有age的话,那么索引就不会生效,因为没有匹配最左边的字段,后面所有的索引字段都不会生效,所以我之前写的sql语句才会因为少了最左边的user_fruit_id字段而走了全表扫描的查询方式。

正常来说,假设一个联合索引设计成(a,b)这样的结构的话,那么用a and b作为条件,或者a单独作为查询条件都会走索引,这种情况下我们就不要再为a字段单独设计索引了。

但如果查询条件里面只有b的语句,是无法使用(a,b)这个联合索引的,这时候你不得不维护另外一个索引,也就是说你需要同时维护(a,b)、(b) 这两个索引。关注公众号互联网架构师获取更多 MySQL 系列教程和面试题答案。

四、找出Bug

虽然临时做了处理,但问题并不算解决,很明显是系统出现了bug才会有走这样的查询条件。

因为是我自己写的代码,所以知道是哪条SQL后我就马上定位到了代码里的具体方法,后来才发现是因为我对user_fruit_id字段的判空处理不生效所致。

因为该字段是从调用方传过来的,所以我在方法参数里对该字段做了非空限制的注解,也就是javax包下的@NotNull。

public class GardenUserTaskListReq implements Serializable {private static final long serialVersionUID = -9161295541482297498L;@ApiModelProperty(notes = "水果id")@NotNull(message = "水果id不能为空")private Long userFruitId;/**以下省略*/.....................
}

虽然加上该注解来做非空校验,但我却没有在参数加上另一个注解@Validated,该注解如果没加上的话,那么调用javax包下的校验规则就都不生效,正确的写法是在controller层方法的参数前面加上注解:

除此之外,因为user_fruit_id这个字段是另一张表的主键,我在代码里也没有对这张表是否存在这个id做查询判断,这样一来,无论调用方传什么值过来都会直接触发sql查询,并且在不跑索引的情况下直接走全表扫描。

不得不说,这真是个低级错误,说真的,我对这个原因真是感到嘀笑皆非,再怎么说也工作几年了,怎么还犯一些新手级别的错误呢,这脸打得真是让我相当惭愧。

五、总结

虽然是低级错误,但造成的后果也算挺严重了,这次事件也让我更加的警醒,在以后的开发工作中必须要遵守该有的原则,大概有这么几点:

1、不能相信调用端

重要的参数都要先做验证,即使是非空值也需要做验证,不符合条件的就要直接返回或抛异常,不能参与业务sql的查询,否则频繁的访问也会对服务造成负担。

2、SQL语句要先做性能查询

对于数据量大的表,建好索引后,所有的SQL查询语句要用explain检测性能,并且根据结果来进一步优化索引。

3、代码必须要review

之前我没有放太大的精力在代码的review上,虽说跟迭代排期的紧凑也有关系,但不管怎么说,bug确实是我的疏忽造成的,尤其是像空值这种细小的错误在Java里可以说家常便饭。

千里之堤毁于蚁穴,有时一个小bug很容易就引发整个系统的崩盘,这一次的问题也让我更加深刻的认识到了review代码的重要性,不管业务开发的工作量有多麻烦,这一步操作绝对不能忽视。

关注微信公众号:互联网架构师,在后台回复:2T,可以获取我整理的教程,都是干货。

猜你喜欢

1、GitHub 标星 3.2w!史上最全技术人员面试手册!FackBoo发起和总结

2、如何才能成为优秀的架构师?

3、从零开始搭建创业公司后台技术栈

4、程序员一般可以从什么平台接私活?

5、37岁程序员被裁,120天没找到工作,无奈去小公司,结果懵了...

6、滴滴业务中台构建实践,首次曝光

7、不认命,从10年流水线工人,到谷歌上班的程序媛,一位湖南妹子的励志故事

8、15张图看懂瞎忙和高效的区别

9、2T架构师学习资料干货分享

我的一个低级错误,导致数据库崩溃半小时!!相关推荐

  1. 我晕,一个低级错误导致我DEBUG两天(std::string c_str()的问题)

    起因是这样的,为了方便读取RO里的素材,我在OPenRO里加入了一个第三方库,他的作用主要就是负责提取RO素材数据,并把他们放在heap里,程序退出他会自动释放. 但是莫名其妙的问题随之而来了:每次程 ...

  2. 又一例SPFILE设置错误导致数据库无法启动

    --======================================== --又一例SPFILE设置错误导致数据库无法启动 --============================== ...

  3. oracle expdp 06512,oracle的expdp时出现ORA-39125ORA-01555ORA-06512错误导致数据库备份失败!...

    oracle的expdp时出现ORA-39125&ORA-01555&ORA-06512错误导致数据库备份失败! 具体报错信息如下,请Oracle大神急救下: Export: Rele ...

  4. 百度文库崩溃半小时:赶论文的网友也集体崩了

    疫情期间停课不停学,马上要放暑假了,期末论文还是要交的,你写完没? 昨天晚上,百度文库崩溃半小时,吓坏一众在家学习赶论文的学子:你默默在手边的时候不觉得有多重要,但是失去的瞬间却真真切切感觉到不舍(痛 ...

  5. 【心塞】因为一个低级错误,生产数据库崩溃了将近半个小时

    [欢迎关注微信公众号:厦门微思网络] 反常的sql语句 上周四午休时分,我正在工位上小憩,睡梦中仿佛看到了自己拿着李白在荣耀峡谷里大杀四方的情景,就在我刚拿完五杀准备带领队友推对面水晶的时候,一句慌乱 ...

  6. 一个低级错误引发Netty编码解码中文异常

    前言 最近在调研Netty的使用,在编写编码解码模块的时候遇到了一个中文字符串编码和解码异常的情况,后来发现是笔者犯了个低级错误.这里做一个小小的回顾. 错误重现 在设计Netty的自定义协议的时候, ...

  7. 一个低级错误,关于timer

    今天开发一个Windows Service程序,里面的Timer一直都不能启动,不知道什么原因.后来花了将近2个小时才发现,原来我拖入设计界面的是Windows Form的Timer. 低级错误,引以 ...

  8. 记录一次mysql数据库文件损坏导致数据库崩溃

    表现: 服务器数据库查询很慢,莫名数据库服务崩溃 问题排查过程: 按照网上操作,配置my.ini ,添加error-log参数,重启mysql,查看错误日志 按照错误日志内容,搜索可能的错误问题 比如 ...

  9. 哔了沟了,今天犯了一个低级错误

    新来一个同事,总监让我指导一下他,帮他弄了一下环境,用到的软件.他之前没用过MacBook,不过我也是Mac新手,只不过比他早用了几个月而已. 给他弄了SourceTree,克隆代码后 正好现在开发任 ...

  10. 最近一个低级错误犯了两次

    有时定义变量 int  a; for(int i=0;i<10;i++) { a+=i; } 最终a是一个不可预见的值并不是0~9的和 因为a没有初始化 int  a=0;//就好了 好可笑 哈 ...

最新文章

  1. 操作系统识别工具 xprobe2 p0f 简介
  2. ios 从assets加载图片_Flutter图片添加水印功能,Flutter保存Widget为图片
  3. 添加按钮图标并且当点击或者悬浮上面出现不同效果的代码
  4. VIM进阶-宏Macro
  5. js 将图片置灰_JS 上传一张图片让其部分变灰色
  6. The Apache Tomcat installation at this directory is version,tomcat
  7. 国嵌c语言深度,国嵌C语言3部全
  8. poj2385 基础的动态规划算法 挑战程序设计竞赛
  9. 学计算机的普通学生那里就业,学计算机我后悔了 现在好就业吗
  10. 打开运行PS、AI等软件时卡在启动窗口的解决办
  11. workbench表头可以是中文_超简单:Python 5步去中国式报表表头
  12. 北京联通华为光猫HG8346R破解改桥接
  13. 算法常用术语中英对照
  14. JPA的cannot simultaneously fetch multiple bags异常的解决
  15. 抖音sdk,抖音开发api接口
  16. hadoop3.3.1搭建过程遇到的坑
  17. element-UI设置背景色和边框色
  18. c语言 aligned,“__attribute __((packed,aligned(4)))”的含义是什么意思?
  19. 解决MAC系统升级后虚拟机黑屏问题
  20. 项目案列:银行ATM存款机系统(笔记经典案列)

热门文章

  1. 还在用iTunes管理iPhone这款软件帮你轻松解决
  2. 音乐艺考生如何提高视唱练耳技巧
  3. 基于SpringMVC国际化资源配置方式Demo
  4. 20050909:女乘客钓男司机?
  5. 吴恩达的21节Deeplearning.ai课程学习经验总结
  6. 你知道地球上还剩多少石油吗?
  7. codeforces D. Palindrome pairs 动态规划
  8. fm足球经理Football Manager 2022 for mac(真实模拟游戏)中文版
  9. indesign入门教程,如何创建文本列?
  10. 苹果Mac应用音频捕获录制工具:​​​​​​​​TunesKit Audio Capture