俗话说,天下大势,合久必分、分久必合。
数据库领域同样如此。过去五十余年,数据库经历OLTP和OLAP两种需求漫长的融合-分离-再融合的过程。究其原因,数据库的发展始终与用户场景需求变迁紧密相关。如今,随着云计算和大数据的兴起,业务场景正在经历前所未有的变革,数据库领域也掀起了一股HTAP浪潮。
Gartner在多次报告中强调,HTAP是数据库领域最重要的发展趋势之一,也是用户数字化转型中重要的数据平台。业界甚至认为,HTAP的兴起代表着数据库大融合时代的开启。
那么,为什么数据库大厂和云服务巨头们均纷纷押宝HTAP?开源+多云为何是HTAP普及的助推剂?面对新一代HTAP数据的崛起,多年积累形成的MySQL生态终于找到最佳归宿?
放在几年前,HTAP可能还会被认为是数据库领域的小众产品,是否成气候还有待观察。
而随着数据资源、数据消费习惯和数据驱动型场景发生巨大变化,用户需求与传统数据库之间的供需矛盾日渐突出,使得HTAP这种具备“同时支持OLTP和OLAP、创新计算存储框架、去ETL”等特征的新时代数据库成为不可阻挡的趋势。
如今,几乎所有数据库大厂和云服务巨头都在布局HTAP。例如,OceanBase去年推出的 3.0版本中就正式宣布向HTAP数据库进军;今年5月,Google Cloud发布HTAP云端数据库AlloyDB,为PG用户提供了HTAP数据库服务;再加上Oracle MySQL Heatwave,甚至连SnowFlake也发布Unistore来“蹭”HTAP的热点。
如果细数近一年以来的HTAP新品,会发现几乎全部都建立在云端之上。新一代HTAP+云正在成为数据库市场重要的潮流。例如,PingCAP近日发布的TiDB 6.0,也是与云端紧密联系的新一代HTAP数据库。
事实上,PingCAP是HTAP数据库领域非常重要的一个引领者。早在TiDB 3.0起,PingCAP就正式转向HTAP,从OLTP主引擎+OLAP辅助能力,到OLTP引擎+外接分析引擎,再到OLTP引擎+融合分析引擎,PingCAP在HTAP领域稳打稳扎,一个版本上一个台阶。
如今,随着TiDB 6.0的发布,针对HTAP进行了更多成熟性改进,TPC-C 性能也较 5.0 版本提升达到 76.32%,TiDB 6.0还增强了多个企业级特性,以更好适合云时代用户对于HTAP数据库的需求。
固然,有人质疑当前HTAP是新瓶装旧酒,并无太多新意。但业界普遍形成共识:新一代HTAP与过去完全不同,开源+云孕育而出,很多都有AI加持,而且是为数据敏捷而生,拥有过去前所未有的创新活力与迭代速度,并逐渐形成数据库技术变革的新潮流。
PingCAP CTO 黄东旭也直言:“TiDB近年来的快速进化与迭代,得益于开源和云的助力。”
HTAP之所受到用户青睐,某种程度是因为用户对于数据敏捷性的极度渴求。
“在数字化时代,客户最为在乎的是如何快速走向市场。这需要数据敏捷性,而HTAP恰恰是数据敏捷的核心能力。”黄东旭如是说。
最近几年,“海量、实时、在线”的需求越来越广泛,大量采用 MySQL 和 PostgreSQL 开源数据库的新一代企业需要提升对于热数据的实时在线分析能力,这类需求遍布几乎所有的互联网企业以及从事线上业务的数字化转型企业。对于新鲜数据的实时分析能力直接决定了这些业务的生死存亡,传统的 OLTP+OLAP+ETL 的数据架构已经严重阻碍了消费者体验,这种诉求催生了 HTAP 的技术变革。
而真正帮助HTAP与用户需求完成对接的则是开源+云。众所周知,开源近年来在数据库领域的流行和影响力与日俱增,DB-Engines数据显示,全球383款数据库中开源数据库占据51.7%,六款开源数据库进入到前十,开源正在成为像HTAP这种新时代数据库的创新源泉。
以PingCAP的TiDB为例,其产品研发体系建立在开源体系和开源社区的基础上,实现了一年一个大版本、一个月一个小版本的迭代速度。黄东旭透露道:“开源是TiDB的第一个增长引擎,通过开源体系,开发者、贡献者、布道者和用户能够很好串联起来,形成飞轮效应,让产品能够走向加速迭代和创新的正向循环。”
据悉,TiDB每年会有超过 40% 的代码更新,而这些代码有很大一部分由外部贡献者所共享。TiDB开源项目一直在全球和中国开源项目活跃度中名列前茅。
如果说开源改变了HTAP产品的开发模式和迭代速度,那么云则能够为HTAP产品提供用户最为直接的需求反馈。众所周知,云数据库一改以往传统数据库部署、运维、扩展等难题,以云服务的方式让数据库使用更加简单;更加关键的是,随着云计算的普及,云上用户群体持续增加,来自云上用户群体的需求反馈无时无刻都在发生,对于数据库产品的进化与迭代至关重要。
“真正的产品迭代是如何缩短用户问题/需求的反馈时间。云无疑为数据库等基础软件提供了这样的价值,让产品可以更好地迭代。”黄东旭如是说。以TiDB为例,自去年五月全托管的数据库即服务(DBaaS)产品 TiDB Cloud 公测版发布以来,已经陆续登陆亚马逊云 科技 、谷歌云等全球知名云服务商的Marketplace,并在今年5月份正式全球商用;今年 6 月与阿里云合作上线阿里云云市场,成为为数不多的跨全球三朵云的数据库服务。
在众多数据库产品之中,MySQL凭借着开源、免费、适合互联网场景等优势,常年位居全球最受欢迎数据库的前三。根据Slintel网站的统计数据,在全球关系型数据库市场中,MySQL市场份额最高,达到43.04%。
过去二十年里,开源MySQL数据库对于各行各业影响至深,捕获了来自互联网、金融、零售、交通等多个行业用户的心,堪称“万人迷”。例如,在中国就有超过9成的金融机构都应用了MySQL数据库。
但任何数据库潮流都是“需求变化+技术变革+架构创新”融合的产物,MySQL是如此,HTAP亦不例外。如今,场景的数据规模、业务并发量、处理速度要求跟以往相比早已不是一个数量级。此时,MySQL数据库的局限性愈发突出,扩展性很难满足用户需求,想继续获得增长的企业不得不使用分库分表方案,但这又会造成数据架构的复杂性。
新一代HTAP数据库无需分库分表,且具备实时海量规模的OLTP和实时数据分析能力,还拥有极为出色的扩展性,与很多业务场景的海量交易实时数据展现、平稳运行的需求高度契合,HTAP凭借技术架构优势崛起已成必然。
“用户需求侧最大的变化就是很多用户需要借助热数据实现运营级别的实时分析,获得实时洞察以支持决策,这极大推动了新一代HTAP数据库的需求。”PingCAP副总裁刘松补充道。
虽然MySQL已经增加列存引擎Heatwave来获得HTAP能力,但主要解决规模化查询的问题,系统本身架构并未产生革命性变化,扩展能力、OLTP吞吐量依然有着很大局限。“智能新能源 汽车 跟传统燃油车在外表看几乎没区别。数据库也类似,像TiDB这种新一代HTAP数据库,从架构设计、应对场景和使用体验等角度,都与传统数据库有着极大的区别。”刘松形象比喻道。
事实上,与过去SAP HANA这种小众、昂贵的HTAP不同,新一代HTAP拥有极强的兼容性,像Google Cloud、PingCAP这些数据库厂商都借助新一代HTAP架构为采用 MySQL或者PG开源数据库的企业拓展 OLTP和OLAP的能力范围。
例如,Google Cloud发布的HTAP云端数据库AlloyDB,为单机版PG生态用户提供了最好选择,TiDB则成为MySQL生态的最佳归宿。PingCAP大量用户中有很多TiDB与MySQL混合部署的成功案例;得益于 TiDB 的开放性,TiDB 也可通过和其他数据服务产品“混搭”形成新的数据服务解决方案, 如通过同样是开源的大数据计算引擎 Flink 混搭形成实时数仓解决方案,扩展 HTAP 数据库的能力边界。
黄东旭则直言,HTAP数据库除了产品、技术之外,尤为需要关心用户体验,“HTAP应该让用户觉得好用,屏蔽掉数据库的复杂性。”据悉,PingCAP是2022 Gartner Peer Insights“Voice of the Customer” 云数据库领域唯一入选的中国数据库公司,客户总体评分达到 4.7 分(满分 5 分),在所有入选企业中位列第一。在参与Gartner Peer Insights评分的PingCAP用户中,像互联网、金融等重点行业用户均高度认可HTAP现代数据库理念。
总体来看,今年是HTAP的大年,各大厂商纷纷在市场中上新。随着新一代HTAP数据库产品的增多,整个市场对于HTAP数据库理念和产品的接受与采用将会提速。而随着新一代HTAP数据库持续完善,让广大MySQL生态用户群真正看到了大数据时代一条绝佳的迁移路径。
我们看看现在世界上一流的互联网公司,前20强中的Yahoo(MySQL用户)、Google(MySQL用户)、Youtube(MySQL用户)、WIN Live(MS SQL Server)、Facebook(MySQL用户)、MSN(MS SQL Server)、Wikipedia(MySQL用户)、Blogger(MySQL用户)、MySpace(不知道)、Yohoo.co.jp(MySQL用户)、Baidu(MySQL用户)、Google.co.in(MySQL用户)、google.de(MySQL用户)、Microsoft(MS SQL Server)、Rapidshare(MySQL用户)、QQ.com(MySQL用户)、Google.fr(MySQL用户)、Sina.com.cn(MySQL用户)、Ebay(MySQL用户)、Fc2.com(MySQL用户),看看这些著名的公司吧。国内的大的互联网公司,找不到没有用MySQL数据库的。前段时间,很多猎头公司找MySQL的高手都很难。
MySQL的确已成为全球最受欢迎的开源数据库,Oracle收购后也必须顺应市场的潮流、顺应客户的需求,因此,这个产品的前途无量啊。
试想,QQ每天巨大的业务量,还涉及Q币、充值等等交易系统,MySQL完全胜任了它所有的业务。
Oracle不仅对MySQL产品做出了十大承诺,事实上,最近一系列的具体措施也证明Oracle将会使MySQL更美好!Oracle is now making MySQL better!
九月份在美国举行的Open OneWorld上有MySQL专场,今年12月份该活动在北京举行,也有MySQL活动的专场。
在产品发展路线上,Oracle预计在今年年底推出MySQL 5.5版本,其默认存储引擎将变为性能更加突出的Innodb,据初步评测,性能有近10倍的提升。Oracle增强了一系列MySQL企业级特性和工具,包含MySQL Monitor、Workbench GUI Tools、Cluster产品等等。
在市场竞争定位上,MySQL产品被主要定位于三大领域:Web、Telecom和Embed,事实上,这些也并不是原有Oracle的突出优势市场。目前,在大中国区,Oracle对MySQL产品已经成立了独立的销售团队,在国内有上海爱可生成为本地化服务和总分销支持。
一切看来,这个产品是符合市场需求的,Oracle这家市场运营的高手,会把一个很有市场前景的产品雪藏吗?答案是肯定不会的。
去精通MySQL吧,未来一定前途无量;阿里云、盛大云、中移动云、虚拟化等等有太多新的技术与MySQL相关;移动互联网、三网融合应用不断发展创造着越来越多的MySQL新机会。
还担心什么呢?大胆采用开源理念,选择最专业的合作伙伴去创新你的系统和业务吧!选择开源MySQL作为数据库平台是符合未来的发展趋势的,其跨平台、开放性、易用性和低的总体拥有成本TCO都是符合企业应用需求的。
目前比较流行数据库:
sqlserver和oracle数据库是比较常用的,且用于管理大型数据的。主流如下:微软:sql server 和 access;瑞典MySQL:AB公司 mysql;IBM公司:db2;美国Sybase公司:Sybase;IBM公司:informix;美国oracle公司:oracle;小型数据库:access、foxbase;中型数据库:sql server 、mysql、informix;大型数据库:db2、Oracle、Sybase。
后半部分无法回答,看个人发展方向,希望对你有用。
1. ASCII
用途:用来映射简单的单字节字符,比如大小写英文字母、阿拉伯数字、常用的标点符、运算符、控制字符等。
编码范围:U+0000 - U+007F
注意:对于用这类字符的场景够用了,但是却无法表达比如汉字,日文等编码。
2. UNICODE
用途:用来映射包含 ASCII 以内的其他的所有字符。
编码范围:U+0000 - U+10FFFF
注意:ASCII 是 UNICODE 的子集,ASCII 编码的字符可以无损转换为 UNICODE 编码的字符。
MySQL 常用字符集
1. Latin1
Latin1 是 cp1252 或者 ISO-8859-1 的别名。ISO-8859-1 编码是单字节编码,向下兼容 ASCII。
编码范围:U+0000 - U+00FF
ISO-8859-1 收录的字符除 ASCII 收录的字符外,还包括西欧语言、希腊语、泰语、阿拉伯语、希伯来语对应的文字符号。
单字节内的空间都被 ISO-8859-1 编码占用,所以能够用 ISO-8859-1 编码存储、传输其他任何编码的字节流。
比如把一个 Utf8mb4 的编码或者 GBK 的编码存入 Latin1,不会有任何问题。因为 Latin1 保留了原始的字节流,这也就是 MySQL 长期以来把 Latin1 做默认字符集的原因。
但是由于 Latin1 对任何字符都存放字节流,造成了字符个数的浪费。
比如:
CHAR(10) CHARACTER SET LATIN1;CHAR(10) CHARACTER SET UTF8;
该字段中存储字符个数 UTF8 是 Latin1 的三倍!!!
2. GB18030
GB18030 是中国官方标准字符集,向前兼容 GBK、GB2312,是这两个的超集。用 1、2、4 个字节分别表示一个符号。比如对一般中文字符,默认是用两个字节编码存储。Windows 系统,默认用的就是 GB18030。
若只是存储中文字符,那 GB18030 最佳。
原因有两点:
1)占用空间小,比如比 UTF8 小。
2)存储的汉字根据拼音来排序,检索快。
3. UTF8
UTF8 是 Unicode 的编码实现,可以存储 UNICODE 编码对应的任何字符, 这也是使用最多的一种编码。最大的特点就是变长的编码方式,用 1 到 4 个字节表示一个符号,可以根据不同的符号编码字节长度。
字母或数字用 1 字节,汉字用 3 字节,emoji 表情符号用 4 字节。UTF8 字符集目前是使用最广泛的。
注意!MySQL 里常说的 UTF8 是 UTF8MB3 的别名,UTF8MB3 是 UTF8MB4 的子集,UTF8MB4 才是真正的 4 字节 UTF8 字符集!
UTF8MB3 表示最大支持 3 个字节存储字符,UTF8MB4 表示最大 4 个字节存储字符。根据实际需要和未来展望,MySQL 8.0 已经默认用 UTF8MB4 基础字符集。
在开始演示之前,我们先介绍下两个概念。
概念一,数据的可选择性基数,也就是常说的cardinality值。
查询优化器在生成各种执行计划之前,得先从统计信息中取得相关数据,这样才能估算每步操作所涉及到的记录数,而这个相关数据就是cardinality。简单来说,就是每个值在每个字段中的唯一值分布状态。
比如表t1有100行记录,其中一列为f1。f1中唯一值的个数可以是100个,也可以是1个,当然也可以是1到100之间的任何一个数字。这里唯一值越的多少,就是这个列的可选择基数。
那看到这里我们就明白了,为什么要在基数高的字段上建立索引,而基数低的的字段建立索引反而没有全表扫描来的快。当然这个只是一方面,至于更深入的探讨就不在我这篇探讨的范围了。
概念二,关于HINT的使用。
这里我来说下HINT是什么,在什么时候用。
HINT简单来说就是在某些特定的场景下人工协助MySQL优化器的工作,使她生成最优的执行计划。一般来说,优化器的执行计划都是最优化的,不过在某些特定场景下,执行计划可能不是最优化。
比如:表t1经过大量的频繁更新操作,(UPDATE,DELETE,INSERT),cardinality已经很不准确了,这时候刚好执行了一条SQL,那么有可能这条SQL的执行计划就不是最优的。为什么说有可能呢?
来看下具体演示
譬如,以下两条SQL,
A:
select * from t1 where f1 = 20;
B:
select * from t1 where f1 = 30;
如果f1的值刚好频繁更新的值为30,并且没有达到MySQL自动更新cardinality值的临界值或者说用户设置了手动更新又或者用户减少了sample page等等,那么对这两条语句来说,可能不准确的就是B了。
这里顺带说下,MySQL提供了自动更新和手动更新表cardinality值的方法,因篇幅有限,需要的可以查阅手册。
那回到正题上,MySQL 8.0 带来了几个HINT,我今天就举个index_merge的例子。
示例表结构:
mysql desc t1;+------------+--------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+------------+--------------+------+-----+---------+----------------+| id | int(11) | NO | PRI | NULL | auto_increment || rank1 | int(11) | YES | MUL | NULL | || rank2 | int(11) | YES | MUL | NULL | || log_time | datetime | YES | MUL | NULL | || prefix_uid | varchar(100) | YES | | NULL | || desc1 | text | YES | | NULL | || rank3 | int(11) | YES | MUL | NULL | |+------------+--------------+------+-----+---------+----------------+7 rows in set (0.00 sec)
表记录数:
mysql select count(*) from t1;+----------+| count(*) |+----------+| 32768 |+----------+1 row in set (0.01 sec)
这里我们两条经典的SQL:
SQL C:
select * from t1 where rank1 = 1 or rank2 = 2 or rank3 = 2;
SQL D:
select * from t1 where rank1 =100 and rank2 =100 and rank3 =100;
表t1实际上在rank1,rank2,rank3三列上分别有一个二级索引。
那我们来看SQL C的查询计划。
显然,没有用到任何索引,扫描的行数为32034,cost为3243.65。
mysql explain format=json select * from t1 where rank1 =1 or rank2 = 2 or rank3 = 2\G*************************** 1. row ***************************EXPLAIN: { "query_block": { "select_id": 1, "cost_info": { "query_cost": "3243.65" }, "table": { "table_name": "t1", "access_type": "ALL", "possible_keys": [ "idx_rank1", "idx_rank2", "idx_rank3" ], "rows_examined_per_scan": 32034, "rows_produced_per_join": 115, "filtered": "0.36", "cost_info": { "read_cost": "3232.07", "eval_cost": "11.58", "prefix_cost": "3243.65", "data_read_per_join": "49K" }, "used_columns": [ "id", "rank1", "rank2", "log_time", "prefix_uid", "desc1", "rank3" ], "attached_condition": "((`ytt`.`t1`.`rank1` = 1) or (`ytt`.`t1`.`rank2` = 2) or (`ytt`.`t1`.`rank3` = 2))" } }}1 row in set, 1 warning (0.00 sec)
我们加上hint给相同的查询,再次看看查询计划。
这个时候用到了index_merge,union了三个列。扫描的行数为1103,cost为441.09,明显比之前的快了好几倍。
mysql explain format=json select /*+ index_merge(t1) */ * from t1 where rank1 =1 or rank2 = 2 or rank3 = 2\G*************************** 1. row ***************************EXPLAIN: { "query_block": { "select_id": 1, "cost_info": { "query_cost": "441.09" }, "table": { "table_name": "t1", "access_type": "index_merge", "possible_keys": [ "idx_rank1", "idx_rank2", "idx_rank3" ], "key": "union(idx_rank1,idx_rank2,idx_rank3)", "key_length": "5,5,5", "rows_examined_per_scan": 1103, "rows_produced_per_join": 1103, "filtered": "100.00", "cost_info": { "read_cost": "330.79", "eval_cost": "110.30", "prefix_cost": "441.09", "data_read_per_join": "473K" }, "used_columns": [ "id", "rank1", "rank2", "log_time", "prefix_uid", "desc1", "rank3" ], "attached_condition": "((`ytt`.`t1`.`rank1` = 1) or (`ytt`.`t1`.`rank2` = 2) or (`ytt`.`t1`.`rank3` = 2))" } }}1 row in set, 1 warning (0.00 sec)
我们再看下SQL D的计划:
不加HINT,
mysql explain format=json select * from t1 where rank1 =100 and rank2 =100 and rank3 =100\G*************************** 1. row ***************************EXPLAIN: { "query_block": { "select_id": 1, "cost_info": { "query_cost": "534.34" }, "table": { "table_name": "t1", "access_type": "ref", "possible_keys": [ "idx_rank1", "idx_rank2", "idx_rank3" ], "key": "idx_rank1", "used_key_parts": [ "rank1" ], "key_length": "5", "ref": [ "const" ], "rows_examined_per_scan": 555, "rows_produced_per_join": 0, "filtered": "0.07", "cost_info": { "read_cost": "478.84", "eval_cost": "0.04", "prefix_cost": "534.34", "data_read_per_join": "176" }, "used_columns": [ "id", "rank1", "rank2", "log_time", "prefix_uid", "desc1", "rank3" ], "attached_condition": "((`ytt`.`t1`.`rank3` = 100) and (`ytt`.`t1`.`rank2` = 100))" } }}1 row in set, 1 warning (0.00 sec)
加了HINT,
mysql explain format=json select /*+ index_merge(t1)*/ * from t1 where rank1 =100 and rank2 =100 and rank3 =100\G*************************** 1. row ***************************EXPLAIN: { "query_block": { "select_id": 1, "cost_info": { "query_cost": "5.23" }, "table": { "table_name": "t1", "access_type": "index_merge", "possible_keys": [ "idx_rank1", "idx_rank2", "idx_rank3" ], "key": "intersect(idx_rank1,idx_rank2,idx_rank3)", "key_length": "5,5,5", "rows_examined_per_scan": 1, "rows_produced_per_join": 1, "filtered": "100.00", "cost_info": { "read_cost": "5.13", "eval_cost": "0.10", "prefix_cost": "5.23", "data_read_per_join": "440" }, "used_columns": [ "id", "rank1", "rank2", "log_time", "prefix_uid", "desc1", "rank3" ], "attached_condition": "((`ytt`.`t1`.`rank3` = 100) and (`ytt`.`t1`.`rank2` = 100) and (`ytt`.`t1`.`rank1` = 100))" } }}1 row in set, 1 warning (0.00 sec)
对比下以上两个,加了HINT的比不加HINT的cost小了100倍。
总结下,就是说表的cardinality值影响这张的查询计划,如果这个值没有正常更新的话,就需要手工加HINT了。相信MySQL未来的版本会带来更多的HINT。