基于Mysql7.0 JSON格式IP抓取数据存储查询设计

需求

抓取网站多样

抓取元素不同

查询条件不固定

Mysql JSON介绍

JSON的格式非常简单:名称/键值。之前MySQL版本里面要实现这样的存储,要么用VARCHAR要么用TEXT大文本。 MySQL5.7发布后,专门设计了JSON数据类型以及关于这种类型的检索以及其他函数解析。 为什么JSON的原生支持

文档合法性

在MySQL5.7.7对JSON提供原生类型的支持之前,用户可以用TEXT或者BLOB类型来存储JSON文档。但对于MySQL来说,用户插入的数据只是序列化后的一个普通的字符串,不会对JSON文档本身的语法合法性做检查,文档的合法性需要用户自己保证。在引入新的JSON类型之后,插入语法错误的JSON文档,MySQL会提示错误,并在插入之后做归一化处理,保证每一个键对应一个值。

更有效的访问

MySQL 5.7.7+本身提供了很多原生的函数以及路径表达式来方便用户访问JSON数据。例如对于下面的JSON文档: { "a": [ [ 3, 2 ], [ { "c" : "d" }, 1 ] ], "b": { "c" : 6 }, "one potato": 7, "b.c" : 8 }

用户可以使用 $.a[1][0]获取{ "c" : "d" }, $.a[1]获取[ { "c" : "d" }, 1 ] 还可以使用通配符 * 和 ** 来进行模糊匹配,详见下一段。

性能优化

在MySQL提供JSON原生支持之前,如果用户需要获取或者修改某个JSON文档的键值,需要把TEXT或者BLOB整个字符串读出来反序列化成JSON对象,然后通过各种库函数访问JSON数据。显然这样是非常没有效率的,特别是对较大的文档。而原生JSON的性能,特别是读性能非常好。根据Oracle公司针对200K+数据文档做的性能测试表明,同样的数据用TEXT和JSON类型的查询性能差异达到两个数量级以上,而且用户还可以对经常访问的JSON键值做索引,进一步提升性能。JSON数据操作性能的提升是基于JSON数据本身的存储结构的,下文会进一步介绍。

JSON的操作接口及路径表达式

JSON的操作接口

JSON_APPEND()JSON_ARRAY_INSERT()JSON_UNQUOTE()JSON_ARRAY()JSON_REPLACE()JSON_CONTAINS()JSON_DEPTH()JSON_EXTRACT()JSON_INSERT()JSON_KEYS()JSON_LENGTH()JSON_VALID()JSON_MERGE()JSON_OBJECT()JSON_QUOTE()JSON_REMOVE()JSON_CONTAINS_PATH()JSON_SEARCH()JSON_SET()JSON_TYPE()

JSON路径表达式

为了更方便快速的访问JSON的键值,MySQL 5.7.7+提供了新的路径表达式语法支持。前文提到的$.a[1][0]就是路径表达式的一个具体的示例。完整的路径表达式语法为:

pathExpression>::= scope  [ ( pathLeg )* ]scope::= [ columnReference ] dollarSigncolumnReference::= [ [ databaseIdentifier period  ] tableIdentifier period ] columnIdentifierdatabaseIdentifier::= sqlIdentifiertableIdentifier::= sqlIdentifiercolumnIdentifier::= sqlIdentifierpathLeg::= member | arrayLocation | doubleAsteriskmember::= period ( keyName | asterisk )arrayLocation::= leftBracket ( non-negative-integer | asterisk ) rightBracketkeyName::= ECMAScript-identifier | double-quoted-string-literaldoubleAsterisk::= **

还是以

{ "a":[ [3,2], [ { "c" :"d"},1] ], "b":{ "c" :6}, "one potato":7, "b.c" :8}

$.a[1] 获取的值为 [ { "c" : "d" }, 1 ] $.b.c 获取的值为 6 $."b.c" 获取的值为 8

JSON的存储结构及具体实现

