首页 > 解决方案 > Mysql将记录放在两条记录之间的顺序

问题描述

这是记录,我们希望在 #3 和 #4 之间移动 id #1

id  title   sort
1     a      1
2     b      2
3     c      3
4     d      4
5     e      5
6     f      6

方法一:

得到#3排序号并加1并用它更新#1排序所以我们有

id  title   sort
1     a      4
2     b      2
3     c      3
4     d      4
5     e      5
6     f      6

然后加上 1 到 #4 排序以及之后的任何记录

我们有

id  title   sort
1     a      4
2     b      2
3     c      3
4     d      5
5     e      6
6     f      7

排序后

id  title   sort
2     b      2
3     c      3
1     a      4
4     d      5
6     e      6
6     f      7

它工作正常,但想象我们有 2,000,000 条记录,所有记录都必须更新......

方法二

得到 #3 和 #4 的总和排序并除以 2 => (3+4)/2=3.5 并将其放入 #1 排序

id  title   sort
2     b      2
3     c      3
1     a      3.5
4     d      4
5     e      5
6     f      6

它也可以正常工作,但想象一下这种操作的数千次会产生像 3.99999999999 这样的大浮点数,过了一会儿它就很可怕了

是否有任何 mysql/mariadb 技巧或方法可以做到这一点?

标签: mysqlsqlmariadb

解决方案


您的“将其放在项目之间的一半”方法可能是最好的。

让我们继续吧,BIGINT UNSIGNED因为它在 8 个字节中为您提供 64 位。不太好:DOUBLE会给你 8 个字节的 53 位,以及一些有趣的指数业务。 DECIMAL以更多字节为代价为您提供更多位,同时不会消除对以下代码的需要。

  • 您知道根据用户输入将其放在“之后”的哪一行吗?
  • 使用 发现之后的行ORDER BY ... ASC LIMIT 1
  • 平均两个值;检查 avg 是否等于它们中的任何一个——如果是,那么你的情况很糟糕。

题外话... 2M 行。以 2K、4K、6K 等作为sort值开始(2M*2K = 4G,的限制BIGINT UNSIGNED。)

这表示您可以在任何相邻对之间挤压 2K 个项目。但是,在最坏的情况下,恰好在第一个值之后重复插入,在撞墙之前只有 11 次插入。11 ~= log2(2000)。也就是说,重新排序可能很快,但最多 11 次中的 1 次,这将是昂贵的。

(请不要在 2K 表示 2000 和 2048 之间争辩;这与算法无关。)

那么,当没有空间插入新的排序值时该怎么办?重建数字会锁定表(2M 行)“太长”,所以让我们尽量避免这种情况。

这个怎么样:

抓取前后的 10 行(2 个带有 ORDER BY 和 LIMIT 的 SELECT)。修复这些sort值,使它们均匀分布。

  • 打桌子的开始或结束可能没有问题;它会少于 20 行。并且有一个无声的 0 和 4G-1 的界限。
  • 如果 20 行不够,那就扩大跨度。
  • 在事务中完成所有这些(包括原始的、简单的、中途代码)。
  • FOR UPDATE在 all(?) 上使用,SELECTs以便阻塞其他线程。
  • 检查死锁。如果遇到,完全重新开始。(第二次尝试可能会发现中途尝试工作正常——因为其他一些线程完成了将排序值展开。)

定时:

  • 中途的情况,即使有事务,也可能需要一毫秒左右。
  • 尽管锁定和更新了 20 行,但更复杂的情况不会花费更长的时间。
  • 您可能每秒可以处理 1K 次操作。

推荐阅读