上万条数据, 短时间内连续查询几千次, 是数据库查, 还是内存查更好一点?

2024-04-14 15:42:17 +08:00
 bthulu

客户库存有一万多, 要将这一万多库存按客户要求分散到几百个工位上, 需要针对库存的多个属性进行多次查询, 循环里套循环, 能查上千次甚至更多.

这种情况下, 我是直接数据库查, 还是将数据一次全拉到内存, 在内存里查更好?

数据库查可以走索引, 内存里可就没索引查一次就是全量遍历一次了. 如果有内存里支持索引的列表就好了. 语言是.net8.

别说什么优化查询方案一次查询搞定的了, 这不可能. 现实业务就是各个工位之间关系也是错综复杂, 只能这样查了.

10491 次点击
所在节点    数据库
76 条回复
22092
2024-04-15 11:45:38 +08:00
你提到是 mysql 做定时任务, 可以直接 Stored Procedure + Scheduled Jobs 做, 准时高效(mysql,sqllite 可以啟用内存模式)
想自由更改而用 NET8 的 可以将数据一次全拉到内存(用 ReadOnlyMemory) 然后用 SimdLinq 查询
zealotpuppy
2024-04-15 13:44:36 +08:00
一万条数据,qps 不到 1 万,这你随便用什么东西都能搞定的事情,你确定瓶颈是在查询数据本身吗?
jstony
2024-04-15 13:47:24 +08:00
才几万条数据还折腾啥,直接一股脑读到内存里,爱咋查咋查。
imokkkk
2024-04-15 13:47:50 +08:00
全查出来内存里再搞事情吧 内存里操作和 IO 效率不是一个量级的
SmiteChow
2024-04-15 15:17:22 +08:00
这么点数据啊,全返回给前端,爱怎么用怎么用。
duluosheng
2024-04-15 15:17:47 +08:00
就这点数据,缓存+优化索引就行。
Y25tIGxpdmlk
2024-04-15 15:32:58 +08:00
纠结遍历干啥,这么点东西,直接内存不就完了,遍历有咋滴,你心疼内存还是心疼电费啊?
JKeita
2024-04-15 15:58:10 +08:00
几万条一次性全查出来筛选就行了。
freewind
2024-04-15 16:32:03 +08:00
用 Dict<int, List<T>>保存, 先用 width 取 List,再用 Linq where 查一下
cat1879
2024-04-16 09:22:25 +08:00
几万条应该没有什么压力吧,直接数据库查呀,你一边减库存还得一边加库存吧。做好锁表动作就好,问题不大
bthulu
2024-04-16 09:33:19 +08:00
@cat1879 查一遍是没压力, 这是要瞬间根据不同条件查几千遍.
815979670
2024-04-16 10:23:00 +08:00
@wanguorui123 甚至用 SQLite 的内存模式 不落盘 也可以
wxf666
2024-04-16 12:06:40 +08:00
@bthulu 楼上这么多人给出的方案,你给点反馈呀?

1. 全读出来,存数组里,直接遍历找
2. 全读出来,存哈希表里,精确查找
3. 全读出来,排序后存数组里,可范围查找
4. 全读出来,丢到 Redis 里,再查
5. 全读出来,每个丢到一个文件里,根据文件名查
6. 用 MySQL 内存表
7. 用 SQLite 内存表
8. 用 MySQL 存储过程写逻辑


个人认为,从速度上说,内存里用哈希表/ B 树/排序后数组二分查找,

> SQLite 内存表(这个每秒只能几万次)> MySQL 存储过程(页面缓存还是有些慢)>数组遍历( 99%无用功)

>丢到 Redis 查(几千次网络开销)> MySQL 内存表(几千次网络开销)>存几万个文件再查(几千次文件系统开销)
bthulu
2024-04-16 12:46:46 +08:00
@wxf666 我目前采用的方案: 直接拉到内存里, 按 id 生成字典. 然后对 2 个区分度相对高的必查字段排序后生成了 1 个 List<Index>.

```
public struct Index(int Width, GsmId[] GsmIds);
public struct GsmId(int Gsm, int Id);
```

每次查询时, 先二分法查找 Width, 再对结果集遍历, 在 GsmIds 中二分法查找 Gsm, 得出满足 Width 和 Gsm 的 id 结果集, 再从字典中取回数据本体生成新的结果集.

这时候的结果集就已经只有几十条了, 其余字段查找, 直接在这个结果集中遍历
bthulu
2024-04-16 12:53:50 +08:00
针对 Width 的二分查找, 在找到符合范围的数据后, 继续往上 100 条一跳查找是否满足条件, 不满足则往上 10 条一跳查找, 还不满足就往上一条条查找, 找到起始点. 同样往下找到结束点.
针对 Gsm 的二分查找, 因为每个 Width 对应的 Gsm 不会有太多, 二分查找到符合条件的数据后, 就不做 100 条一跳了, 直接 10 条一跳转 1 条一跳.
22092
2024-04-18 11:51:42 +08:00
怪,用到 NET8 又要效率为甚么还用 List<T>,而不用 ReadOnlyMemory<T>,Span<T>等内存优化合集
生成字典似乎已经遍历一次,又要生成 List<T>占内存,LINQ 性能有应该不会这么差吧.(为甚么不用呢)
主要是生成的字典用一個查询就失效了,字典的重用时优勢
而且直接遍历一次,又有必查字段,可以做个迭代器 EX 在途中找到符合条件的数据再 yield 出去处理..
最后都拉到内存了,用 ConcurrentDictionary , SimdLinq 以及 Parallel 作个多线程查询更好
如果这些属性可组织起來,可以看下抽象语法树,表达式树有没有帮助

i7-12700 16GB 256G
一万多库存, 定时任务, 30 秒左右执行一次, 执行时间尽量控制在几百毫秒内, 完成几千次查询(每次查询需要针对库存的多个属性进行多次查询)
从这些情况看,不是大家的方案不好,而是你认为机器太差不合适
有独立机器,不是必要在 VM 内的 Docker 跑程序已经很好了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1032362

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX