今天的分享是再批 json, 去年分享过因为 mysql json 导致的故障,今天的 case 其实是去年的姊妹篇,原理一模一样。上一篇弱智的 MySQL NULL, 居然有小伙伴留言说,在业务中依赖 NULL 使联合索引不唯一的特性,比如有的用户就要多条记录,有的仅一条。我看了差点一口老血喷出来,把业务逻辑耦合在 DB 中这样真的合适嘛? 要是外包另当别论,正常项目谁接手谁倒霉。
今天的分享是再批 json, 去年分享过因为 mysql json 导致的故障,今天的 case 其实是去年的姊妹篇,原理一模一样。有两个原因不建议用 json:
Table Schema 就是强一致的,约束开发不要乱搞,json 这种弱约束的就是开后门,时间一长 json 字段就成了下水道
MySQL JSON 很垃圾,5.7 系列都有性能问题,测试 8.0 好很多。强烈建义大家,使用前压测一下
上面提到的两点有争议?有争议就对了,一致认同是垃圾的东西谁会讨论它呢?
JSON 有两种表示方法:文本可读的在 mysql 中对应 json_dom.cc, binary 二进制表示的对应 json_binary.cc
IfthevalueisaJSONobject,itsbinaryrepresentationwillhaveaheaderthatcontains:-themembercount-thesizeofthebinaryvaluebytes-alistofpointerstoeachkey-alistofpointerstoeachvalueTheactualkeysandvalueswillcomeaftertheheader,thesameorderastheheader.Similarly,thevalueisaJSONarray,thebinaryrepresentationwillhaveaheaderWITH-theelementcount-thesizeofthebinaryvaluebytes-alistofpointerstoeachvalue
源码中注释也写的比较清楚,二进制分成两部分 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 是 ok 的,没有性能问题,查看提交的 commit, 2016 年就有人发现并 fix 了,不知道有没有 back port 到 mysql 5.7 那几个版本
commita2f9ea422e4bdfd65da6dd0c497dc233629ec52eAuthor:KnutAndersHatlenDate:FriApr112:56:232016+0200BugIfaJSONvalueconsistsofalargesub-documentiswrappedmanylevelsofJSONarraysorobjects,serializationoftheJSONvaluemaytakeaverylongtimetocomplete.ThisiscausedbyhowtheserializationSwitchesbetweenthesmallstorageFORmat(usedbydocumentsthatneedlessthan64KB)andthelargestorageformat.Whenitdetectsthatthelargestorageformathastobeused,itredoestheserializationofthecurrentsub-documentusingthelargeformat.Butthisre-serializationhastoberedOneagainwhentheparentofthesub-documentisswitchedFromsmallformattolargeformat.Fordeeplynesteddocuments,theinnerpartsendupgettingre-serializingagainandagain.Thispatchchangeshowtheswitchbetweentheformatsis.Insteadofstartingwithre-serializingtheinnerparts,itnowstartswiththeouterparts.Ifasub-documentexceedsthemaximumsizethesmallformat,weknowthattheparentdocumentwillexceeditandneedtobere-serializedtoo.Re-serializinganinnerdocumentisthereforeawasteoftimewehaven
看Ajax书之前需要熟悉XML吗?·····
就目前而言,使用AJAX时数据传输有两种格式。 一种是XML,另一种则是JSON。
Redis有哪些数据结构?
Redis有五种结构:1、String可以是字符串,整数或者浮点数,对整个字符串或者字符串中的一部分执行操作,对整个整数或者浮点执行自增(increment)或者自减(decrement)操作。 字符串命令:①get、获取存储在指定键中的值②set、设置存储在指定键中的值③del、删除存储在指定键中的值(这个命令可以用于所有的类型)2、list一个链表,链表上的每个节点都包含了一个字符串,虫链表的两端推入或者弹出元素,根据偏移量对链表进行修剪(trim),读取单个或者多个元素,根据值查找或者移除元素。 列表命令:①rpush、将给定值推入列表的右端②lrange、获取列表在指定范围上的所有值③lindex、获取列表在指定范围上的单个元素④lpop、从列表的左端弹出一个值,并返回被弹出的值3、set包含字符串的无序收集器(unordered collection)、并且被包含的每个字符串都是独一无二的。 添加,获取,移除单个元素,检查一个元素是否存在于集合中,计算交集,并集,差集,从集合里面随机获取元素。 集合命令:①sadd、将给定元素添加到集合②smembers、返回集合包含的所有元素③sismember、检查指定元素是否存在于集合中④srem、检查指定元素是否存在于集合中,那么移除这个元素4、hash包含键值对无序散列表,添加,获取,移除当键值对,获取所有键值对。 散列命令:①hset、在散列里面关联起指定的键值对②hget、获取指定散列键的值③hgetall、获取散列包含的所有键值对④hdel、如果给定键存在于散列里面,那么移除这个键5、zset字符串成员(member)与浮点数分值(score)之间的有序映射,元素的排列顺序由分值的大小决定。 添加,获取,删除单个元素,根据分值范围(range)或者成员来获取元素。 有序集合命令:①zadd、将一个带有给定分值的成员添加到有序集合里面②zrange、根据元素在有序排列中所处的位置,从有序集合里面获取多个元素③zrangebyscore、获取有序集合在给定分值范围内的所有元素④zrem、如果指定成员存在于有序集合中,那么移除这个成员
java实体类为什么要实现serlializable接口
因为对象要序列化对象的序列化其实是持久化的一种技术.那么什么叫序列化呢?就是在文件系统(FileSystem)上存储对象的一种方法.序列化后的对象就想C盘里面的一个文件,打开以后是一堆16进制的数字..它可以被java虚拟机识别并在内存中构建一个对象.同时,序列化后的对象可以通过网络传输,等等,就不再单纯是内存中的一块.而是可以自由传输与移动的数据块.那什么样的对象可以序列化呢? 没错,就是实现了Serializable接口的对象.下面讲点儿题外话:说到对象持久化,比如你有一个java bean,里面存储了一个姓名,一个身份证号,你需要持久化,有哪几种方法呢?首先我们能想到的是存在数据库里..在数据库里建立一个对应的表.有name和id两个field,每次需要持久化的时候,存储到数据库里就可以了..这是一种方法..还有一种方法,比如存储成json字符串,然后存档到文件系统上.比如构建一个类似于{name: , id: XXXXX}的字符串第三种方法就是上面说的这种序列化技术.. json字符串的技术的缺点有1.保密性不强,一眼就看懂了.不论磁盘存储或者网络传输也好,一眼就被看穿了..2.效率感人..需要json框架去识别,虽然如果对象层次不是很深或者不是很大,速度也还可以接受,但是相比序列化来说,还是太慢了...但序列化也有缺点..比如人类没法看懂..还有网页程序主要用js做位前端语言..没法序列化..以上就是一点序列化的姿势...2017年8月31日 13:15:14
发表评论