› MySQL 5.5 Community Server
› MySQL 5.6 Community Server
› Percona Configuration Wizard
› XtraBackup 搭建主从复制
Great Sites on MySQL
› Percona
› MySQL Performance Blog
› Severalnines
推荐管理工具
› Sequel Pro
› phpMyAdmin
推荐书目
› MySQL Cookbook
MySQL 相关项目
› MariaDB
› Drizzle
参考文档
› http://mysql-python.sourceforge.net/MySQLdb.html
phony2r
V2EX  ›  MySQL

MySQL 如何保存有顺序的列表?

  •  
  •   phony2r · May 24, 2021 · 5492 views
    This topic created in 1952 days ago, the information mentioned may be changed or developed.

    有一个表表示一个列表的每一小项 id/name/order, 其中 order 表示小项在列表中的顺序

    现在这个列表是可以拖动进行排序的, 这样的话每次排序都会产生大量的 order 变化, 比如 1 移到 4, 就会产生 2->1 / 3->2 / 4->3 / 1->4 一共 4 个 order 变化

    类似的, 加入 1 移到 100 的话就会产生 100 个 order 变化

    如果批量更新的话, MySQL 的性能是个问题, 而且也无法保证原子性(部分更新 order 成功部分失败)

    像这种带顺序的列表应该怎么设计?

    23 replies  •  2021-05-25 16:15:04 +08:00
    WaldenHorizon
        1
    WaldenHorizon  
       May 24, 2021
    我会选择全量更新,一个 varchar 字段保存整个列表的表头,用 json 数组或逗号隔开
    renmu123
        2
    renmu123  
       May 24, 2021 via Android
    设计 order 为 0,10000,20000 如果有变化,就设置 order 为前后两个 order 的一半
    yungo8
        3
    yungo8  
       May 24, 2021 via Android
    放一个事务里没有你说的原子性问题。

    你从 1 拖到 100 拖到很长了,大量数据要求拖拽排序的业务场景很难想象是什么,像信息流之类的不如给个权重设置。

    有请后面大佬给出方案
    phony2r
        4
    phony2r  
    OP
       May 24, 2021
    @wangritian 没理解
    phony2r
        5
    phony2r  
    OP
       May 24, 2021
    @renmu123 这样设计的话更新次数有限制 达到一定次数就失效了
    phony2r
        6
    phony2r  
    OP
       May 24, 2021
    @yeqizhang 比如歌单里面音乐的顺序
    SorcererXW
        7
    SorcererXW  
       May 24, 2021   ❤️ 3
    可以用联合索引( order + last_order_timestamp )排序

    1->4 的话, 就把 1 的 order 字段更新为 4, last_order_timestamp 设置为当前时间戳
    排序的时候优先按 order 升序, 再按 last_order_timestamp 降序排

    这样每次更新只需要更新一行
    renmu123
        8
    renmu123  
       May 24, 2021 via Android
    @phony2r 为什么会失效?小数点也可以比大小啊
    timethinker
        9
    timethinker  
       May 24, 2021
    取决于是读多还是写多,另外跟数据量也有关系,假如说按照最坏的情况,修改序列会导致上 W 条的记录被修改,那么单独维护一张表存储序列是一个不错的方案,里面的内容是一个字符串,用分隔符组装 ID 列表,这样在变更序列的时候仅需要修改这一条数据(一对多,这里序列表 ID 就是主表的 ID,比如歌单 ID,评论文章 ID,小说 ID 等等)。

    不过在读取的时候可能会遇到条件过滤+分页的问题,还是取决于场景,可以在更新序列表后异步的更新表数据( order ),此时数据表上的 order 字段只是一个冗余,在修改过后可能会遇到延迟的情况,在关键的地方还是要从序列表当中取出正确的排序,不过大多数情况下是可以接受的。
    yungo8
        10
    yungo8  
       May 24, 2021 via Android
    我想了下,觉得全量没毛病的,其实全量也是更新一个区间的数据,具体多少就是拖拽了多少个位置,性能上问题也不是很大,限制一下一次最多能拖多长就行
    lujjjh
        11
    lujjjh  
       May 24, 2021
    https://www.zhihu.com/question/55789722/answer/146650607

    teambition 是类似 #2 的做法,但是有极小概率会 fallback 到 slow path (整个分组任务重算)。
    jorneyr
        12
    jorneyr  
       May 24, 2021
    我们是用浮点数表示顺序 (初始赋值为创建时候的时间戳),2 个浮点数求平均值就可以了。
    est
        13
    est  
       May 24, 2021
    > 现在这个列表是可以拖动进行排序的, 这样的话每次排序都会产生大量的 order 变化, 比如 1 移到 4, 就会产生 2->1 / 3->2 / 4->3 / 1->4 一共 4 个 order 变化


    保存为 float 。23333

    1 2 3 4 把 1 移动到 4,改成
    2 3 4 4.1
    keenkiller
        14
    keenkiller  
       May 24, 2021   ❤️ 1
    如果列表很小的话:设 order 是一个 bigint 字段,前端传入 front 节点,令拖动节点 order=front+1,并 update 所有 order>front 的 order=order+1

    如果列表比较大:设 order 是一个 decimal 字段,插入时两个间隔大一点,拖动后取 order=(front+after)/2 。日取其半,万世不竭(才怪,会达到精度上限)
    yungo8
        15
    yungo8  
       May 24, 2021 via Android
    @est 太大了,拖不了几次,得是 4.0000001🐶
    NULL2020
        16
    NULL2020  
       May 24, 2021
    改用 redis list,前端每次更新都全量发过来,后端直接保存就行了
    neoblackcap
        17
    neoblackcap  
       May 24, 2021
    实际上这需求我觉得传统的关系型数据库不好搞。你用一个内存数据库作为缓存层,定时将结果全量同步回数据库就可以了。
    no1xsyzy
        18
    no1xsyzy  
       May 24, 2021   ❤️ 1
    @SorcererXW 时间这个想法很好,但想到一个问题(以下表示为 元素=(order, time))
    a=(1,0) b=(2,0) c=(3,0) d=(4,0)
    d 移到 ab 之间
    a=(1,0) d=(2,1) b=(2,0) c=(3,0)
    然后尝试 c 移到 db 之间,那样就无法正确找到这个间隔了。
    love
        19
    love  
       May 24, 2021   ❤️ 1
    有一个方法很好用,就是另建一个表保存列表顺序,就一个 varchar 字段,里面是那个有序列表的 id 列表,用逗号分隔
    这样只要保存一条记录就可
    akira
        20
    akira  
       May 25, 2021
    对连续的数据进行位置调整,要变动最小的话,那可以考虑链表呀。

    例如 表结构改为 id/name/pre-id ,这样你每次调整排序,最多只需要改 2 个数了。
    cwang22
        21
    cwang22  
       May 25, 2021
    数据可以试试 Jira 排序 ticket 的做法 LexoRank

    排序 key 的格式大概是`2|i019qh`,更新读取都是 O(1),后台任务定期 rebalance


    https://tmcalm.nl/blog/lexorank-jira-ranking-system-explained/
    crclz
        22
    crclz  
       May 25, 2021
    使用 mysql 的 json 类型。
    CEBBCAT
        23
    CEBBCAT  
       May 25, 2021
    @crclz #22 可以仔细讲讲吗?没听懂
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   2529 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 78ms · UTC 09:57 · PVG 17:57 · LAX 02:57 · JFK 05:57
    ♥ Do have faith in what you're doing.