同样的语句mysql执行很快,tidb下执行很慢

SELECT
  count,
  `is_gift`,
  `gift`
FROM
  `product_order`
WHERE
  (
    `type` IN ('待提货', '待评价', '已评价')
    AND `pay_time` BETWEEN 1607432400
    AND 1607518799
    AND `is_pre_sale` = 0
    AND `community_id` = 15657
  )
  OR (
    `pre_sale_end_time` BETWEEN 1607432400
    AND 1607518799
    AND `is_pre_sale` = 1
    AND `type` IN ('待提货', '待评价', '已评价')
    AND `community_id` = 15657
  );

tidb下执行计划



mysql执行计划

1、同样的 sql 语句在 mysql 和 tidb 的执行计划差异较大,通过上的截图可以看到 mysql 中选择的是 index,key 是 community_id,而 tidb 则是 table full scan。鉴于执行计划的差别建议在 tidb 中也尝试创建合适的索引来提高访问效率。如果表中已有索引,但是优化器未命中索引,则建议检查下该表的统计信息健康度。

2、tidb 执行计划解读相关文档链接如下:
https://docs.pingcap.com/zh/tidb/stable/explain-overview

3、统计信息相关文档链接如下:
https://docs.pingcap.com/zh/tidb/stable/statistics

排查了一下,发现因为有enum类型字段并且有or的时候会导致很慢,有什么办法可以优化吗

  1. 当前使用的 tidb 版本是什么?

  2. 方便提供下目标表 product_order 完整的表结构吗?

TiDB 版本:5.7.25-TiDB-v4.0.0 TiDB Server

这个 sql 请改成下面的方式执行下看下执行计划是怎样的吧:

SELECT
  count,
  `is_gift`,
  `gift`
FROM
  `product_order`
WHERE
    `type` IN ('待提货', '待评价', '已评价')
    AND `pay_time` BETWEEN 1607432400 AND 1607518799
    AND `community_id` = 15657
    AND (`is_pre_sale` = 0 OR `is_pre_sale`=1)

另外,方便贴下建表的 DDL 语句吗?