在处理JSON时,MySQL使用的utf8mb4字符集,utf8mb4是utf8和ascii的超集。由于历史原因,这里utf8并非是我们常说的UTF-8 Unicode变长编码方案,而是MySQL自身定义的utf8编码方案,最长为三个字节。具体区别非本文重点,请大家自行Google了解。

MySQL在内存中是以DOM的形式表示JSON文档,而且在MySQL解析某个具体的路径表达式时,只需要反序列化和解析路径上的对象,而且速度极快。要弄清楚MySQL是如何做到这些的,我们就需要了解JSON在硬盘上的存储结构。有个有趣的点是,JSON对象是BLOB的子类,在其基础上做了特化。

JSON的索引

现在MySQL不支持对JSON列进行索引,官网文档的说明是:

JSON columns cannot be indexed. You can work around this restriction by creating an index on a generated column that extracts a scalar value from the JSON column.

虽然不支持直接在JSON列上建索引,但MySQL规定,可以首先使用路径表达式对JSON文档中的标量值建立虚拟列,然后在虚拟列上建立索引。这样用户可以使用表达式对自己感兴趣的键值建立索引。举个具体的例子来说明:

CREATETABLEfeatures (idINTNOTNULLAUTO_INCREMENT, featureJSONNOTNULL, PRIMARYKEY(id));

插入它的JSON数据的格式为:

{  "type":"Feature",  "properties":{      "TO_ST":"0",      "BLKLOT":"0001001",      "STREET":"UNKNOWN",      "FROM_ST":"0",      "LOT_NUM":"001",      "ST_TYPE":null,      "ODD_EVEN":"E",      "BLOCK_NUM":"0001",      "MAPBLKLOT":"0001001"}}

使用:

ALTERTABLEfeaturesADDfeature_streetVARCHAR(30)AS(JSON_UNQUOTE(feature->"$.properties.STREET"));ALTERTABLEfeaturesADDINDEX(feature_street);

两个步骤,可以对feature列中properties键值下的STREET键(feature->"$.properties.STREET")创建索引。

其中,feature_street列就是新添加的虚拟列。之所以取名虚拟列,是因为与它对应的还有一个存储列(stored column)。它们最大的区别为虚拟列只修改数据库的metadata,并不会存储真实的数据在硬盘上,读取过程也是实时计算的方式;而存储列会把表达式的列存储在硬盘上。两者使用的场景不一样,默认情况下通过表达式生成的列为虚拟列。

这样虚拟列的添加和删除都会非常快,而在虚拟列上建立索引跟传统的建立索引的方式并没有区别,会提高虚拟列读取的性能,减慢整体插入的性能。虚拟列的特性结合JSON的路径表达式,可以方便的为用户提供高效的键值索引功能。

JSON比较与排序

JSON值可以使用=, <, <=, >, >=, <>, !=, <=>等操作符,BETWEEN, IN,GREATEST, LEAST等操作符现在还不支持。JSON值使用的两级排序规则,第一级基于JSON的类型,类型不同的使用每个类型特有的排序规则。

实践

创建表

