Mysql 获取用户';s在队列表中的位置太慢

Mysql 获取用户';s在队列表中的位置太慢,mysql,mariadb,Mysql,Mariadb,我有一张MariaDB表,看起来像这样: +--------+--------+--------+---------------------+ | realm | key2 | userId | date | +--------+--------+--------+---------------------+ | AB3 | 123 | 1 | 2017-08-04 17:30:00 | | AB3 | 124 |

我有一张MariaDB表,看起来像这样:

+--------+--------+--------+---------------------+
| realm  |  key2  | userId |        date         |
+--------+--------+--------+---------------------+
|    AB3 |    123 |      1 | 2017-08-04 17:30:00 |
|    AB3 |    124 |      1 | 2017-08-04 17:30:00 |
|    AB3 |    125 |      1 | 2017-08-04 17:30:00 |
|    XY7 |     97 |      2 | 2017-08-04 17:35:00 |
|    XY7 |     98 |      2 | 2017-08-04 17:35:00 |
|    XY7 |     99 |      2 | 2017-08-04 17:35:00 |
|    AB3 |    110 |      3 | 2017-08-04 17:40:00 |
|    AB3 |    111 |      3 | 2017-08-04 17:40:00 |
+--------+--------+--------+---------------------+

PRIMARY_KEY (realm, key2)
INDEX (realm, userId)
INDEX (date)
此表作为某种队列用于处理用户操作。基本上,服务器总是从该表中获取最旧的数据,对其进行处理并将其从该表中删除。每个领域都有自己的服务器处理此队列

现在我想找出用户在该领域队列中的位置。因此,使用上面的示例,当我请求领域'AB3'中的userId 3的位置时,我希望得到结果
2
,因为只有一个其他用户(userId 1)需要在领域AB3之前处理

(在本例中,行
key2
可能与此无关。我之所以包含它,是因为它是主键的一部分,可能与找到一个好的解决方案相关)

以下是SQL模式:

CREATE TABLE `queue` (
  `realm` varchar(5) NOT NULL,
  `key2` int(10) UNSIGNED NOT NULL,
  `userId` int(10) UNSIGNED NOT NULL,
  `date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

INSERT INTO `queue` (`realm`, `key2`, `userId`, `date`) VALUES
('AB3', 110, 3, '2017-08-04 17:40:00'),
('AB3', 111, 3, '2017-08-04 17:40:00'),
('AB3', 123, 1, '2017-08-04 17:30:00'),
('AB3', 124, 1, '2017-08-04 17:30:00'),
('AB3', 125, 1, '2017-08-04 17:30:00'),
('XY7', 97, 2, '2017-08-04 17:35:00'),
('XY7', 98, 2, '2017-08-04 17:35:00'),
('XY7', 99, 2, '2017-08-04 17:35:00');

ALTER TABLE `queue`
  ADD PRIMARY KEY (`realm`,`key2`),
  ADD KEY `ru` (`realm`,`userId`) USING BTREE,
  ADD KEY `date` (`date`);
我提出了这个查询,它看起来很有效,但对于一个有10000000个条目的表来说速度非常慢(约3秒):

SELECT (COUNT(DISTINCT `realm`, `userId`)+1) `position`
FROM `queue`
WHERE `realm` = 'AB3'
AND `date` < (
  SELECT `date`
  FROM `queue`
  WHERE `realm` = 'AB3' AND `userId` = 3
  GROUP BY `realm`, `userId`
)
您是否知道如何优化此查询以在包含10000000个条目的表上更快地运行


在此表上运行的其他查询:

SELECT `m`.*
FROM `queue` `m`
JOIN (
    SELECT `m`.*
    FROM `queue` `m`
    WHERE `m`.`realm` = ?
    ORDER BY `date` ASC
    LIMIT 1
) `mm` ON `m`.`realm` = `mm`.`realm` AND `m`.`userId` = `mm`.`userId`;


如何优化索引?

我觉得DDL表有问题。无论如何,我会重写您的查询,如下所示:

SELECT (COUNT(DISTINCT `userId`)+1) `position`
FROM `queue`
WHERE `realm` = 'AB3'
    AND `date` < (
        SELECT min(`date`)
        FROM `queue`
        WHERE `realm` = 'AB3' AND `userId` = 3
    )
index (realm, date)
你可以试试sheety索引

index (realm, date, userId)  

但甚至不能肯定它会比前一个更快。

它不是特别慢,但也不是特别准确,除非有一条关于日期的信息你没有告诉我们:-(子查询的最佳索引是
(领域,用户ID,日期)
(用户ID,领域,日期)
。外部查询的最佳索引是
(领域、日期、用户ID)
-但是如果没有
用户ID
,这可能就足够了。我很惊讶给定的索引只需要3秒。请检查仅子查询需要多长时间。@PaulSpiegel仅子查询的运行时间为0.002秒。使用wargre答案中的一个几乎需要相同的时间。看起来只有外部查询需要改进在
(领域,日期)
上添加索引可能会显著加快速度。--我还编辑了此表上经常使用的其他两个查询。鉴于这些查询,您建议此表使用哪些索引?(出于插入原因,我需要在
(领域,键2)
上使用PK或UNIQUE)@user2015253您能告诉我一些数字吗?.distinct realm/server的数量。ditinct userId的平均数量。每天的平均插入数量。@PaulSpiegel distinct realms:10+。distinct userId:100k。每天的插入数:很难估计,可能至少5000万次。值得注意的是,userId仅对某个领域是唯一的,因此userId 1是一个为领域A和领域B统计不同的用户。如果重要的话,服务器有一个强大的CPU,256 GB RAM,但只使用HDD进行存储(因为SSD不够大,无法承受数据库中正在进行的所有写入和删除操作)。不过,I/O现在似乎不是瓶颈。我应该做类似的事情(realm、userId、date)作为主键,并确保日期始终是不同的(每个userId)。(事实上,我撒谎,我会让MessageBroker来做这件事;)我在原始帖子中编辑了我经常使用的其他两个查询。如果我想使用这两个查询以及您答案中的查询,您将如何优化索引?如果您添加了(领域,日期)索引,您的第一个查询将无法再索引。删除操作使用(领域,用户ID)索引,所以没什么可做的了。为什么一个额外的索引会阻止我的第一个查询使用索引?我的意思是,第一个查询的理想索引是(领域,日期)和(领域,用户ID)。如果你有这些索引,你就不能做得更多了
index (realm, date)
index (realm, date, userId)