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

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

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

2027 次点击
所在节点    Go 编程语言
22 条回复
nulIptr
146 天前
性能很差是多差? cpu 满了吗?

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

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

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

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

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

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

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

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

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

© 2021 V2EX