无法优化 mySQL 查询
Can't optimise mySQL query
我是 运行 从 MySQL 数据库中检索一些游戏关卡的查询。查询本身需要大约 0.00025 秒才能在包含 40 个级别字符串的基础上执行。我认为这是令人满意的,直到我收到网站主机的消息告诉我优化下面提到的查询,或者脚本将被删除,因为它给他们的服务器带来了很大的压力。
我尝试通过使用 explain 和 explain extended 进行优化并相应地调整列(添加索引),但我总是获得相同的性能。我还注意到 MySQL 没有在可用的地方使用索引,而是进行了完整的 table 扫描。
EXPLAIN EXTENDED 的结果:
table id select_type type possible_keys key key_len ref rows Extra
users 1 SIMPLE ALL PRIMARY,id NULL NULL NULL 7 Using temporary; Using filesort
AllTime 1 SIMPLE ref PRIMARY,userid PRIMARY 4 Test.users.id 1
查询:
SELECT users.nickname, AllTime.userid, AllTime.id, AllTime.levelname, AllTime.levelstr
FROM AllTime
INNER JOIN users
ON AllTime.userid=users.id
ORDER BY AllTime.id DESC
LIMIT ($value_from_php),20;
table:
用户
| id(int) | nickname(varchar) |
| (Primary, Auto_increment) | |
|---------------------------|-------------------|
| 1 | username1 |
| 2 | username2 |
| 3 | username3 |
| ... | ... |
和 AllTime
| id(int) | userid(int) | levelname(varchar) | levelstr(text) |
| (Primary, Auto_increment) | (index) | | |
|---------------------------|-------------|--------------------|----------------|
| 1 | 2 | levelname1 | levelstr1 |
| 2 | 2 | levelname2 | levelstr2 |
| 3 | 3 | levelname3 | levelstr3 |
| 4 | 1 | levelname4 | levelstr4 |
| 5 | 1 | levelname5 | levelstr5 |
| 6 | 1 | levelname6 | levelstr6 |
| 7 | 2 | levelname7 | levelstr7 |
有没有办法优化这个查询,或者我会更好地从 php 调用两个连续的查询来避免警告?
我刚刚学习MySQL,所以请在回复时考虑到这些信息,谢谢:)
看起来查询没有问题。
- 查看应用程序代码。问题很可能出在代码中
- 勾选MySQL query execution plan
- 可能您缺少索引
- 确保将数据缓存在应用程序中 Database(仅供参考,有时您可以将整个数据库加载到应用程序内存中)
- 确保使用连接池
- 创建一个视图(改进的机会很小)
- 尝试删除 "Order By" 子句(再次提高性能的可能性非常小)
The query itself takes around 0.00025 seconds ... I got a message from the website host telling me to optimise the below-mentioned query, or the script will be removed since it is pushing a lot of strain onto their servers.
向网站托管商询问有关此查询为何被标记为需要注意的更多详细信息。一个微不足道的查询不会对任何事情造成压力,除非它被非常频繁地调用。
查明该查询被执行了多少次运行。我敢打赌你的网站正在被机器人攻击并且每分钟被执行数百或数千次。如果是这样,那才是你真正的问题。
我假设您正在使用 InnoDB。
对于 INNER JOIN,MySQL 通常从行数最少的 table 开始,在本例中为 users
。但是,由于您只想将最新的 20 AllTime
条记录与相应的 user
条记录相结合,实际上您应该从 AllTime
开始,因为对于 LIMIT
,它会更小数据集。
使用STRAIGHT_JOIN强制加入顺序:
SELECT users.nickname, AllTime.userid, AllTime.id, AllTime.levelname,
AllTime.levelstr
FROM AllTime
STRAIGHT_JOIN users
ON users.id = AllTime.userid
ORDER BY AllTime.id DESC
LIMIT ($value_from_php),20;
应该可以用AllTime
table上的主键,然后按降序排列。它会抓取同一页面上的所有数据。
还应该使用用户table上的主键来获取id和昵称。如果不止两列,可以在(id, nickname)上加一个多列覆盖索引来提高速度。
如果可以,将 levelstr
列转换为 VARCHAR
以便数据与其余数据存储在同一页上,否则,它必须去获取文本列分别地。这假设您的列在 InnoDB 的 8000 字节行限制之下。没有办法避免 USING TEMPORARY
除非你去掉文本列。
很可能,您的主机已使用慢速查询日志识别此查询,它可以识别所有不使用索引的查询,或者由于 Using temporary
,它们可能已将其标记为红色。
LIMIT ($value_from_php),20;
-- 如果 $value_form_php 很大,则查询很慢。这是因为在到达您需要的 20 页之前,需要扫描所有 'old' 页。
通过"remembering where you left off",您可以使每个页面都同样快。有关详细信息,请参阅此内容:http://mysql.rjweb.org/doc.php/pagination
我是 运行 从 MySQL 数据库中检索一些游戏关卡的查询。查询本身需要大约 0.00025 秒才能在包含 40 个级别字符串的基础上执行。我认为这是令人满意的,直到我收到网站主机的消息告诉我优化下面提到的查询,或者脚本将被删除,因为它给他们的服务器带来了很大的压力。
我尝试通过使用 explain 和 explain extended 进行优化并相应地调整列(添加索引),但我总是获得相同的性能。我还注意到 MySQL 没有在可用的地方使用索引,而是进行了完整的 table 扫描。
EXPLAIN EXTENDED 的结果:
table id select_type type possible_keys key key_len ref rows Extra
users 1 SIMPLE ALL PRIMARY,id NULL NULL NULL 7 Using temporary; Using filesort
AllTime 1 SIMPLE ref PRIMARY,userid PRIMARY 4 Test.users.id 1
查询:
SELECT users.nickname, AllTime.userid, AllTime.id, AllTime.levelname, AllTime.levelstr
FROM AllTime
INNER JOIN users
ON AllTime.userid=users.id
ORDER BY AllTime.id DESC
LIMIT ($value_from_php),20;
table: 用户
| id(int) | nickname(varchar) |
| (Primary, Auto_increment) | |
|---------------------------|-------------------|
| 1 | username1 |
| 2 | username2 |
| 3 | username3 |
| ... | ... |
和 AllTime
| id(int) | userid(int) | levelname(varchar) | levelstr(text) |
| (Primary, Auto_increment) | (index) | | |
|---------------------------|-------------|--------------------|----------------|
| 1 | 2 | levelname1 | levelstr1 |
| 2 | 2 | levelname2 | levelstr2 |
| 3 | 3 | levelname3 | levelstr3 |
| 4 | 1 | levelname4 | levelstr4 |
| 5 | 1 | levelname5 | levelstr5 |
| 6 | 1 | levelname6 | levelstr6 |
| 7 | 2 | levelname7 | levelstr7 |
有没有办法优化这个查询,或者我会更好地从 php 调用两个连续的查询来避免警告?
我刚刚学习MySQL,所以请在回复时考虑到这些信息,谢谢:)
看起来查询没有问题。
- 查看应用程序代码。问题很可能出在代码中
- 勾选MySQL query execution plan
- 可能您缺少索引
- 确保将数据缓存在应用程序中 Database(仅供参考,有时您可以将整个数据库加载到应用程序内存中)
- 确保使用连接池
- 创建一个视图(改进的机会很小)
- 尝试删除 "Order By" 子句(再次提高性能的可能性非常小)
The query itself takes around 0.00025 seconds ... I got a message from the website host telling me to optimise the below-mentioned query, or the script will be removed since it is pushing a lot of strain onto their servers.
向网站托管商询问有关此查询为何被标记为需要注意的更多详细信息。一个微不足道的查询不会对任何事情造成压力,除非它被非常频繁地调用。
查明该查询被执行了多少次运行。我敢打赌你的网站正在被机器人攻击并且每分钟被执行数百或数千次。如果是这样,那才是你真正的问题。
我假设您正在使用 InnoDB。
对于 INNER JOIN,MySQL 通常从行数最少的 table 开始,在本例中为 users
。但是,由于您只想将最新的 20 AllTime
条记录与相应的 user
条记录相结合,实际上您应该从 AllTime
开始,因为对于 LIMIT
,它会更小数据集。
使用STRAIGHT_JOIN强制加入顺序:
SELECT users.nickname, AllTime.userid, AllTime.id, AllTime.levelname,
AllTime.levelstr
FROM AllTime
STRAIGHT_JOIN users
ON users.id = AllTime.userid
ORDER BY AllTime.id DESC
LIMIT ($value_from_php),20;
应该可以用AllTime
table上的主键,然后按降序排列。它会抓取同一页面上的所有数据。
还应该使用用户table上的主键来获取id和昵称。如果不止两列,可以在(id, nickname)上加一个多列覆盖索引来提高速度。
如果可以,将 levelstr
列转换为 VARCHAR
以便数据与其余数据存储在同一页上,否则,它必须去获取文本列分别地。这假设您的列在 InnoDB 的 8000 字节行限制之下。没有办法避免 USING TEMPORARY
除非你去掉文本列。
很可能,您的主机已使用慢速查询日志识别此查询,它可以识别所有不使用索引的查询,或者由于 Using temporary
,它们可能已将其标记为红色。
LIMIT ($value_from_php),20;
-- 如果 $value_form_php 很大,则查询很慢。这是因为在到达您需要的 20 页之前,需要扫描所有 'old' 页。
通过"remembering where you left off",您可以使每个页面都同样快。有关详细信息,请参阅此内容:http://mysql.rjweb.org/doc.php/pagination