Mysql从5.7开始,支持原生的json, 也有不少的工程师建议采用mysql来取代文本数据库。下面将介绍json在mysql与国内的nosql文档数据库sequoiaDB的操作方式,存储,以及高可用性架构方面的不同,通过介绍之后,至于在什么情况下该使用seqoiaDB以及什么情况下使用Mysql,基本可以选择正确的方案。
1 操作方式比较
1.1 Mysql数据库操作
创建一个表,只含有json字段。
mysql> CREATE TABLE t1 (jdoc JSON);
Query OK, 0 rows affected (0.20 sec)
1.1.1 Insert操作
mysql> insert into t1 values(JSON_OBJECT('key1', 1, 'key2','abc'));
Query OK, 1 row affected (0.01 sec)
mysql> select * from t1;
+----------------------------+
| jdoc |
+----------------------------+
| {"key1": 1, "key2":"abc"} |
+----------------------------+
1 row in set (0.00 sec)
mysql> set @j=repeat('x',60000);
Query OK, 0 rows affected (0.00 sec)
mysql> insert into t1 values(JSON_OBJECT('key', @j));
Query OK, 1 row affected (4.72 sec)
mysql> insert into t1 values(JSON_OBJECT('key', @j));
Query OK, 1 row affected (1 min 6.79 sec)
(gdb) c
Continuing.
Breakpoint 3, dtuple_convert_big_rec(index=0x7f54e0144d10, upd=0x0, entry=0x7f54e01584d0,n_ext=0x7f55a2bf4708)
at /data/mysql/mysql-5.7.10/storage/innobase/data/data0data.cc:591
591 if (!dict_index_is_clust(index)) {
命中断点dtuple_convert_big_rec, 将json 字段以大字段存储。
当执行insert into t1 values('["key1", 1,"key2", "abc"]'); 这样的操作时,因为字段jdoc的类型是json类型,所以对该列的值会用Json_dom::parse函数进行词法分析,如果是json格式,则通过。
(gdb) c
Continuing.
Breakpoint 2, Json_dom::parse(text=0x7f54e000f948 "[\"key1\", 1, \"key2\",\"abc\"]", length=26, syntaxerr=0x7f55a2bf5f18,offset=0x7f55a2bf5f10,
preserve_neg_zero_int=false)at /data/mysql/mysql-5.7.10/sql/json_dom.cc:865
1.1.2 json的函数
在mysql中,json区分大小,大写的null无法转化为json类型。
mysql> SELECT CAST('NULL' AS JSON);
ERROR 3141 (22032): Invalid JSON text inargument 1 to function cast_as_json: "Invalid value." at position 0in 'NULL'.
mysql> SELECT CAST('null' AS JSON);
+----------------------+
| CAST('null' AS JSON) |
+----------------------+
| null |
mysql> SELECT JSON_MERGE('[1, 2]','["a", "b"]', '[true, false]');
+-----------------------------------------------------+
| JSON_MERGE('[1, 2]', '["a","b"]', '[true, false]') |
+-----------------------------------------------------+
| [1, 2, "a", "b",true, false] |
+-----------------------------------------------------+
1 row in set (7.72 sec)
Breakpoint 2, Json_dom::parse(text=0x7f54e000f938 "[1, 2]", length=6, syntaxerr=0x7f55a2bf5a90,offset=0x7f55a2bf5a88, preserve_neg_zero_int=false)
at /data/mysql/mysql-5.7.10/sql/json_dom.cc:865
865 Rapid_json_handler handler(preserve_neg_zero_int);
(gdb) c
Continuing.
Breakpoint 2, Json_dom::parse(text=0x7f54e000faf0 "[\"a\", \"b\"]", length=10,syntaxerr=0x7f55a2bf5a90, offset=0x7f55a2bf5a88, preserve_neg_zero_int=false)
at /data/mysql/mysql-5.7.10/sql/json_dom.cc:865
865 Rapid_json_handler handler(preserve_neg_zero_int);
(gdb) c
Continuing.
Breakpoint 2, Json_dom::parse(text=0x7f54e000fc88 "[true, false]", length=13,syntaxerr=0x7f55a2bf5a90, offset=0x7f55a2bf5a88, preserve_neg_zero_int=false)
at /data/mysql/mysql-5.7.10/sql/json_dom.cc:865
865 Rapid_json_handler handler(preserve_neg_zero_int);
其他示例操作省略介绍。。。。。。 , 请参考官方文档,json 的基本操作都有。
总结:经过简单跟踪对含有json 字段的表的sql操作获悉,对在mysql中支持json的理解,
可以简单地将json的类型类比其他的数据类型,如int, datetime等,它是mysql支持的一种数据类型,仅仅而已。就算仅仅创建一个只有一个json 类型的字段的表(如图上的示例T1,就只有一个jdoc的json类型的例)。 对于json列的更新(包括插入)操作,会调用Json_dom::parse 进行json类型的值合法性检查,同时,mysql中包含了许多json类型的操作函数(如上示例的操作),对于只包含一个json列的表进行插入操作,其需要执行的函数逻辑跟一个普通表的insert 完全一致, 最终将json字段以大字段类型lob类型存储到innodb 的存储引擎中。
所以从本质上来讲,mysql 5.7 支持json , 实际上是扩展了一个数据类型,通过扩展一些操作函数来支持json的数据类型,该数据类型最终以大字段类型lob的方式存储在innodb 存储引擎中。
从上面的分析得知,mysql支持json ,但依然是在原来sql处理方式,来支持json的数据类型。当表中含有json字段时,我们可以当其时一个blob 字段,但这个列中的每一个数据项,都是一个json的文档对象,支持很多json类型的操作。
正是因为json仅仅是mysql的数据库表中的一个lob的类型字段 ,所以,其性能也跟普通的包含blob字段类型的表无异。当行的长度较短时,支持非常高的读写并发操作,但同样,如果json字段的长度很长,操作时会带来大量的IO操作,QPS自然下降。
2 性能比较
1.1 插入性能
以下是进行的长json类型的比较测试,json的文档大小为32K ,测试结果如下:
1.1.1 SequoiaDB
测试结果:(当集合表中的数据量越大,insert性能越慢),从空表开始,每个测试线程执行一个20000 insert的文件
file_no:1-0 runing_num: 1 total_time: 66 qps: 303 run_script: test_sdb.sh comment:test sdb on phyiscal machine 32k sharding by 6 replication group
file_no:21-1 runing_num: 20 total_time: 276 qps: 1449 run_script: test_sdb.sh comment:test sdb on phyiscal machine 32k sharding by 6 replication group
file_no:21-20 runing_num: 1 total_time: 70 qps: 285 run_script: test_sdb.sh comment:test sdb on phyiscal machine 32k sharding by 6 replication group
file_no:21-1 runing_num: 20 total_time: 433 qps: 923 run_script: test_sdb.sh comment:test sdb on phyiscal machine 32k sharding by 6 replication group
file_no:21-1 runing_num: 20 total_time: 837 qps: 477 run_script: test_sdb.sh comment:test sdb on phyiscal machine 32k sharding by 6 replication group
(简单说明一下测试结果 : runing_num为并发执行 sql的会话数 ,total_time为总的执行时间, qps就是每秒(插入)的执行数 ,后面是 备注 )
如果是12个复制组的插入性能如下:
file_no:1-0 runing_num: 1 total_time: 64 qps: 312 run_script: test_sdb.sh comment:test sdb on phyiscal machine 32k sharding by 12 replication group
file_no:20-1 runing_num: 19 total_time: 144 qps: 2638 run_script: test_sdb.sh comment:test sdb on phyiscal machine 32k sharding by 12 replication group
file_no:20-1 runing_num: 19 total_time: 160 qps: 2375 run_script: test_sdb.sh comment:test sdb on phyiscal machine 32k sharding by 12 replication group
file_no:23-1 runing_num: 22 total_time: 183 qps: 2404 run_script: test_sdb.sh comment:test sdb on phyiscal machine 32k sharding by 12 replication group
file_no:23-1 runing_num: 22 total_time: 210 qps: 2095 run_script: test_sdb.sh comment:test sdb on phyiscal machine 32k sharding by 12 replication group
file_no:23-1 runing_num: 22 total_time: 210 qps: 2095 run_script: test_sdb.sh comment:test sdb on phyiscal machine 32k sharding by 12 replication group
file_no:23-1 runing_num: 22 total_time: 205 qps: 2146 run_script: test_sdb.sh comment:test sdb on phyiscal machine 32k sharding by 12 replication group
file_no:23-13 runing_num: 10 total_time: 123 qps: 1626 run_script: test_sdb.sh comment:test sdb on phyiscal machine 32k sharding by 12 replication group
如果是单复制组,则结果如下
(测试Sdb单复制组的情况,每个线程执行2万insert .)
file_no:1-0 runing_num: 1 total_time: 64 qps: 312 run_script: test_sdb.sh comment:test sdb on phyiscal machine 32k no sharding 1 replication group
file_no:11-1 runing_num: 10 total_time: 249 qps: 803 run_script: test_sdb.sh comment:test sdb on phyiscal machine 32k no sharding 1 replication group
file_no:20-1 runing_num: 19 total_time: 500 qps: 760 run_script: test_sdb.sh comment:test sdb on phyiscal machine 32k no sharding 1 replication group
可以看到,复制组越多,也就是分片越多,支持的并发插入的qps就越多。
1.1.2 Mysql的插入性能
mysql单库的测试结果,json列的大小为32k。 每个sql文件20000行,总共插入40万记录,表文件大小13329498112
file_no:5-0 runing_num: 5 total_time: 82 qps: 1219 run_script: xcytest.sh comment:test mysql big json column
file_no:20-10 runing_num: 10 total_time: 197 qps: 1015 run_script: xcytest.sh comment:test mysql big json column
file_no:25-20 runing_num: 5 total_time: 99 qps: 1010 run_script: xcytest.sh comment:test mysql big json column
1.1.3 插入性能对比总结
本次测试的是32K的行的插入性能,从上面的对比可以看出,Mysql的单机性能不如6个复制组以上的分片。且SequoiaDB 的分片数越多,插入性能越大。
但需要指出的是,如果是小行插入,如行的大小100byte左右,则mysql的性能远胜于SequoiaDB ,在CPU只有4核的机器上,跑出3万多的insert的qps。 而在sequioadb 上,只能跑出不到15000的qps.