json格式-JSON数据结构的优缺点


揭秘JSON的挑战

今日我们要深入探讨的是关于JSON的讨论。曾因MySQL中的JSON功能引发的问题,再次引发了我们的关注。实际上,这个案例与之前的姊妹篇有着相同的原理。

这里有两个原因让我们不建议频繁使用JSON格式:

  1. 强一致的Table Schema为我们提供了明确的约束,使得开发更加规范。而JSON这种弱约束则可能导致数据结构的不规范,随着时间的推移,原本应该只是临时使用的JSON字段可能会变得杂乱无章,甚至沦为数据中的“下水道”。
  2. MySQL的JSON支持存在性能问题。尽管在测试中8.0版本有所改善,但5.7系列仍存在明显的性能瓶颈。强烈建议大家在使用前进行充分的压力测试。

对于上述两点,或许你会有疑问。存在争议是好事,因为只有被大众认同的优质内容才会引起讨论。我们不必对那些被广泛认为存在问题的内容进行过多的探讨。

让我们深入到技术的核心。

JSON有两种表现形式:在MySQL中,可读的文本对应于json_dom.cc,而二进制的表示则对应于json_binary.cc。源码中的注释也解释得相当清楚,二进制部分被分为header和element两部分。实际上,MySQL服务器虽然能够识别json格式,但各个存储引擎仍然存储的是二进制blob。

换句话说,对于底层引擎来说,json是无感知的数据,只是一条普通的数据记录。

官方文档json-function-reference[1]提供了许多在服务器层操作json的方法,有兴趣的读者可以深入探究。

我们的经验与教训

当MySQL Client读取json时,会调用json_dom的wrapper_to_string方法,将json数据序列化为可读格式。而写入json时,则是由json_binary调用serialize_json_value方法,将数据序列化为上述图示中的binary数据,然后由引擎层以blob格式存储。

去年我们遭遇的服务端故障也与数据相关:处理单条数据失败导致主动触发异常(panic),这让人深感坑人(原因是数据不一致,宁愿不提供服务也要坚持原则)。这个案例告诉我们:在线服务的可用性远比数据一致性来得重要。如果那条关键数据出了问题,后果不堪设想。

性能问题往往出现在wrapper_to_string方法上,当处理json array中元素特别多时,该方反复进行mem_realloc以创建内存空间,从而导致性能下降。

事实上,去年我们并没有完全解决这个问题。最近我们发现写入过程中也存在类似问题,即serialize_json_value在写入存储引擎前会反复进行mem_realloc操作导致超时。此时前端页面会显示写入超时,并尝试进行(人工)重试以继续写入json数据。

这种情况又恰逢联合索引中存在NULL字段,导致出现了唯一索引不唯一的现象。那么如何解决呢?仅仅依靠前端按钮的冷却时间(cooldown)只能治标不治本。要从根本上解决问题,我们需要升级到mysql 8.0并移除NULL字段。但是这样是否又会引入其他问题呢?

对未来的展望与建议

在项目初期做出错误的选择可能会让后人容易为此买单。希望我们踩过的坑能让你在决定使用JSON之前犹豫几秒钟。

关于8.0版本的改进

我们在测试机上发现8.0版本表现良好,没有明显的性能问题。通过查看提交的commit记录,我们发现早在2016年就有人发现了这个问题并进行了修复。我们不知道这个修复是否已经回溯到mysql 5.7的版本中。

参考资料