go json.Unmarshal 深拷贝性能太差怎么办? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
6581
V2EX    Go 编程语言

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

  •  
  •   6581 2023 年 12 月 18 日 3031 次点击
    这是一个创建于 780 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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 日
    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 日   3
    性能很差是多差? cpu 满了吗?

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

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

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

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

    其实核心问题是:
    每个请求来的时候,需要使用配置,使用配置时是否使用同一个对象
    如果使用同一个对象,如何保证没有开发去写这个对象。(比如 config 中有 map ,如何不让开发去写,在代码层面一定程度上控制就行)
    如果不使用同一个对象,如何更节省资源地深拷贝一个对象。
    paceewang1
        14
    paceewang1  
       2023 年 12 月 19 日
    @6581 redis 里的信息有并发问题就加锁呗,不是说你从一个大 string 换成 hash 才出现的问题,即使是使用大 string ,你 unmarshal 后修改,再写进去也有并发问题啊;
    另一个方面,json unmarshal 慢是因为用到了反射,如果你事先知道 struct 的具体结构的话,其实用 easyjson 应该是最快的,但是有额外代码生成,op 可以了解一下 easyjson
    CloveAndCurrant
        15
    CloveAndCurrant  
       2023 年 12 月 19 日
    要不试试 fastjson: https://github.com/valyala/fastjson
    这个不转化为 object ,直接操作 JSON 字符串,占用资源和内存更小,性能也更高。
    MoYi123
        16
    MoYi123  
       2023 年 12 月 19 日
    写 go 的应该会背 sync map 的八股文吧,
    为什么有并发的情况下不抄这个的实现,
    而要是每次都 Unmarshal json?
    jones2000
        17
    jones2000  
       2023 年 12 月 19 日
    外面封装下,修改内容的时候才做一份拷贝, 如果是读取直接引用就可以。
    8355
        18
    8355  
       2023 年 12 月 19 日
    我就说一个问题吧,配置只要加一个版本号,单独通过 redis 读 versionKey 就可以确保 config 是否修改,是否需要进行一次刷新,你配置不会天天改,5 分钟才检查一次,如果有修改才运行一次你说的慢操作,这有多大开销?
    8355
        19
    8355  
       2023 年 12 月 19 日
    如果大量业务依赖这个配置本身,需要尽可能的保证实时性,直接上队列消费不是很好嘛。
    kkbblzq
        20
    kkbblzq  
       2023 年 12 月 19 日
    不嫌麻烦其实可以自己在配置的结构体上写个 Copy 方法的,硬编码的 Copy 连反射都不用就没性能问题;
    dyllen
        21
    dyllen  
       2023 年 12 月 20 日
    一个配置,竟然会有人往里写?人的问题很大?
    smartdoc647
        22
    smartdoc647  
       2023 年 12 月 25 日   1
    go 官方这个原生 json 库性能确实不太高。普通业务没什么问题,高并发场景官方库性能真不行,比如做日志实时消费不停处理 json 这种,我们压测性能确实一般,不要迷性官方库一定就性能好。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1946 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 15:56 PVG 23:56 LAX 07:56 JFK 10:56
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86