V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
6581
V2EX  ›  Go 编程语言

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

  •  
  •   6581 · 2023-12-18 18:56:33 +08:00 · 2380 次点击
    这是一个创建于 374 天前的主题,其中的信息可能已经有所发展或是发生改变。
    1. 项目 A 需要使用项目 B 提供的配置文件
    2. 项目 B 提供的配置文件是以 json 字符串格式存在,保存在 redis
    3. redis 中的配置文件是会变化的
    4. A 项目需要频繁使用配置文件,就需要不断做 json.Unmarshal()。性能很差
    5. 如果把 json.Unmarshal() 之后的 object 保存在内存中,减少 json.Unmarhsal() 的操作。不同 goroutine 拿到的 object 就是浅拷贝的,并发不安全。

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

    第 1 条附言  ·  2023-12-19 09:32:15 +08:00
    1. 数据是存成 hash 结构的了,因此也不是过大导致的,就是次数很多。
    2. 通过 pprof 查看,确定是 json.Unmarshal() 占用资源很多。
    3. 会在内存中缓存一份字符串配置,5 分钟过期,但是在使用时还需要做 unmarshal(),因此缓存与否都需要做 json unmarshal()
    4. 如果内存存储 object 。按道理大家都不会写这个配置对象,没有问题。主要是怕大家写,就导致并发读写,而且也发现代码中确实存在写的情况。 不知道如何不让大家写,
    5. 具体的使用是:接收到请求,去内存中拿字符串,拿不到就到 redis 中拿,然后 unmarshal()
    6. sonic 库看上去是不错的方案,我们使用 arm64 机器,看着是支持的。
    22 条回复    2023-12-25 17:51:38 +08:00
    nulIptr
        1
    nulIptr  
       2023-12-18 19:02:31 +08:00   ❤️ 3
    性能很差是多差? cpu 满了吗?

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

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

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

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

    其实核心问题是:
    每个请求来的时候,需要使用配置,使用配置时是否使用同一个对象
    如果使用同一个对象,如何保证没有开发去写这个对象。(比如 config 中有 map ,如何不让开发去写,在代码层面一定程度上控制就行)
    如果不使用同一个对象,如何更节省资源地深拷贝一个对象。
    paceewang1
        14
    paceewang1  
       2023-12-19 10:11:58 +08:00
    @6581 redis 里的信息有并发问题就加锁呗,不是说你从一个大 string 换成 hash 才出现的问题,即使是使用大 string ,你 unmarshal 后修改,再写进去也有并发问题啊;
    另一个方面,json unmarshal 慢是因为用到了反射,如果你事先知道 struct 的具体结构的话,其实用 easyjson 应该是最快的,但是有额外代码生成,op 可以了解一下 easyjson
    CloveAndCurrant
        15
    CloveAndCurrant  
       2023-12-19 10:13:28 +08:00
    要不试试 fastjson: https://github.com/valyala/fastjson
    这个不转化为 object ,直接操作 JSON 字符串,占用资源和内存更小,性能也更高。
    MoYi123
        16
    MoYi123  
       2023-12-19 11:27:59 +08:00
    写 go 的应该会背 sync map 的八股文吧,
    为什么有并发的情况下不抄这个的实现,
    而要是每次都 Unmarshal json?
    jones2000
        17
    jones2000  
       2023-12-19 12:52:05 +08:00
    外面封装下,修改内容的时候才做一份拷贝, 如果是读取直接引用就可以。
    8355
        18
    8355  
       2023-12-19 21:05:37 +08:00
    我就说一个问题吧,配置只要加一个版本号,单独通过 redis 读 versionKey 就可以确保 config 是否修改,是否需要进行一次刷新,你配置不会天天改,5 分钟才检查一次,如果有修改才运行一次你说的慢操作,这有多大开销?
    8355
        19
    8355  
       2023-12-19 21:06:51 +08:00
    如果大量业务依赖这个配置本身,需要尽可能的保证实时性,直接上队列消费不是很好嘛。
    kkbblzq
        20
    kkbblzq  
       2023-12-19 21:55:30 +08:00
    不嫌麻烦其实可以自己在配置的结构体上写个 Copy 方法的,硬编码的 Copy 连反射都不用就没性能问题;
    dyllen
        21
    dyllen  
       2023-12-20 15:57:14 +08:00
    一个配置,竟然会有人往里写?人的问题很大?
    smartdoc647
        22
    smartdoc647  
       2023-12-25 17:51:38 +08:00   ❤️ 1
    go 官方这个原生 json 库性能确实不太高。普通业务没什么问题,高并发场景官方库性能真不行,比如做日志实时消费不停处理 json 这种,我们压测性能确实一般,不要迷性官方库一定就性能好。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1092 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 36ms · UTC 19:00 · PVG 03:00 · LAX 11:00 · JFK 14:00
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.