新进入公司,接手了一个发票系统。技术是比较简单的,就是普通的springboot架构加上mysql的数据库。数据量整体上也不是很大,总表也就千万级别的数据,理论上这点数据不应该有什么性能问题的,但是其中的一个菜单在查询的时候,总是感觉到特别的慢。
比较慢的这个菜单我们称之为A菜单,A菜单和B菜单,C菜单在底层是公用的同一个mybatis的select语句。同时,查询时都会加上时间范围的限制,而且这个时间字段也是正常加上索引的。但是很奇怪的是,A菜单查询时经常需要几十秒,有时甚至查不到结果。B菜单和C菜单速度就很快了,一般1秒内都会查询出结果。大家都是用时间范围查询,也没加其他太多的条件。理论上不大可能有很大差别的。后面无意间发现,连查出来的数据量都不一样,这就更奇怪了,页面的查询条件也一样啊。
在下面我们先回顾一下SQL的八股,对于数据库中字段为NULL的八股。
- NULL值会影响一些函数的统计,如count,遇到NULL值,这条记录不会统计在内。
- B+树不存储NULL,所以索引用不到NULL,会造成第一点中说的统计不到的问题。
- NOT IN子查询在有NULL值的情况下返回的结果都是空值。
- MySQL在进行比较的时候,NULL会参与字段的比较,因为NULL是一种比较特殊的数据类型,数据库在处理时需要进行特数处理,增加了数据库处理记录的复杂性。
此次的问题就在于八股中的第二点。后续经过仔细的排查,我发现A菜单中的时间范围对应的字段是create_time, 表中所有的行都存在create_time值。虽然create_time字段存在索引,但是可能由于表太大,且时间范围内命中的记录又比较多,因此sql最终执行的时候优化并没有走索引,而是全表,后续加上了force index,A菜单的查询效率就从60+秒降到了3+秒.B菜单和C菜单中的时间范围对应的字段是apply_time, 因为业务逻辑的原因,这个字段不为空的行数其实并没有很多,量级大概在10W+,同时这个字段上也加上了索引,结合八股的第二条,我们就可以知道B菜单和C菜单其实一直是在10W+数据中进行筛选,所以速度也比较快,同时查询出来的数据也相对少很多。