我在系统中发现很长的查询。 MySQL慢日志显示以下内容:
# Time: 2018-07-08T18:47:02.273314Z
# User@Host: server[server] @ localhost [] Id: 1467
# Query_time: 97.251247 Lock_time: 0.000210 Rows_sent: 50 Rows_examined: 41646378
SET timestamp=1531075622;
SELECT n1.full_name AS sender_full_name, s1.email AS sender_email,
e.subject, e.body, e.attach, e.date, e.id, r.status,
n2.full_name AS receiver_full_name, s2.email AS receiver_email,
r.basket,
FROM email_routing r
JOIN email e ON e.id = r.message_id
JOIN people_emails s1 ON s1.id = r.sender_email_id
JOIN people n1 ON n1.id = s1.people_id
JOIN people_emails s2 ON s2.id = r.receiver_email_id
JOIN people n2 ON n2.id = s2.people_id
WHERE r.sender_email_id = 21897 ORDER BY e.date desc LIMIT 0, 50;
EXPLAIN查询不显示全表扫描,并且查询使用索引:
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE s1 NULL const PRIMARY PRIMARY 4 const 1 100.00 Using temporary; Using filesort
1 SIMPLE n1 NULL const PRIMARY,ppl PRIMARY 4 const 1 100.00 NULL
1 SIMPLE n2 NULL index PRIMARY,ppl ppl 771 NULL 1 100.00 Using index
1 SIMPLE s2 NULL index PRIMARY s2 771 NULL 3178 10.00 Using where; Using index; Using join buffer (Block Nested Loop)
1 SIMPLE r NULL ref bk1,bk2,msgid bk1 4 server.s2.id 440 6.60 Using where; Using index
1 SIMPLE e NULL eq_ref PRIMARY PRIMARY 4 server.r.message_id 1 100.00 NULL
这是我对所用表的SHOW CREATE TABLE查询:
CREATE TABLE `email_routing` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`message_id` int(11) NOT NULL,
`sender_email_id` int(11) NOT NULL,
`receiver_email_id` int(11) NOT NULL,
`basket` int(11) NOT NULL,
`status` int(11) NOT NULL,
`popup` int(11) NOT NULL DEFAULT '0',
`tm` int(11) NOT NULL DEFAULT '0',
KEY `id` (`id`),
KEY `bk1` (`receiver_email_id`,`status`,`sender_email_id`,`message_id`,`basket`),
KEY `bk2` (`sender_email_id`,`tm`),
KEY `msgid` (`message_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1055796 DEFAULT CHARSET=utf8
-
CREATE TABLE `email` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`subject` text NOT NULL,
`body` text NOT NULL,
`date` datetime NOT NULL,
`attach` text NOT NULL,
`attach_dir` varchar(255) CHARACTER SET cp1251 DEFAULT NULL,
`attach_subject` varchar(255) DEFAULT NULL,
`attach_content` longtext,
`sphinx_synced` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `Index_2` (`attach_dir`),
KEY `dt` (`date`)
) ENGINE=InnoDB AUTO_INCREMENT=898001 DEFAULT CHARSET=utf8
-
CREATE TABLE `people_emails` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`nick` varchar(255) NOT NULL,
`email` varchar(255) NOT NULL,
`key_name` varchar(255) NOT NULL,
`people_id` int(11) NOT NULL,
`status` int(11) NOT NULL DEFAULT '0',
`activity` int(11) NOT NULL,
`internal_user_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `s2` (`email`,`people_id`)
) ENGINE=InnoDB AUTO_INCREMENT=22146 DEFAULT CHARSET=utf8
-
CREATE TABLE `people` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`fname` varchar(255) CHARACTER SET cp1251 NOT NULL,
`lname` varchar(255) CHARACTER SET cp1251 NOT NULL,
`patronymic` varchar(255) CHARACTER SET cp1251 NOT NULL,
`gender` tinyint(1) NOT NULL,
`full_name` varchar(255) NOT NULL DEFAULT ' ',
`category` int(11) NOT NULL,
`people_type_id` int(255) DEFAULT NULL,
`tags` varchar(255) CHARACTER SET cp1251 NOT NULL,
`job` varchar(255) CHARACTER SET cp1251 NOT NULL,
`post` varchar(255) CHARACTER SET cp1251 NOT NULL,
`profession` varchar(255) CHARACTER SET cp1251 DEFAULT NULL,
`zip` varchar(16) CHARACTER SET cp1251 NOT NULL,
`country` int(11) DEFAULT NULL,
`region` varchar(10) NOT NULL,
`city` varchar(255) CHARACTER SET cp1251 NOT NULL,
`address` varchar(255) CHARACTER SET cp1251 NOT NULL,
`address_date` date DEFAULT NULL,
`last_update_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `ppl` (`id`,`full_name`)
) ENGINE=InnoDB AUTO_INCREMENT=415040 DEFAULT CHARSET=utf8
这是这4个表的SHOW TABLE STATUS
输出:
Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment
email InnoDB 10 Dynamic 753748 12079 9104785408 0 61112320 4194304 898167
email_routing InnoDB 10 Dynamic 900152 61 55132160 0 69419008 6291456 1056033
people InnoDB 10 Dynamic 9538 386 3686400 0 2785280 4194304 415040
people_emails InnoDB 10 Dynamic 3178 752 2392064 0 98304 4194304 22146
MySQL版本5.7.22 Ubuntu 16.04
但是我注意到一件事-如果我将ORDER BY从查询中删除,但离开LIMIT
,则查询几乎立即运行,耗时不超过0.2秒。因此,我开始考虑在没有ORDER BY的情况下运行查询并通过PHP进行排序,但最终似乎变得很复杂,因为使用没有ORDER BY的LIMIT会导致错误的排序范围。
还有什么我可以做以加快或优化该查询的事情吗?
替代我可以通过我的PHP代码进行排序和分页。我将附加的columnt添加到SELECT ..., UNIX_TIMESTAMP(e.date) as ts
中,然后执行以下操作:
<?php
...
$main_query = $server->query($query);
$emails_list = $main_query->fetch_all(MYSQLI_ASSOC);
function cmp($a, $b) {
return strcmp($a['ts'], $b['ts']);
}
$emails_sorted = usort($emails_list, "cmp");
for ($i=$start;$i<$lenght;$i++)
{
$singe_email = $emails_sorted[$i]
// Format the output
}
但是当我这样做的时候
致命错误:允许的内存大小为134217728字节已耗尽
$emails_sorted = usort($emails_list, "cmp");
行的
答案 0 :(得分:1)
如果您的数据很快返回,那么如何包装它……但是实际上要返回多少行而没有限制。也许在...之后,您仍然可以获得更好的性能。
select PQ.*
from ( YourQueryWithoutOrderByAndLimt ) PQ
order by PQ.date desc
LIMIT 0, 50;
答案 1 :(得分:1)
警告,我对MySQL不太熟悉,实际上,我主要是在我(大多数)阅读有关MySQL的知识的基础上投射MSSQL经验。
1)可能的解决方法:可以安全地假设email.id和email.date总是相同的顺序吗?从功能的角度看,随着时间的推移将电子邮件添加到表中并因此具有不断增加的自动编号,这似乎是合乎逻辑的……但是,数据的初始加载是按不同/随机顺序进行的吗?无论如何,如果是library(shiny)
ui<-
fluidPage(
sidebarLayout(position="left",
sidebarPanel("Parameters",width = 4,
radioButtons("Type","Test", choices= list("Test"="p",
"Test"="l")),
actionButton("GO","Open Modual")
),
mainPanel(
plotOutput("Test")
)))
server<- function(input,output){
Credential<-function(Test){
showModal(modalDialog(
title = "Credentials Required",
textInput("Username", "Enter User Name", value = ""),
textInput("Password", "Enter Password:", value = ""),
footer = actionButton("Submit", "Submit"),
modalButton("Cancel"))
)
#Use Assigned Username and Password to go fetch data.
#Note data must be returned, somehow need to pause or somthing here.
}
#Call Function
observeEvent(input$GO,{
data <- Credential("Test")
})
}
shinyApp(server=server,ui=ui)
而不是ORDER BY e.id
会发生什么?
2)在ORDER BY e.date
上添加复合索引(按该顺序!)是否有帮助?
3)如果所有这些都无济于事,请将查询分为两部分可能会对优化器有所帮助。 (您可能需要修复MySQL的语法)
email (id, date)
答案 2 :(得分:1)
我怀疑这是MySQL联接优化器高估了块嵌套循环(BNL)联接的好处的情况。您可以尝试通过以下方法关闭BNL:
set optimizer_switch='block_nested_loop=off';
希望这将提供更好的加入顺序。您也可以尝试:
set optimizer_prune_level = 0;
强制联接优化器探索所有可能的联接顺序。
另一个选择是使用STRAIGHT_JOIN强制执行特定的连接顺序。在这种情况下,查询文本中指定的顺序似乎很好。因此,要强制执行此特定的加入顺序,您可以编写
SELECT STRAIGHT_JOIN ...
请注意,无论您做什么,您都无法期望查询的速度与没有ORDER BY时一样快。只要您需要查找来自特定发件人的最新电子邮件,并且电子邮件表中没有有关发件人的信息,就无法使用索引来避免排序,而无需浏览所有发件人的所有电子邮件。如果您在email_routing表中有关于日期的信息,情况将会有所不同。然后可以使用该表上的索引来避免排序。
答案 3 :(得分:0)
MySQL无法在查询中使用索引排序依据,因为
查询联接了许多表,而ORDER BY中的列不是 全部来自用于检索行的第一个非恒定表。 (这是EXPLAIN输出中的第一个表,没有 const连接类型。)