【问题标题】:Creating Balance between up- and downvotes is slow and does not work as expected在赞成票和反对票之间建立平衡很慢并且不能按预期工作
【发布时间】:2021-11-08 02:03:57
【问题描述】:

我有一张为文章投票的表格。

CREATE TABLE `articlevote` (
  `id` int NOT NULL AUTO_INCREMENT,
  `articleid` int NOT NULL,
  `ipaddress` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  `vote` int NOT NULL,
  PRIMARY KEY (`id`,`articleid`,`ipaddress`),
  UNIQUE KEY `UNIQUEVOTE` (`articleid`,`ipaddress`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=149235 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

对于这个最小的例子,我插入 3 个文章投票

insert into articlevote (id,articleid, ipaddress, vote) values (1,1,"1.2.3.4",1),(2,2,"1.2.3.4",1),(3,1,"1.2.3.5",-1);

列投票:1 = 赞成,-1 = 反对
ipaddress 应该防止双重投票。

我有一个查询要给我点赞最多的文章

SELECT v.articleid, count(v.vote) as votes FROM articlevote v 
        WHERE v.vote > 0 
        GROUP BY v.articleid 
        ORDER BY votes DESC;

但现在我需要获得一份考虑赞成票和反对票的最受欢迎文章的列表。

我试过了

SELECT v.articleid, sum(v.vote) as votes FROM articlevote v 
        GROUP BY v.articleid 
        ORDER BY votes DESC;

我希望第 2 条以 1 票位居榜首,第 1 条以 0 票位居第二。 但我得到了第 1 条和第 1 条的总票数。 在大型数据集上,使用 sum 聚合的查询比使用 count 聚合的查询慢得多。

如何在高性能查询中同时获得预期结果?

【问题讨论】:

  • 您的代码运行良好:dbfiddle.uk/…
  • 您使用的是什么MySQL 版本?
  • 我使用的是 MySQL 8.0.23。我刚刚更新到 MySQL 8.0.26

标签: mysql sql count sum


【解决方案1】:

使用 mysql 8 的最新版本,您没有这个问题,也许是一个错误,用户界面已经修复

CREATE TABLE `articlevote` (
  `id` int NOT NULL AUTO_INCREMENT,
  `articleid` int NOT NULL,
  `ipaddress` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  `vote` int NOT NULL,
  PRIMARY KEY (`id`,`articleid`,`ipaddress`),
  UNIQUE KEY `UNIQUEVOTE` (`articleid`,`ipaddress`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=149235 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
insert into articlevote (id,articleid, ipaddress, vote) values (1,1,"1.2.3.4",1)
,(2,2,"1.2.3.4",1)
,(3,1,"1.2.3.5",-1);
SELECT v.articleid, sum(v.vote) as votes FROM articlevote v 
        GROUP BY v.articleid 
        ORDER BY votes DESC;
文章ID |选票 --------: | ----: 2 | 1 1 | 0

db小提琴here

【讨论】:

  • 我将 Windows 上的 MYSQL 从 8.0.23 更新到 8.0.26,现在我得到了正确的结果。谢谢你。但是问题的性能方面并没有解决。给你一个想法。 “计数查询”的执行时间为 407 毫秒。 “求和查询”的执行时间为 2.797 秒。两者都在一个有 123'257 行的表上执行。这意味着“计数查询”几乎比“总和查询”快 7 倍。
  • 在 article_id 和投票上创建一个索引,如果有帮助,请点赞
  • 你就是男人!它的工作原理甚至比“计数查询”还要快。
【解决方案2】:
  • 摆脱id;它似乎完全没用。此外,它还会影响性能。

  • UNIQUE升级为PRIMARY

  • 不要在 +1 或 -1 的值上浪费 4 个字节;而不是INT 使用 TINYINT(1 字节)。

  • 大型 ISP 中的用户共享 IP 地址 -- 两个用户可能拥有相同的 IP(在不同的时间),或者一个不经常使用的用户可能获得不同的 IP。

  • 您正在处理 IPv4,但不是 IPv6。

  • COUNT(vote) 检查vote 在总数中包含“1”或排除它之前是否为 NOT NULL。它没有看到vote 的标志。

      CREATE TABLE `articlevote` (
        `articleid` int NOT NULL,
        `ipaddress` varchar(15) CHARACTER SET ascii NOT NULL,
        `vote` TINYINT NOT NULL,
        PRIMARY KEY (`articleid`,`ipaddress`) USING BTREE
      ) ENGINE=InnoDB;
    

【讨论】:

  • 谢谢。你提出了一些非常好的观点。我将实施其中的大部分。 IP 地址检查是我能想到的最接近的防止重复投票的方法。它并不完美,也不必如此。它主要防止同一会话中的用户多次赞成或反对。
猜你喜欢
  • 2012-12-15
  • 1970-01-01
  • 1970-01-01
  • 2018-06-07
  • 1970-01-01
  • 2021-06-19
  • 1970-01-01
  • 2013-10-24
  • 2018-07-08
相关资源
最近更新 更多