5

我的桌子

Field   Type    Null    Key Default Extra
id      int(11)     NO  PRI NULL    auto_increment
userid  int(11)     NO  MUL NULL     
title   varchar(50) YES     NULL     
hosting varchar(10) YES     NULL     
zipcode varchar(5)  YES     NULL     
lat     varchar(20) YES     NULL     
long    varchar(20) YES     NULL     
msg    varchar(1000)YES MUL NULL     
time    datetime    NO      NULL     

那是桌子。我模拟了 500k 行数据并随机删除了 270k 行,只留下 230k 行,自动增量为 500k。

这是我的索引

Keyname Type    Unique  Packed  Field   Cardinality Collation   Null
PRIMARY BTREE   Yes No  id            232377    A       
info    BTREE   No  No  userid          2003    A       
lat                                    25819    A   YES
long                                   25819    A   YES
title                                  25819    A   YES
time                                   25819    A   

考虑到这一点,这是我的查询:

SELECT * FROM postsWHERE long>-118.13902802886 AND long<-118.08130797114 AND lat>33.79987197114 AND lat<33.85759202886 ORDER BY id ASC LIMIT 0, 25

显示第 0 - 15 行(共 16 行,查询耗时 1.5655 秒)[id: 32846 - 540342]

该查询只给我带来了 1 页,但因为它必须搜索所有 230k 条记录,所以仍然需要 1.5 秒。

这是解释的查询:

id  select_type table   type    possible_keys   key     key_len ref rows    Extra
1   SIMPLE      posts   index   NULL            PRIMARY 4       NULL 25     Using where

因此,即使我使用 where 子句只返回 16 个结果,我仍然会得到一个缓慢的查询。

现在例如,如果我进行更广泛的搜索:

SELECT * FROM `posts` WHERE `long`>-118.2544681443 AND `long`<-117.9658678557 AND `lat`>33.6844318557 AND `lat`<33.9730321443 ORDER BY id ASC LIMIT 0, 25

显示第 0 - 24 行(共 25 行,查询耗时 0.0849 秒)[id: 691 - 29818]

从 20 页中检索第一页时速度要快得多,总共找到 483 页,但我限制为 25 页。

但如果我要最后一页

SELECT * FROM `posts` WHERE `long`>-118.2544681443 AND `long`<-117.9658678557 AND `lat`>33.6844318557 AND `lat`<33.9730321443 ORDER BY id ASC LIMIT 475, 25

显示第 0 - 7 行(共 8 行,查询耗时 1.5874 秒)[id: 553198 - 559593]

我得到一个缓慢的查询。

我的问题是如何实现良好的分页?当网站上线时,我预计当它起飞时,每天都会有数百条帖子被删除和发布。帖子应按 id 或时间戳排序,并且 Id 不连续,因为某些记录将被删除。我想要一个标准的分页

1 2 3 4 5 6 7 8 ... [Last Page]
4

7 回答 7

2

使用WHERE子句从出现在较早页面上的结果记录中过滤:然后您不需要指定偏移量,只需指定行数。例如,跟踪查看的最后一个 id 或时间戳,并仅过滤那些 id 或时间戳大于该值的记录。

于 2012-08-28T09:57:50.197 回答
0

不幸的是,mysql 在输出 30 个结果之前必须读取所有 20000 行。如果您可以尝试使用 WHERE 子句中的索引列过滤来缩小搜索范围。

于 2012-08-28T10:00:30.663 回答
0

几句话。

鉴于 you order by id,这意味着在每个页面上,您都有第一条和最后一条记录的 id,因此您应该使用 where id > $last_id limit 20 而不是限制 200000,这将非常快。

缺点显然是,如果 id 不是连续的(在两者之间删除),则您不能提供“最后”页面或中间的任何页面。然后,您可以使用最后一个已知 id 和 offset + limit 组合的组合。

显然,拥有适当的索引也将有助于排序和限制。

于 2012-08-28T10:01:14.510 回答
0

看起来你只有一个主键索引。您可能希望在您使用的字段上定义索引,例如:

create index idx_posts_id on posts (`id` ASC);
create index idx_posts_id_timestamp on posts (`id` ASC, `timestamp` ASC);

除了主唯一键索引之外,在您的键字段上有一个常规索引通常有助于加快 mysql 的速度。

于 2012-08-28T10:03:46.307 回答
0

Mysql 在较大的偏移量下损失了相当多的性能:来自mysqlPerformance 博客

当心大的 LIMIT 如果您需要前几行,则使用索引进行排序是有效的,即使发生了一些额外的过滤,因此您需要按索引扫描更多行,然后由 LIMIT 请求。但是,如果您正在处理具有大偏移效率的 LIMIT 查询,则会受到影响。LIMIT 1000,10 可能比 LIMIT 0,10 慢。确实,大多数用户的搜索结果不会超过 10 页,但搜索引擎机器人可能会这样做。我已经看到机器人在我的项目中查看 200 多页。此外,对于许多未能解决此问题的网站来说,这为发起 DOS 攻击提供了非常简单的任务——从少数连接中请求具有大量数量的页面,这就足够了。如果您不执行任何其他操作,请确保阻止页码过大的请求。

在某些情况下,例如,如果结果是静态的,那么预先计算结果可能是有意义的,这样您就可以查询它们的位置。因此,不是使用 LIMIT 1000,10 进行查询,而是在 1000 和 1009 之间的 WHERE 位置,这对于任何位置都具有相同的效率(只要它被索引)

于 2012-08-28T10:03:59.297 回答
0

如果您使用自动增量,您可以使用:

SELECT * FROM帖子 WHEREid >= 200000 ORDER BYidDESC LIMIT 200000 , 30

这样 mysql 将不得不只遍历 200000 以上的行。

于 2012-08-28T10:08:14.037 回答
0

我想到了。让我慢下来的是订购。因为我会调用一个限制,并且我要求走的越远,它必须排序的越多。所以然后我通过添加一个子查询来修复它,首先使用WERE 子句提取我想要的数据,然后我使用ORDER BYLIMIT

SELECT * FROM 
    (SELECT * from `posts` as `p` 
        WHERE 
           `p`.`long`>-119.2544681443 
           AND `p`.`long`<-117.9658678557 
           AND `p`.`lat`>32.6844318557 A
           ND `p`.`lat`<34.9730321443  

    ) as posttable 
    order by id desc 
    limit x,n

通过这样做,我实现了以下目标:

id  select_type     table        type   possible_keys   key key_len ref     rows    Extra
1   PRIMARY         <derived2>   ALL    NULL            NULL NULL   NULL    3031    Using filesort
2   DERIVED         p            ALL    NULL            NULL NULL   NULL    232377  Using where

现在,我使用“where”过滤 232k 结果,并且仅使用 orderby 和限制 3031 个结果。

Showing rows 0 - 3030 (3,031 total, Query took 0.1431 sec)

于 2012-08-28T23:56:22.473 回答