2016-01-09 361 views
0

我有一个聊天应用程序。我有一个API,它返回用户说话的用户列表。但是,当MySQL达到100000行数据时,需要很长时间才能返回列表消息。 这是我的邮件表MysqL大表查询优化

CREATE TABLE IF NOT EXISTS `messages` (
    `_id` int(11) NOT NULL AUTO_INCREMENT, 
    `fromid` int(11) NOT NULL, 
    `toid` int(11) NOT NULL, 
    `message` text NOT NULL, 
    `attachments` text NOT NULL, 
    `status` tinyint(1) NOT NULL DEFAULT '0', 
    `date` datetime NOT NULL, 
    `delete` varchar(50) NOT NULL, 
    `uuid_read` varchar(250) NOT NULL, 
    PRIMARY KEY (`_id`), 
    KEY `fromid` (`fromid`,`toid`,`status`,`delete`,`uuid_read`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=118561 ; 

,这是我的用户表(简体)

CREATE TABLE IF NOT EXISTS `users` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `login` varchar(50) DEFAULT '', 
    `sex` tinyint(1) DEFAULT '0', 
    `status` varchar(255) DEFAULT '', 
    `avatar` varchar(30) DEFAULT '0', 
    `last_active` datetime DEFAULT NULL, 
    `active` tinyint(1) DEFAULT '1', 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=15523 ; 

这里是我的查询(用户ID为1930)

select SQL_CALC_FOUND_ROWS `u_id`, `id`, `login`, `sex`, `birthdate`, `avatar`, `online_status`, SUM(`count`) as `count`, SUM(`nr_count`) as `nr_count`, `date`, `last_mesg` from 
(
(select `m`.`fromid` as `u_id`, `u`.`id`, `u`.`login`, `u`.`sex`, `u`.`birthdate`, `u`.`avatar`, `u`.`last_active` as online_status, COUNT(`m`.`_id`) as `count`, (COUNT(`m`.`_id`)-SUM(`m`.`status`)) as `nr_count`, `tm`.`date` as `date`, `tm`.`message` as `last_mesg` from `messages` as m inner join `messages` as tm on `tm`.`_id`=(select MAX(`_id`) from `messages` as `tmz` where `tmz`.`fromid`=`m`.`fromid`) left join `users` as u on `u`.`id`=`m`.`fromid` where `m`.`toid`=1930 and `m`.`delete` not like '%1930;%' group by `u`.`id`) 
UNION 
(select `m`.toid as `u_id`, `u`.`id`, `u`.`login`, `u`.`sex`, `u`.`birthdate`, `u`.`avatar`, `u`.`last_active` as online_status, COUNT(`m`.`_id`) as `count`, 0 as `nr_count`, `tm`.`date` as `date`, `tm`.`message` as `last_mesg` from `messages` as m inner join `messages` as tm on `tm`.`_id`=(select MAX(`_id`) from `messages` as `tmz` where `tmz`.`toid`=`m`.`toid`) left join `users` as u on `u`.`id`=`m`.`toid` where `m`.`fromid`=1930 and `m`.`delete` not like '%1930;%' group by `u`.`id`) 
order by `date` desc) as `f` group by `u_id` order by `date` desc limit 0,10 

请帮助优化此查询

我需要什么, 谁用户ta lked到(姓名,性别,等等) 是什么(从我或给我)的最后一条消息 计数消息(全部) 计数未读邮件(仅限于我)的

查询效果很好,但需要很长时间。

输出必须是这样

enter image description here

+1

您没有提供'EXPLAIN'输出。每个MySQL相关的问题都有。从我可以看到的 - 你正在做一个'LIKE'查询,在开始和结束时都带有通配符 - 表示一个完整的表扫描(所以它遍历整个表的数据)。没有提及配置,所以我们不知道MySQL是否可以正确使用你的硬件。根据这个问题来看,你正在运行默认配置,在机械驱动器上,没有任何优化,如果你必须执行'LIKE'搜索来获取ID ='1930'的用户数据,那么你完全错误地查询它恐怕。 –

+0

我编辑了输出图片的问题。 我添加“user_id +分号”删除列时,用户删除消息。所以在查询中有''m'.'lelete'不像'%1930;%'“。如此删除的消息不被检索。 –

+1

您使用LIKE运算符引用的删除列的目的是什么?如果这是删除消息的用户的ID,它应该是一个像ID一样的INT,如果它拥有一个ID列表(我猜是基于它是一个VARCHAR的事实),那么你应该创建一个单独的连接表来保存这些ID。 – SeanN

回答

1

你有你的查询和数据库的一些设计问题。

  • 您应该避免使用关键字作为列名,如delete列或count列;
  • 你应该避免选择在group by未声明的列没有聚合函数...虽然MySQL允许这一点,它不是一个标准,你没有什么数据都将被选择的控制;
  • 您的not like构造可能会导致您的查询不良行为,因为'%1930;%'可能匹配11930;和11930不等于1930;
  • 您应该避免like开始和结尾%通配符,这将导致文本处理需要更长的时间;
  • 您应该设计一种更好的方式来表示消息删除,可能是更好的标志和/或另一个表,以保存与该操作相关的任何重要数据;
  • 试试limit你的结果在加入条件之前(用派生表)执行较少的处理;

我试图用我理解它的最好方式来重写你的查询。我已经在消息表中执行了我的查询,其中有200.000行,没有索引,它在0.15秒内执行。但是,当数据量增加时,您应该创建正确的索引以帮助它更好地运行。

SELECT SQL_CALC_FOUND_ROWS 
    u.id, 
    u.login, 
    u.sex, 
    u.birthdate, 
    u.avatar, 
    u.last_active AS online_status, 
    g._count, 
    CASE WHEN m.toid = 1930 
    THEN g.nr_count 
    ELSE 0 
    END AS nr_count, 
    m.`date`, 
    m.message AS last_mesg 
FROM 
(

    SELECT 
    MAX(_id) AS _id, 
    COUNT(*) AS _count, 
    COUNT(*) - SUM(m.status) AS nr_count 
    FROM messages m 
    WHERE 1=1 
    AND m.`delete` NOT LIKE '%1930;%' 
    AND 
    (0=1 
     OR m.fromid = 1930 
     OR m.toid = 1930 
    ) 
    GROUP BY 
    CASE WHEN m.fromid = 1930 
     THEN m.toid 
     ELSE m.fromid 
    END 
    ORDER BY MAX(`date`) DESC 
    LIMIT 0, 10 
) g 
INNER JOIN messages AS m ON 1=1 
    AND m._id = g._id 
LEFT JOIN users AS u ON 0=1 
    OR (m.fromid <> 1930 AND u.id = m.fromid) 
    OR (m.toid <> 1930 AND u.id = m.toid) 
ORDER BY m.`date` DESC 
; 
+0

'LIMIT'对于加快查询速度没有任何作用。这使得它们变得更慢。虽然你的回答很有帮助,但我相信应该删除关于“LIMIT”的重点。 –

+0

@ N.B。如果确实限制了将要加入的数据量不会产生任何改进? –

+0

由于“LIMIT”的工作原理 - 不,不是。如果查询必须非常复杂,那几乎总是意味着有更好的方法。 –