|
|
|
|
公众号矩阵

再批 MySQL Json

今天的分享是再批 json, 去年分享过因为 mysql json 导致的故障,今天的 case 其实是去年的姊妹篇,原理一模一样。

作者: 董泽润 来源:董泽润的技术笔记|2021-07-15 07:23

本文转载自微信公众号「董泽润的技术笔记」,作者董泽润。转载本文请联系董泽润的技术笔记公众号。

上一篇弱智的 MySQL NULL, 居然有小伙伴留言说,在业务中依赖 NULL 使联合索引不唯一的特性,比如有的用户就要多条记录,有的仅一条。

我看了差点一口老血喷出来,把业务逻辑耦合在 DB 中这样真的合适嘛? 要是外包另当别论,正常项目谁接手谁倒霉。

讨伐 json

今天的分享是再批 json, 去年分享过因为 mysql json 导致的故障,今天的 case 其实是去年的姊妹篇,原理一模一样。有两个原因不建议用 json:

  1. Table Schema 就是强一致的,约束开发不要乱搞,json 这种弱约束的就是开后门,时间一长 json 字段就成了下水道
  2. MySQL JSON 很垃圾,5.7 系列都有性能问题,测试 8.0 好很多。强烈建义大家,使用前压测一下

上面提到的两点有争议?有争议就对了,一致认同是垃圾的东西谁会讨论它呢?

实现

JSON 有两种表示方法:文本可读的在 mysql 中对应 json_dom.cc, binary 二进制表示的对应 json_binary.cc

Binary Format

  1. If the value is a JSON object, its binary representation will have a 
  2. header that contains
  3.  
  4. - the member count 
  5. - the size of the binary value in bytes 
  6. - a list of pointers to each key 
  7. - a list of pointers to each value 
  8.  
  9. The actual keys and values will come after the header, in the same 
  10. order as in the header. 
  11.  
  12. Similarly, if the value is a JSON array, the binary representation 
  13. will have a header with 
  14.  
  15. - the element count 
  16. - the size of the binary value in bytes 
  17. - a list of pointers to each value 

源码中注释也写的比较清楚,二进制分成两部分 header + element. 实际上 mysql 只是 server 识别了 json, 各个存储引擎仍存储的二进制 blob

换句话说,底层引擎对 json 是无感知的,就是一条数据而己

json-function-reference[1] 官方有好多在 server 层操作 json 的方法,感兴趣的可以看一下

我们的问题

MySQL Client 读取 json 时是 json_dom 调用 wrapper_to_string 方法,序列化成可读格式数据

写入 json 时,是由 json_binary 调用 serialize_json_value 方法,序列化成上面图表示的 binary 数据,然后由引擎层存储成 blob 格式

去年故障也有服务端的问题:加载单条数据失败主动 panic, 坑人不浅 (理由是数据不一致,宁可不对外提供服务,问题是那条数据恰好是重不重要的一类)。所以这个故事告诉我们: 在线服务的可用性,远高于数据一致性

慢的原因是 wrapper_to_string 遇到 json array 特别多的情况下反复 mem_realloc 创建内存空间,导致性能下降

其实去年没有 fix 完整,最近发现写入也有类似问题,只不过是 serialize_json_value 写入存储引擎前反复 mem_realloc 造成超时。这时前端页面发现写入超时了,(人工)重试继续写入 json 数据

恰好赶上联合索引中有 NULL 字段,由此引出了唯一索引不唯一的现象。那怎么解决呢?前端按钮 cooldown 治标不治本,sql 执行 12s 前端肯定又点击提交了,治本还得升级 mysql 8.0 并且移除 NULL 字段, 那会不会又引入其它问题呢?

项目初期做了错误的决定,后人很容易买单。希望我们踩到的坑,能让你决定使用 json 前犹豫几秒钟 ^^

8.0 fix

在测试机上发现 8.0 是 ok 的,没有性能问题,查看提交的 commit, 2016 年就有人发现并 fix 了,不知道有没有 back port 到 mysql 5.7 那几个版本

  1. commit a2f9ea422e4bdfd65da6dd0c497dc233629ec52e 
  2. Author: Knut Anders Hatlen <knut.hatlen@oracle.com> 
  3. Date:   Fri Apr 1 12:56:23 2016 +0200 
  4.  
  5.     Bug#23031146: INSERTING 64K SIZE RECORDS TAKE TOO MUCH TIME 
  6.  
  7.     If a JSON value consists of a large sub-document which is wrapped in 
  8.     many levels of JSON arrays or objects, serialization of the JSON value 
  9.     may take a very long time to complete. 
  10.  
  11.     This is caused by how the serialization switches between the small 
  12.     storage format (used by documents that need less than 64KB) and the 
  13.     large storage format. When it detects that the large storage format 
  14.     has to be used, it redoes the serialization of the current 
  15.     sub-document using the large format. But this re-serialization has to 
  16.     be redone again when the parent of the sub-document is switched from 
  17.     small format to large format. For deeply nested documents, the inner 
  18.     parts end up getting re-serializing again and again. 
  19.  
  20.     This patch changes how the switch between the formats is done. Instead 
  21.     of starting with re-serializing the inner parts, it now starts with 
  22.     the outer parts. If a sub-document exceeds the maximum size for the 
  23.     small format, we know that the parent document will exceed it and need 
  24.     to be re-serialized too. Re-serializing an inner document is therefore 
  25.     a waste of time if we haven't already expanded its parent. By starting 
  26.     with expanding the outer parts of the JSON document, we avoid the 
  27.     wasted work and speed up the serialization. 

参考资料

[1]json-function-reference: https://dev.mysql.com/doc/refman/5.7/en/json-function-reference.html

【编辑推荐】

  1. 鸿蒙官方战略合作共建——HarmonyOS技术社区
  2. Java代码中,如何监控MySQL的Binlog?
  3. 重学MySQL之一揭开面纱,显露架构
  4. MySQL中的SQL Mode及其作用
  5. MySQL 中的共享表空间与独立表空间,用哪个好呢?
  6. 将数据从MySQL复制到Redshift的四种方式
【责任编辑:武晓燕 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

订阅专栏+更多

带你轻松入门 RabbitMQ

带你轻松入门 RabbitMQ

轻松入门RabbitMQ
共4章 | loong576

21人订阅学习

数据湖与数据仓库的分析实践攻略

数据湖与数据仓库的分析实践攻略

助力现代化数据管理:数据湖与数据仓库的分析实践攻略
共3章 | 创世达人

12人订阅学习

云原生架构实践

云原生架构实践

新技术引领移动互联网进入急速赛道
共3章 | KaliArch

41人订阅学习

视频课程+更多

Python系列视频教程

Python系列视频教程

讲师:sunnyDLL2121人学习过

Oracle数据库工程师入门培训实战教程(从Oracle11g 到 Oracle19c)

Oracle数据库工程师入门培训实战教程(从Orac

讲师:风哥197903人学习过

OGG(GoldenGate)管理与实战

OGG(GoldenGate)管理与实战

讲师:白丁1283人学习过

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊

51CTO服务号

51CTO官微