CREATETABLE`user`(`uid`int(11)NOTNULLAUTO_INCREMENT,`info`jsonDEFAULTNULL, #注意desc字段类型为jsonPRIMARYKEY(`uid`))ENGINE=InnoDBAUTO_INCREMENT=3DEFAULTCHARSET=utf8;

插入数据

INSERT  INTO `user`(`uid`,`info`) VALUES (1,'{\"mail\":\"jiangchengyao@gmail.com\",\"name\":\"David\",\"address\":\"Shangahai\"}'),(2,'{\"mail\":\"amy@gmail.com\",\"name\":\"Amy\"}');

普通查询

selectuid,infofromuserwhereinfolike'%mail%'

key查询

SELECTuid,json_keys(info)as"keys"FROMuser;

value查询

SELECTuid,json_extract(info,'$.mail')AS'mail',json_extract(info,'$.name')AS'name'FROMUSER;

条件查询

json表里面有个content字段数据如下面的Json格式 {"eq":{"f_1360040399":"admin","f_3038116851":"20","f_318208994":"admin@localhost.com"}}

需要对其中的f开头的Json key值所对的value进行模糊查询,方法如下:

select * from table where content->'$.eq.f_1360040399' like '%min%'

SELECTuid,json_extract(info,'$.mail')AS'mail',json_extract(info,'$.name')AS'name'FROMUSERwhereinfo->'$.name'="Amy";

SELECTuid,json_extract(info,'$.mail')AS'mail',json_extract(info,'$.name')AS'name'FROMUSERwhereinfo->'$.name'like"%A%";

性能测试

100万条数据检索

创建随机字符串

select concat('{\"mail\":\"',rand_string(15),'@gmail.com\",\"name\":\"',rand_string(20),'\",\"address\":\"',rand_string(30),'\",\"company\":\"',rand_string(30),'\"}') from dual ;

造100万数据

DELIMITER $$USE `platform`$$DROPPROCEDUREIFEXISTS`autoinsert`$$CREATEPROCEDURE`autoinsert`(INIP_NUM INT)BEGINDECLAREiIPINTDEFAULT0 ;DECLARE json varchar(255);WHILE(iIP < IP_NUM)DOSETjson =concat('{\"mail\": \"',rand_string(15),'@gmail.com\", \"name\":\"',rand_string(20),'\", \"address\": \"',rand_string(30),'\", \"company\": \"',rand_string(30),'\"}');  insertintouser(info)values (json);SETiIP = iIP+1;ENDWHILE;END$$DELIMITER ;call `autoinsert`(1000000);

条件查询

SELECTuid,json_extract(info,'$.mail')AS'mail',json_extract(info,'$.name')AS'name'FROMUSERwhereinfo->'$.name'like"%D%";

创建索引

ALTERTABLEuserADDnameVARCHAR(50)AS(JSON_UNQUOTE(info->"$.name"));ALTERTABLEuserADDINDEX(name);

条件查询(同上)

DurationTime统计表

数据量未加索引添加索引

110万0.005s0.005s

09:58:36SELECT uid,json_extract(info,'$.mail')AS'mail',json_extract(info,'$.name')AS'name'FROM USER where info->'$.name'like"%D%"277070row(s)returned0.005sec /2.416sec10:09:05SELECT uid,json_extract(info,'$.mail')AS'mail',json_extract(info,'$.name')AS'name'FROM USER where info->'$.name'like"%A%"276982row(s)returned0.002sec /1.858sec

结论

本文介绍了Mysql JSON数据类型的特性,并实践测试JSON效率。

Json格式的灵活性满足需求中

抓取网站多样

不同网站对应不同json格式

抓取元素不同

查询条件不固定

查询条件可以先将json_keys(json)返回前端动态生成查询条件

100万数据量添加索引效果不明显 ,大数据量待测试

参考资料

生成 JSON 值的函数

MySQL5.7 JSON实现简介

MySQL 5.7 新特性 JSON 的创建,插入,查询,更新

mysql存储过程中(varchar)变量的问题

mysql concat 字符串拼接命令

关于mysql的Fetch Time 和 Duration Time

mysql 索引( mysql index )

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • Spark SQL, DataFrames and Datasets Guide Overview SQL Dat...
    Joyyx阅读 8,319评论 0 16
  • 我的老家在湖南的一个小山村,,是一个依山傍水的好地方,哪里远离城市的喧嚣,远离雾霾的侵害。我最喜欢每年的夏天,想起...
    纳兰静阅读 285评论 0 1
  • 2. 好尴尬。 冬至同班男生让我帮忙给他们宿舍煮汤圆,本来还以为他们宿舍没人会做饭。原来是有一个厨神的。尴尬了,因...
    去社阅读 221评论 4 0
  • 计算机的力量果然强大,因为最近考试所以在看linux的服务器搭建和应用,虽然自己是linux系统但是怕玩坏装系统麻...
    今天是晴天阅读 296评论 0 0