我知道解决方案,当您可以按某些唯一索引对表格进行排序
SELECT user_id, external_id, name, metadata, date_created
FROM users
WHERE user_id > 51234123
ORDER BY user_id ASC
LIMIT 10000;
但是对于我来说,我想按某些索引对表进行排序,这些索引具有随机数据
CREATE TABLE `t` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`sorter` bigint(20) NOT NULL,
`data1` varchar(200) NOT NULL,
`data2` varchar(200) NOT NULL,
`data3` varchar(200) NOT NULL,
`data4` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `sorter` (`sorter`),
KEY `id` (`id`,`sorter`),
KEY `sorter_2` (`sorter`,`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
for ($i = 0; $i < 2e6; $i++)
$db->query("INSERT INTO `t` (`sorter`, `data1`, `data2`, `data3`, `data4`) VALUES (rand()*3e17, rand(), rand(), rand(), rand())");
for ($i = 0; $i < 1e6; $i++)
$db->query("INSERT INTO `t` (`sorter`, `data1`, `data2`, `data3`, `data4`) VALUES (0, rand(), rand(), rand(), rand())");
解决方案1:
for ($i = 0; $i < $maxId; $i += $step)
select * from t
where id>=$i
order by sorter
limit $step
select * from t order by sorter limit 512123, 10000;
10000 rows in set (9.22 sec)
select * from t order by sorter limit 512123, 1000;
1000 rows in set (6.25 sec)
+------+-------------+-------+------+---------------+------+---------+------+---------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------+------+---------------+------+---------+------+---------+----------------+
| 1 | SIMPLE | t | ALL | NULL | NULL | NULL | NULL | 3000000 | Using filesort |
+------+-------------+-------+------+---------------+------+---------+------+---------+----------------+
解决方案2:
从t顺序中按排序器限制选择ID 1512123,10000;
+------+-------------+-------+-------+---------------+----------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------+-------+---------------+----------+---------+------+---------+-------------+
| 1 | SIMPLE | t | index | NULL | sorter_2 | 16 | NULL | 1522123 | Using index |
+------+-------------+-------+-------+---------------+----------+---------+------+---------+-------------+
已设置10000行(0.74秒)
0.74听起来不错,但是对于所有表来说,这需要花费0.74 * 3000e3 / 10e3 / 60 = 3分钟以上,并且仅用于收集ID
答案 0 :(得分:0)
使用OFFSET
效率不如您想象的那样。使用LIMIT 1512123, 10000
,必须跨过1512123行。该数字越大,查询运行越慢。
解释EXPLAINs
...
“解决方案1”使用SELECT *
;您没有覆盖指数。因此,可以通过两种方式运行查询:
(这样做):扫描表中的“ ALL”,收集所有列(*
);分类;跳过512123行;并投放10000或1000行。
(一个小的OFFSET
和LIMIT
可能会导致此问题):在INDEX(sorter, id)
的BTree内部,跳过OFFSET
行;抢LIMIT
行;对于索引中的每个抓取行,请使用字节偏移量到达数据文件(注意:您使用的是MyISAM,而不是InnoDB)来查找行;抢*
并将其交付。不需要排序。
不幸的是,优化器没有足够的统计信息,也没有足够的智慧来始终正确地在这两个选择之间进行选择。
“解决方案2”使用“覆盖”索引INDEX(sorter, id)
。 (线索:“使用索引”。)其中包含在查询(sorter
)任意位置找到的 all 列(仅id
和select id from t order by sorter limit 1512123, 10000;
),因此可以(通常通常)优先使用索引而不是扫描表。
另一个涉及where id>=$i
的解决方案。这样可以避免OFFSET
。但是,由于使用的是MyISAM,因此无法将索引和数据一起“聚类”。使用InnoDB,数据根据PRIMARY KEY
进行排序。如果那是id
,则可以通过直接跳入数据中间(在$i
处)开始查询。使用MyISAM,我刚刚描述的内容是在INDEX(id)
的BTree中完成的;但是它仍然必须在该Btree和数据所在的.MYD
文件之间来回跳动。 (这是InnoDB的设计从本质上比MyISAM的效率更高的示例。)
如果您的目标是从表中获得一堆随机行,请阅读我的treatise。总而言之,有更快的方法,但通常都不是“完美的”,尽管通常“足够好”。