MybatisPlus应用参数类型不一致导致索引失效

业务场景介绍

在电商项目中,有一个商品表【t_goods】和一个商品sku表【t_goods_sku】,具体表结构如下所示:

CREATE TABLE `t_goods`  (
    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键id',
    `brand_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '品牌ID',
    `buy_count` int NULL DEFAULT 0 COMMENT '购买数量',
    `comment_num` int NULL DEFAULT NULL COMMENT '评论数量',
    `cost` decimal(16, 4) NULL DEFAULT NULL COMMENT '成本价格',
    `goods_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '商品名称',
    `goods_unit` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '计量单位',
    `grade` decimal(16, 4) NULL DEFAULT NULL COMMENT '商品好评率',
    `intro` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '商品详情',
    ...
    PRIMARY KEY (`id`) USING BTREE,
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '商品表' ROW_FORMAT = Dynamic;

CREATE TABLE `t_goods_sku`  (
    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键id',
    `goods_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '商品ID',
    `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '商品名称',
    `unit` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '计量单位',
    `intro` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '商品详情',
    `market_enable` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '上架状态',
    `auth_flag` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '审核状态',
    `price` decimal(16, 4) NULL DEFAULT NULL COMMENT '商品价格',
    `quantity` int NULL DEFAULT NULL COMMENT '库存',
    PRIMARY KEY (`id`) USING BTREE,
    INDEX `idx_goods_id_market_enable_auth_flag`(`goods_id` ASC, `market_enable` ASC, `auth_flag` ASC) USING BTREE COMMENT '商品id、上架状态、审核状态联合索引',
    INDEX `inx_goods_id`(`goods_id` ASC) USING BTREE COMMENT '商品id'
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '商品sku表' ROW_FORMAT = Dynamic;

在t_goods_sku表中通过goods_id可以找到指定商品的所有sku信息,这正是出现索引失效的场景。

索引失效问题描述

根据goods_id获取该商品对应的sku列表信息,代码如下:

goodsSkuService.list(new LambdaQueryWrapper().eq(GoodsSku::getGoodsId, goodsId))

这段代码在实际执行中发现比较慢,通过explain命令查看执行计划:

EXPLAIN
SELECT * FROM qu_goods_sku WHERE goods_id = 99994373;

执行结果如下:
在这里插入图片描述
从执行结果来看,t_goods_sku两个索引,一个都未命中。

索引失效问题分析

MySQL 索引失效是一个常见的性能问题,它可能导致查询变慢甚至全表扫描。以下是一些常见的导致索引失效的情况和解决方法的总结:

  1. 不使用索引列进行查询:当查询条件中不包含索引列,MySQL 将无法使用索引来加速查询。解决方法是确保查询条件中包含适当的索引列,以便 MySQL 可以使用索引来执行查询。

  2. 索引列上使用函数或表达式:如果在索引列上使用函数或表达式,MySQL 将无法使用索引。解决方法是尽量避免在索引列上使用函数或表达式,或者考虑创建函数索引来支持这些查询。

  3. 列类型不匹配:当查询条件中的列类型与索引列的类型不匹配时,MySQL 可能无法使用索引。解决方法是确保查询条件的列类型与索引列的类型相匹配。

  4. 组合索引顺序不正确:对于组合索引,索引列的顺序非常重要。如果查询条件中的列顺序与组合索引的列顺序不匹配,MySQL 可能无法使用索引。解决方法是确保查询条件中的列顺序与组合索引的列顺序一致。

  5. 数据分布不均匀:如果索引列的数据分布不均匀,MySQL 可能无法有效地使用索引。这种情况下,可以考虑重新设计索引或使用分区表来改善数据分布。

  6. 过多的索引:过多的索引可能导致索引失效和性能下降。解决方法是仅创建必要的索引,并定期评估和优化现有索引。

  7. 隐式类型转换:当查询条件中的列类型与索引列的类型不匹配时,MySQL 可能会进行隐式类型转换,导致索引失效。解决方法是确保查询条件中的列类型与索引列的类型完全匹配。

  8. 字符串列使用前缀索引:如果字符串列使用了前缀索引,MySQL 可能无法使用索引。解决方法是考虑增加索引长度或使用全文索引来支持模糊查询。

  9. 高基数列索引失效:当索引列的基数(不同值的数量)非常高时,MySQL 可能会选择不使用索引。解决方法是评估索引列的基数和查询的选择性,并根据情况调整索引策略。

  10. 更新频繁的表索引失效:当表上的索引需要频繁更新时,索引可能会失效。解决方法是根据具体情况权衡索引的更新成本和查询性能,并选择合适的索引策略。

针对笔者项目中碰到的问题,通过分析发现,是由于第3点【列类型不匹配】导致的。

GoodsSku对象的goodsId定义为String类型,但是在使用MybatisPlus进行代码查询对象时LambdaQueryWrapper构建时,可以接受Long型值,所以导致sql中where语句为goods_id = 99994373,然后,goods_sku表中goods_id字段是varchar类型,由于传入的列值与列类型不匹配,导致索引失效。

如何解决呢?只要在代码中将传入的Long类型值转为String即可,代码如下:

goodsSkuService.list(new LambdaQueryWrapper().eq(GoodsSku::getGoodsId, String.valueOf(goodsId)))

这样子,执行sql如下:

SELECT * FROM qu_goods_sku WHERE goods_id = '99994373';

使用explain命令查看执行计划:
在这里插入图片描述
从上图可以看出,修改参数值类型之后,命中了索引idx_goods_id_market_enable_auth_flag,实际执行效率也得到了明显提升。

你可能感兴趣的:(java,Mybatis,mysql,索引失效)