从 MySQL 5.6 版本开始,MySQL就支持除 SQL 外的其他访问方式,比如 NoSQL,甚至可以把 MySQL 打造成一个百万级并发访问的 KV 数据库或文档数据库。下面我们就一起来看看。

一、MySQL中表的访问方式

SQL 是访问数据库的一个通用接口,虽然数据库有很多种,但数据库中的 SQL 却是类似的,因为 SQL 有标准存在,如 SQL92、SQL2003 等。

虽然有些数据库会扩展支持 SQL 标准外的语法,但 90% 的语法是兼容的,所以,不同数据库在 SQL 层面的学习成本是比较低的。也因为上述原因,从一种关系型数据库迁移到另一种关系型数据库,开发的迁移成本并不高。比如去 IOE,将 Oracle 数据库迁移到 MySQL 数据库,通常 SQL 语法并不是难题。

MySQL 8.0 版本前,有不少技术伙伴会吐槽 MySQL 对于 SQL 标准的支持的程度。但是在当前 8.0 版本下,MySQL 对于 SQL 语法的支持度已经越来越好,甚至在某些方面超过了商业数据库 Oracle。 从上图可以看到,MySQL 8.0 在这一块非常完善,特别是对 JSON_TABLE 的支持功能。

二、通过Memcached 协议访问表

MySQL 5.6 版本开始支持通过插件 Memcached Plugin,以 KV 方式访问表,这时可以将 MySQL视作一个 Memcached KV 数据库。

对于数据的访问不再是通过 SQL 接口,而是通过 KV 数据库中常见的 get、set、incr 等请求。

但为什么要通过 KV 的方式访问数据呢?因为有些业务对于数据库的访问本质上都是一个 KV 操作。比如用户登录系统,大多是用于信息确认,这时其 SQL 大多都是通过主键或唯一索引进行数据的查询,如:

SELECT * FROM User WHERE PK = ?

若在海量并发访问的系统中,通过 SQL 访问这些表,由于通过主键索引进行访问,速度很快。但 SQL 解析(哪怕是软解析)却要耗费不少时间,这时单数据库实例性能会受到一定的限制。基于 Memcached 的 KV 访问,可以绕过 SQL 解析,通过映射关系,直接访问存储在 InnoDB 引擎中的数据,这样数据库的整体性能会在不花费额外成本的前提下得到极大的提升。

那么要启用 Memcached 协议访问 MySQL 需要做两件事情:

  • 开启 Memcached 插件;
  • 配置表与 KV 的映射关系。

具体操作如下所示:

-- 安装映射表

mysql> source MYSQL_HOME/share/innodb_memcached_config.sql



-- 安装插件,默认会启动11211端口

mysql> INSTALL PLUGIN daemon_memcached soname "libmemcached.so";

执行完上述操作后,会新增一个库 innodb_memcache,里面的表 containers 就是需要配置的KV映射表。如果业务常见的主键查询 SQL 如下,其中列 user_id 是主键:

SELECT user_id,cellphone,last_login 

FROM test.User 

WHERE user_id = ?

那么我们可以在表 Containers 中插入一条记录:

INSERT INTO containers

VALUES ('User','test','user_id','user_id|cellphone|last_login','0','0','0','PRIAMRY')

上面的映射关系表示通过 Memcached 的 KV 方式访问,其本质是通过 PRIAMRY 索引访问 key 值,key 就是 user_id,value 值返回的是由列 user_id、cellphone、last_login 组合而成,分隔符为"|"的字符串。

最后,通过 SQL 和 KV 的对比性能测试,可以发现通过 KV 的方式访问,性能要好非常多,在测试服务器上结果如下所示:

从测试结果可以看到,**基于 Memcached 的 KV 访问方式比传统的 SQL 方式要快54.33%,而且CPU 的开销反而还要低20%**。

另一种使用 Memcached Plugin 的场景是原先使用原生 Memcached KV 数据库的用户。这些用户可以考虑将 Memcached 数据库迁移到 MySQL 。这样的好处是:

  • 通过 MySQL 进行访问的性能比原生 Memcached 好,数据库并发优化做得更好;

  • 存储可以持久化,支持事务,数据一致性和安全性更好;

  • 利用 MySQL 复制技术,可以弥补原生 Memcached 不支持数据复制的短板;

三、通过 X Protocol 访问表

MySQL 5.7 版本开始原生支持 JSON 二进制数据类型,同时也提供将表格映射为一个 JSON 文档。同时,MySQL 也提供了 X Protocol 这样的 NoSQL 访问方式,所以,现在我们 MySQL 打造成一个SQL & NoSQL的文档数据库。

对比 MongoDB 文档数据库,将 MySQL 打造为文档数据库与 MongoDB 的对比在于:

可以看到,除了 MySQL 目前还无法支持数据分片功能外,其他方面 MySQL 的优势会更大一些,特别是 MySQL 是通过二维表格存储 JSON 数据,从而实现文档数据库功能。这样可以通过 SQL 进行很多复杂维度的查询,特别是结合 MySQL 8.0 的 CTE(Common Table Expression)、窗口函数(Window Function)等功能,而这在 MongoDB 中是无法原生实现的。

另外,和 Memcached Plugin 不同的是,MySQL 默认会自动启用 X Plugin 插件,接着就可以通过新的 X Protocol 协议访问 MySQL 中的数据,默认端口 33060,可以通过下面命令查看有关 X Plugin 的配置:

mysql> SHOW VARIABLES LIEK '%mysqlx%';

+-----------------------------------+--------------------+

| Variable_name                     | Value              |

+-----------------------------------+--------------------+

| mysqlx_bind_address               | *                  |

| mysqlx_compression_algorithms     | 

DEFLATE_STREAM,LZ4_MESSAGE,ZSTD_STREAM                   |

| mysqlx_connect_timeout            | 30                 |

| mysqlx_document_id_unique_prefix  | 0                  |

| mysqlx_enable_hello_notice        | ON                 |

| mysqlx_idle_worker_thread_timeout | 60                 |

| mysqlx_interactive_timeout        | 28800              |

| mysqlx_max_allowed_packet         | 67108864           |

| mysqlx_max_connections            | 100                |

| mysqlx_min_worker_threads         | 2                  |

| mysqlx_port                       | 33060              |

| mysqlx_port_open_timeout          | 0                  |

| mysqlx_read_timeout               | 30                 |

| mysqlx_socket                     | /tmp/mysqlx.sock   |

......

要通过 X Protocol 管理 MySQL 需要通过新的 MySQL Shell 命令,默认并不安装,需要单独安装。下载地址:https://dev.mysql.com/downloads/shell/。安装后就可以通过命令 mysqlsh 通过新的 X Protocol 访问 MySQL 数据库:

root@test:# mysqlsh root@localhost/test

X Protocol 协议支持通过 JS、Python、SQL 的方式管理和访问 MySQL,若要通过 X Protocol 协议管理文档数据,也需要下载新的 MySQL Connector,并引入新的 X 驱动库,如 Python 驱动:

import mysqlx



# Connect to server on localhost

session = mysqlx.get_session({

    'host': 'localhost',

    'port': 33060

})



schema = session.get_schema('test')



# Use the collection 'my_collection'

collection = schema.get_collection('my_collection')



# Specify which document to find with Collection.find()

result = collection.find('name like :param').bind('param', 'S%').limit(1).execute()



# Print document

docs = result.fetch_all()

print('Name: {0}'.format(docs[0]['name']))

session.close()

文章将持续更新,欢迎关注公众号:服务端技术精选。欢迎点赞、关注、转发