CREATE TABLE `product_order` (
  `order_id` int(11) NOT NULL AUTO_INCREMENT,
  `order_num` varchar(255) DEFAULT '' COMMENT '订单号',
  `transaction_id` varchar(255) DEFAULT '' COMMENT '微信生成的订单号',
  `out_trade_no` varchar(255) DEFAULT '' COMMENT '商户订单号',
  `out_refund_no` varchar(255) DEFAULT '' COMMENT '退款单号',
  `customer_id` int(11) DEFAULT NULL COMMENT '用户ID',
  `product_id` int(11) DEFAULT NULL COMMENT '商品ID',
  `partner_id` int(11) DEFAULT NULL COMMENT '合伙人ID',
  `community_id` int(11) DEFAULT '0' COMMENT '社区ID',
  `price` float(10,2) DEFAULT '0.00' COMMENT '商品单价',
  `money` float(10,2) DEFAULT '0.00' COMMENT '总金额',
  `count` int(11) DEFAULT NULL COMMENT '商品数量',
  `type` enum('未支付','已退款','已评价','待评价','待提货','待退款') DEFAULT '未支付' COMMENT '订单状态',
  `create_time` int(11) DEFAULT NULL COMMENT '订单生成时间',
  `pay_time` int(11) DEFAULT NULL COMMENT '支付时间',
  `end_time` int(11) DEFAULT NULL COMMENT '提货时间',
  `year` varchar(255) DEFAULT '' COMMENT '年份',
  `mouth` varchar(255) DEFAULT '' COMMENT '月份',
  `sku` float DEFAULT '0.15',
  `remark` varchar(128) DEFAULT NULL COMMENT '订单备注',
  `cancel_reason` varchar(255) DEFAULT NULL,
  `cancel_status` enum('审核中','已拒绝','已通过','售后已通过','') DEFAULT '',
  `cancel_time` int(10) DEFAULT NULL,
  `cancel_audit_time` int(10) DEFAULT NULL,
  `cancel_audit_reason` varchar(255) DEFAULT NULL,
  `is_quit` tinyint(1) DEFAULT '0',
  `is_refund` tinyint(1) DEFAULT '0' COMMENT '是否退货不退款',
  `gift` json DEFAULT NULL COMMENT '赠品内容',
  `is_gift` tinyint(1) DEFAULT '0' COMMENT '是否赠品',
  `is_pre_sale` tinyint(1) DEFAULT '0' COMMENT '是否预售商品 0否  1是',
  `pre_sale_end_time` int(11) DEFAULT NULL COMMENT '预售商品截止时间',
  `chain_sku_1_2` decimal(4,3) DEFAULT '0.030' COMMENT '连锁收益比例 一级  执行',
  `chain_sku_1_3` decimal(4,3) DEFAULT '0.030' COMMENT '连锁收益比例 一级  资深',
  `chain_sku_1_4` decimal(4,3) DEFAULT '0.030' COMMENT '连锁收益比例 一级  钻石',
  `chain_sku_2_2` decimal(4,3) DEFAULT '0.000' COMMENT '连锁收益比例 二级  执行',
  `chain_sku_2_3` decimal(4,3) DEFAULT '0.020' COMMENT '连锁收益比例 二级  资深',
  `chain_sku_2_4` decimal(4,3) DEFAULT '0.030' COMMENT '连锁收益比例 二级  钻石',
  `partner_type` tinyint(1) DEFAULT '1' COMMENT '合伙人类型 1:T+3. 2:T+1',
  `cancel_explain` text COMMENT '退单说明',
  `cancel_num` varchar(255) DEFAULT NULL COMMENT '退单编号',
  `targetOrderId` varchar(255) DEFAULT NULL COMMENT '第三方支付订单(回调获取,用于退款)',
  `product_shelves_id` int(11) DEFAULT NULL COMMENT '关联的商品上架信息id',
  `warehouse_id` int(11) DEFAULT NULL COMMENT '仓库id',
  `name` varchar(255) DEFAULT NULL COMMENT '商品名称',
  `is_ums` tinyint(1) DEFAULT '0' COMMENT '银联订单复查支付 0否。1是',
  `after_sale_user_type` tinyint(1) DEFAULT '0' COMMENT '提交售后用户,1小程序,2团长,默认0',
  `after_sale_status` tinyint(1) DEFAULT '0' COMMENT '默认0售后状态0未提交1售后中2已售后3已拒绝',
  `is_crowd_relay` tinyint(1) DEFAULT '0' COMMENT '是否群接龙订单 0否 1是。2是,换小区',
  `change_partner_id` int(11) DEFAULT NULL COMMENT '群接龙原团长id',
  `relay_id` int(11) DEFAULT '0' COMMENT '群接龙id',
  PRIMARY KEY (`order_id`),
  KEY `order_num` (`order_num`(191)),
  KEY `out_trade_no` (`out_trade_no`(191)),
  KEY `partner_id` (`partner_id`),
  KEY `customer_id` (`customer_id`),
  KEY `type` (`type`),
  KEY `community_id` (`community_id`),
  KEY `product_id` (`product_id`),
  KEY `pay_time_type` (`pay_time`,`type`) USING BTREE,
  KEY `customer_id_type` (`customer_id`,`type`) USING BTREE,
  KEY `shelves_id` (`product_shelves_id`) USING BTREE,
  KEY `warehouse_id` (`warehouse_id`) USING BTREE,
  KEY `type_create` (`create_time`,`type`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=14704321 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='订单表';

TiDB 在处理 ( ) or ( ) 这样的查询条件非预期的现象,请将原 sql 中的 enum 列去掉,辛苦再看下执行计划,sql 如下:

SELECT
  count,
  `is_gift`,
  `gift`
FROM
  `product_order`
WHERE
  (
    AND `pay_time` BETWEEN 1607432400
    AND 1607518799
    AND `is_pre_sale` = 0
    AND `community_id` = 15657
  )
  OR (
    `pre_sale_end_time` BETWEEN 1607432400
    AND 1607518799
    AND `is_pre_sale` = 1
    AND `community_id` = 15657
  );


SELECT
  count,
  `is_gift`,
  `gift`
FROM
  `product_order`
WHERE
    `type` IN ('待提货', '待评价', '已评价')
    AND `pay_time` BETWEEN 1607432400 AND 1607518799
    AND `community_id` = 15657
    AND (`is_pre_sale` = 0 OR `is_pre_sale`=1)

sql 改写成上面的方式,是否满足业务需求?

看起来是 TiDB 的 enum 类型在 () or () 这样的查询方式中,存在非预期的现象。关于这个问题,请继续关注该帖,如有新消息,会及时更新,感谢反馈 ~

这样确实可以,期待改进!:muscle:

:handshake::handshake::handshake:

这个问题,后续请关注下面的链接:

https://github.com/pingcap/tidb/issues/14558