V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
lithium148
V2EX  ›  问与答

减少查询次数的骚操作

  •  
  •   lithium148 · 2020-09-08 10:42:45 +08:00 · 2278 次点击
    这是一个创建于 1535 天前的主题,其中的信息可能已经有所发展或是发生改变。

    假设我有一个记录,用来管理某元件开关的状态,{id:1;开关:ture;其他记录:etc…}。 所有开关的初始状态均为开,随机 X 分钟后变为关状态( X 由用户设定,可以读取到),关闭后永远不会开启。 现在我把 10000 个这样的记录存在数据库的一张表里。我的程序需要每分钟遍历 1 次所有的记录,找出开关是开的。问题是即使只有最后 10 个开关是开启的,我也需要遍历 10000 条记录。当数据量增大之后,每分钟检测一次会成为严重的负担。 有没有减轻负担的方法? ————————————————————————————————————————————————————————————————— 因为关闭的时间是随机的,有可能后面的开关已经关闭了,前面的开关还开启着,所以不能用检测到 y 个开关均为关闭就停止检测;另外已经关闭的开关也不能删除,因为用户还要查看记录。 ————————————————————————————————————————————————————————————————— 我一开始想到的方法是,单独维护一张“开启表”,存储开启开关的 id,形如[1,2,3],所有开关初始默认放到“开启表”,程序只检测“开启表”中 id 的开启状态,程序检测到关闭后再删除。 问题是这样检测的时候,还是要遍历所有的记录,看他们的 id 是否在开启表中,并没有降低负担。 你有更好的方法吗? 非常感谢!

    14 条回复    2020-09-09 16:30:17 +08:00
    a570295535
        1
    a570295535  
       2020-09-08 11:09:07 +08:00 via Android
    开关的话,直接储存在用户 Cookie 里就完了,反正一般用户也不会闲的蛋疼清理 Cookie,

    如果真的挺需要记住用户配置的话,在用户修改的时候 Cookie 记一次线上同步一次,时间记录直接用 Cookie 的数据就行,不用遍历线上。

    只在用户修改的时候或第一次登陆的时候线上同步一次用户配置就行。
    dzdh
        2
    dzdh  
       2020-09-08 11:17:26 +08:00   ❤️ 2
    bitmap ?
    opengps
        3
    opengps  
       2020-09-08 11:26:26 +08:00
    你需要借用内存表来减少硬盘 io,每次写入历史记录之前更新下这个位于内存里的最新状态表
    imn1
        4
    imn1  
       2020-09-08 11:37:12 +08:00
    每分钟检索一次的意义是什么?
    我觉得应该改业务流程(逻辑)
    lithium148
        5
    lithium148  
    OP
       2020-09-08 11:37:21 +08:00
    我的另一个思路是,将默认表分为“开启表”和“关闭表”,内容形式均为{id:1;开关:ture;其他记录:etc…}。所有开关初始均在“开启表”中,程序只遍历“开启表”。当检测到开关已关闭后,复制对应的记录到“关闭表,并在“开启表”中删除。“关闭表”中存放的是冷数据,不会被程序遍历。
    这样会额外带来复制的负担,但是比为了 10 条数据遍历 100000 条数据来得聪明一些。
    lithium148
        6
    lithium148  
    OP
       2020-09-08 11:38:08 +08:00
    @imn1 向用户发出通知,X 号开关已被关闭之类的,不能改
    lithium148
        7
    lithium148  
    OP
       2020-09-08 11:39:09 +08:00
    @a570295535 不是很懂您的意思,能展开讲讲吗
    scnace
        8
    scnace  
       2020-09-08 11:41:22 +08:00 via Android   ❤️ 1
    /t/702117 (感觉是这个场景?
    imn1
        9
    imn1  
       2020-09-08 11:44:03 +08:00
    @lithium148 #6
    你这个应该由事件驱动,在关闭动作发生时,附带驱动发通知的事件
    a570295535
        10
    a570295535  
       2020-09-08 11:45:39 +08:00 via Android
    @lithium148 已经说的很明白了呀,还展个啥开?
    wysnylc
        11
    wysnylc  
       2020-09-08 11:52:43 +08:00
    状态机
    death00
        12
    death00  
       2020-09-08 13:19:23 +08:00
    加一个开关状态变化时间字段,这样每次扫描表,只需要取该字段大于上次扫描时间的记录即可。
    不知道这样的方案是否可以。
    ryd994
        13
    ryd994  
       2020-09-08 16:26:14 +08:00 via Android   ❤️ 1
    “问题是这样检测的时候,还是要遍历所有的记录,看他们的 id 是否在开启表中”
    你的数据库索引呢?如果不是数据库那就自己做个二分
    就算遍历,也只遍历开启表,这就节约很多了啊

    但是其实这样也就和数据库索引做好差不多了,最多是部署的时候有性能 /成本优势。
    全表,以(开启状态,ID )作索引,结果就是第一步直接就把关闭部分排除了。
    分表只不过是把开启状态这一栏排除出来。但是就要考虑一致性问题。
    lithium148
        14
    lithium148  
    OP
       2020-09-09 16:30:17 +08:00
    我来补充下同个问题其他论坛的回复。另外最后采用的方案是索引,之所以没用其他方法是因为我没有底层的操作权限,get()就 get 了没办法控制它是怎么 get 的,没办法说查询到开启开关的总数量就 break,因为没有提供这个接口

    引用自 https://jandan.net/qa/MjAyMDA5MDktMTg=#comments
    开启表用 SET,查询是 O(1)的。
    ;
    只看数据库,开启表已经是结果了,查询肯定没问题,只需要解决源表和结果表的同步问题,如果允许用数据库触发器就用触发器。
    抛开数据库,结果可以本地缓存,数据库在修改时间建索引,只取最近的变化;或者在应用之间用消息队列等。
    ;
    你对表的状态建索引就行了.(建议你先了解下索引原理,数据库的数据存储会按索引位置存放)
    我的思路是再维护一个状态表记录开关状态的各个状态的元件个数,判断为开为 0 时不去查询.你再对元件表的状态建索引.加快搜索速度.
    不过这要看你的数据量级,一般 mysQl 几万条加对索引查询挺快的.
    另一个要考虑并发量级,考虑会不会有修改冲突突.
    还有就是状态表和元件表的一致性问题.
    ;
    假设检验了解一下
    ;
    用视图,每次操作完刷新一次视图,查询的时候从视图中查询。
    ;
    开启表里面越来越多,那不是查询时间越来越短了吗(开启表可以放内存,只查询开启表没有的)

    这东西最好用硬件实现,FPGA 比较理想,不会的话逻辑门电路都可以实现。单片机读出来传到电脑,一万条每秒传一遍都毫无压力,多爽
    ;
    啥数据库? 上的索引即可#index)
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1692 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 16:48 · PVG 00:48 · LAX 08:48 · JFK 11:48
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.