【发布时间】:2020-10-04 04:01:54
【问题描述】:
我正在尝试在下面的查询中加快选择速度,WHERE IN 中有超过 1000 个项目
表:
CREATE TABLE `user_item` (
`user_id` int(11) unsigned NOT NULL,
`item_id` int(11) unsigned NOT NULL,
PRIMARY KEY (`user_id`,`item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
查询:
SELECT
item_id
FROM
user_item
WHERE
user_id = 2
AND item_id IN(3433456,67584634,587345,...)
IN 列表中有 1000 个项目,执行查询大约需要 3 秒。在这种情况下可以进行任何优化吗?此表中可能有数十亿行。是否可以通过其他数据库或编程方法更快地执行此操作?
更新:
解释的结果如下:
如果我在 IN(...) 语句中有 999 个项目:
+------+-------------+----------+-------+---------------+---------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+----------+-------+---------------+---------+---------+------+------+--------------------------+
| 1 | SIMPLE | user_item | range | PRIMARY | PRIMARY | 8 | NULL | 999 | Using where; Using index |
+------+-------------+----------+-------+---------------+---------+---------+------+------+--------------------------+
如果我在 IN(...) 语句中有 1000 个项目:
+------+--------------+-------------+--------+---------------+---------+---------+--------------------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+--------------+-------------+--------+---------------+---------+---------+--------------------+------+--------------------------+
| 1 | PRIMARY | <subquery2> | ALL | distinct_key | NULL | NULL | NULL | 1000 | |
| 1 | PRIMARY | user_item | eq_ref | PRIMARY | PRIMARY | 8 | const,tvc_0._col_1 | 1 | Using where; Using index |
| 2 | MATERIALIZED | <derived3> | ALL | NULL | NULL | NULL | NULL | 1000 | |
| 3 | DERIVED | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
+------+--------------+-------------+--------+---------------+---------+---------+--------------------+------+--------------------------+
更新 2
我想解释一下为什么我需要在上面做:
我想让用户能够列出按 sort_criteria_1、sort_criteria_2 或 sort_criteria_3 排序的项目,并从列表中排除那些在 user_item 表中由给定 (n) 个用户标记的项目。
这是示例架构:
CREATE TABLE `user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(45) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `item` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`file` varchar(45) NOT NULL,
`sort_criteria_1` int(11) DEFAULT NULL,
`sort_criteria_2` int(11) DEFAULT NULL,
`sort_criteria_3` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_sc1` (`sort_criteria_1`),
KEY `idx_sc2` (`sort_criteria_2`),
KEY `idx_sc3` (`sort_criteria_3`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `user_item` (
`user_id` int(11) NOT NULL,
`item_id` int(11) NOT NULL,
PRIMARY KEY (`user_id`,`item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
以下是我如何获取按 sort_criteria_2 排序的项目,不包括 user_item 表中用户(300、6、1344、24)记录的项目:
SELECT
i.id,
FROM
item i
LEFT JOIN user_item ui1 ON (i.id = ui1.item_id AND ui1.user_id = 300)
LEFT JOIN user_item ui2 ON (i.id = ui2.item_id AND ui2.user_id = 6)
LEFT JOIN user_item ui3 ON (i.id = ui3.item_id AND ui3.user_id = 1344)
LEFT JOIN user_item ui4 ON (i.id = ui4.item_id AND ui4.user_id = 24)
WHERE
ui1.item_id IS NULL
AND ui2.item_id IS NULL
AND ui3.item_id IS NULL
AND ui4.item_id IS NULL
ORDER BY
v.sort_criteria_2
LIMIT
800
上述方法的主要问题是我过滤的用户越多,查询的成本就越高。我希望客户端浏览器支付过滤费用。因此,我会将每个用户的项目列表和匹配的 user_item 记录列表发送到客户端进行过滤。这也有助于分片,因为我不必在同一台机器上拥有 user_item 表或记录集。
【问题讨论】:
-
您的表没有
parent_id列,您是如何创建表的?另外,parent_id有索引吗? -
另外的理论问题。
item_id对您的查询很重要,它过滤掉的项目的百分比是多少?您是否尝试过通过user_id获取所有项目并以编程方式将它们过滤掉? -
@Progman 将 999 项添加的结果解释为 1000 项,因为它们不同。
-
@Jim 每当我听到“某某不是一种选择”时,除非有正当理由,否则我会忽略该声明。我通常会继续忽略。无论如何声明,因为在这个宇宙中,很少有事情是真正“不是一种选择”。那么,究竟为什么“加入不是一种选择”呢?您能否向我们展示一些项目 ID 列表存储位置的架构?
-
如何回答我关于项目 ID 列表来自何处的问题,也显示架构。就上下文而言,我的预感是加入会非常有效;也许以毫秒为单位返回第一行,但我需要查看架构。您能否提供一个有效的连接查询,尽管速度很慢?
标签: mysql mariadb innodb rdbms