go json.Unmarshal 深拷贝性能太差怎么办?

2023-12-18 18:56:33 +08:00
 6581
  1. 项目 A 需要使用项目 B 提供的配置文件
  2. 项目 B 提供的配置文件是以 json 字符串格式存在,保存在 redis
  3. redis 中的配置文件是会变化的
  4. A 项目需要频繁使用配置文件,就需要不断做 json.Unmarshal()。性能很差
  5. 如果把 json.Unmarshal() 之后的 object 保存在内存中,减少 json.Unmarhsal() 的操作。不同 goroutine 拿到的 object 就是浅拷贝的,并发不安全。

大佬们如何解决这个问题呢?

2381 次点击
所在节点    Go 编程语言
22 条回复
nulIptr
2023-12-18 19:02:31 +08:00
性能很差是多差? cpu 满了吗?

配置更新的频率是多高?必须要每次用都从 redis 中取吗? 5 分钟刷新一次可不可以,发布订阅模式更新可不可以?

每次用都是要整个 json 配置吗,能否拆出高频变化的 key 单独存 redis ?
iyaozhen
2023-12-18 19:06:29 +08:00
具体一点呢 数据呢。json 多大,耗时多久,cpu 占用多少?
ScepterZ
2023-12-18 19:07:01 +08:00
一般是通过人来解决而不是代码,大家约定好配置只读不可以改。
很在乎这个问题的话,如果调用的频率不是特别高,可以直接换 jsoniter/sonic ,对于大部分业务来说性能足够了
iyaozhen
2023-12-18 19:08:38 +08:00
一般来说都是本地 cache 下,过期时间看业务。

如果实在怀疑是 Unmarshal 问题,欢迎使用我厂的 https://github.com/bytedance/sonic
a632079
2023-12-18 19:10:20 +08:00
似乎可以试试 sonic ?不需要你手动做缓存啥的方案了。
https://github.com/bytedance/sonic/blob/main/docs/INTRODUCTION_ZH_CN.md
BeautifulSoap
2023-12-18 19:12:21 +08:00
"如果把 json.Unmarshal() 之后的 object 保存在内存中,减少 json.Unmarhsal() 的操作。不同 goroutine 拿到的 object 就是浅拷贝的,并发不安全。"

你这 json 配置难道解析后还要修改?不修改的话解析完成设置变量的时候上锁写个 singleton 不就好了,怎么会并发不安全
darkengine
2023-12-18 20:57:19 +08:00
如果配置很大, 可以考虑在 Redis 里加个 config_version 字段, 项目 B 在修改 config 的时候先修改 config_version. 项目 A 先比较内存里的 config_version 和 redis 里的 config_version, 不一致再去更新配置.
matrix1010
2023-12-18 21:15:23 +08:00
结构固定直接走代码生成不就行了
iseki
2023-12-18 21:24:22 +08:00
建议代码生成,Java 的 getter 有点过时了,考虑 Record 模式吧😋😁
danbai
2023-12-19 08:27:49 +08:00
Redis 是支持订阅的可以让 a 在更改的时候发布一下
xuanbg
2023-12-19 08:39:51 +08:00
性能有问题肯定是这个 Json 太大了。这么大的数据,就不能用 hash 存,非得 string ?
plutome
2023-12-19 09:28:08 +08:00
完全不能理解 因为频繁使用配置文件, 然后 json.Unmarshal 导致 性能差
你一天是要读几百亿次配置文件么, 不然怎么会有性能差的问题?
6581
2023-12-19 09:37:27 +08:00
@6581 @BeautifulSoap @ScepterZ @a632079 @danbai @darkengine @iseki @iyaozhen @matrix1010 @nulIptr @plutome @xuanbg 感谢大佬回复。具体细节已 append 了。

其实核心问题是:
每个请求来的时候,需要使用配置,使用配置时是否使用同一个对象
如果使用同一个对象,如何保证没有开发去写这个对象。(比如 config 中有 map ,如何不让开发去写,在代码层面一定程度上控制就行)
如果不使用同一个对象,如何更节省资源地深拷贝一个对象。
paceewang1
2023-12-19 10:11:58 +08:00
@6581 redis 里的信息有并发问题就加锁呗,不是说你从一个大 string 换成 hash 才出现的问题,即使是使用大 string ,你 unmarshal 后修改,再写进去也有并发问题啊;
另一个方面,json unmarshal 慢是因为用到了反射,如果你事先知道 struct 的具体结构的话,其实用 easyjson 应该是最快的,但是有额外代码生成,op 可以了解一下 easyjson
CloveAndCurrant
2023-12-19 10:13:28 +08:00
要不试试 fastjson: https://github.com/valyala/fastjson
这个不转化为 object ,直接操作 JSON 字符串,占用资源和内存更小,性能也更高。
MoYi123
2023-12-19 11:27:59 +08:00
写 go 的应该会背 sync map 的八股文吧,
为什么有并发的情况下不抄这个的实现,
而要是每次都 Unmarshal json?
jones2000
2023-12-19 12:52:05 +08:00
外面封装下,修改内容的时候才做一份拷贝, 如果是读取直接引用就可以。
8355
2023-12-19 21:05:37 +08:00
我就说一个问题吧,配置只要加一个版本号,单独通过 redis 读 versionKey 就可以确保 config 是否修改,是否需要进行一次刷新,你配置不会天天改,5 分钟才检查一次,如果有修改才运行一次你说的慢操作,这有多大开销?
8355
2023-12-19 21:06:51 +08:00
如果大量业务依赖这个配置本身,需要尽可能的保证实时性,直接上队列消费不是很好嘛。
kkbblzq
2023-12-19 21:55:30 +08:00
不嫌麻烦其实可以自己在配置的结构体上写个 Copy 方法的,硬编码的 Copy 连反射都不用就没性能问题;

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

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

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

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

© 2021 V2EX