技术开发 频道

目标自主安全可控 中国银联分布式数据库实践

  【IT168 技术】导语:本文根据周家晶老师在2018年5月12日【第九届中国数据库技术大会(DTCC)】现场演讲内容整理而成。

  周家晶

  中国银联股份有限公司 高级工程师

  中国银联分布式中间件UPSQL Proxy负责人,从事开源数据库MySQL的研发工作,主要关注分布式事务、SQL优化等。

  摘要:介绍银联为满足自主安全可控这一目标,通过较长时间的积累,所形成的MySQL产品体系与架构。具体介绍分布式数据库中间件的分布式事务实现机制、SQL优化和分布式存储引擎实践。

  正文:

  分布式数据库

  分布式数据库简单来说就是海量数据和高性能要素可以水平扩展的数据库。中国银联在2014年开始投入MySQL开源数据库,主要为了自主可控以及去IOE。

  产品的建设分为UPSQL和UPSQL Proxy。

  回顾整个研发历程,可以分为三个阶段,初期、中期和当前。初期主要关注高可用、读写分离和分库分表;中期阶段开始考虑一些更加复杂议题,比如说分布式事务如何支持和实现、数据库连接池以及SQL解析执行优化;当前是一些新的议题,比如复杂查询。

  初始架构

  银联的初始架构,从下往上看,最下层是UPSQL data node数据节点,主要负责数据库层的高可用,是一个逻辑节点。中间层叫做UPSQL Proxy,在生产实践中我们一般录入两个或两个以上UPSQL Proxy。最上面一层是客户端,应用上要求负载均衡连接两个UPSQL或Proxy,同时我们有一个adm-server的组件,这个组件跟UPSQL和Proxy协同完成数据库检测和高可用决策、切户以及Proxy隔离,实现了从数据节点到UPSQL Proxy节点的高可用切换。

  在整个研发工程中,我们一直在思考一个问题:Spanner方案和Proxy方案现在是业界的两种方案,那么Spanner方案和Proxy方案在功能上有哪些区别,是否Spanner就是最终解决方案?

  分布式数据库

  无论是Spanner方案还是Proxy方案,在分布式事务实现上只有一条途径,那就是2PC。银联的实现方案主要有两点:一是用后端的数据库储存XA日志,避免对其他分布式组件依赖,另一点则是使用最后参与者策略去优化性能。

  方案对比

  左侧是一个通用方案,右侧是我们的方案。

  左侧方案,在一个全局事务里,写入两个数据分片节点,在commit 阶段两个本地事务都会做XA,然后选择一个节点进行分布式日志记录,完成之后,整个事务分布式就可以保证提交了,最后再进行xa commit。

  2PC方案最大的问题就是参与者和协调者处理网络异常数据比较困难,通过分布式日志就可以保证对异常的处理。

  我们方案的区别是,选择某一个本地事务,比如选择数据分片1作为最后参与者,最终参与者不需要使用XA事务,过程中不需要做xa commit,并由最后参与者进行分布式日志记录。

  主要优势

  1.   不需要使用单独会话进行xa日志记录(友商方案中向xa日志表的写入是自动提交的另一个会话)

  2.   减少一个两阶段事务

  主要缺点

  1.   涉及分布式事务的后端用户,都需要配置xa日志表权限,因而存在运维风险。

  所以左侧方案更适合云上公共产品的使用,右侧方案依赖严格的运维管理性策略。

  分布式死锁

  但是我们发现一个分布式死锁的问题。比如说,全局事务的a&b开启分布式事务,那么在time 1: a write db1.resource1 、time 2: b write db2.resource2紧接着time 3: a write db2.resource2、 time 4: b write db1.resource1,交叉反向的操作导致db1和db2上都出现了ab的所有等待,从全局的角度上看,就出现了死锁的问题,那么怎么去解决这个问题呢?

  死锁检测

  一个方案就是冲突图的检测。我们需要修改innodb-trx表,增加XAID信息,将XAID作为全局事务的一个标志,以XAID的角度去看这个锁的关联关系。这样只要我们拿到所有参与这张表的信息,就可以实现对死锁冲突的检测。

  死锁预防

  分布式死锁的预防策略是时间戳策略(Timestamp ordering, TO),保证单一时序的锁等待,来避免死锁的发生。

  具体做法是,在XAID的生成规则里进行逻辑时间拼接,事务开始时间+ TM(proxy)事务序号+ TM编号+ datanode + ...就可以保证业务上的需求。然后在DeadlockChecker::search() 中获取xaid,根据死锁预防策略进行处理,进行一个简单的时序保证。

  进一步优化

  虽然我们使用了最后参与者,但是最后参与者在整个事务提交过程中事务管理上是有相互的。我们正常的事务是begin、commit就结束了,要想尽可能的让操作一样多,就需要把xa end拿掉。拿掉的做法也很简单,以前在进入xa commit之前,会要求所处的事务状态必须先是xa end然后才能跳转到提交执行,现在把这个约束给修改掉,这样就会减少一个网络交互。

  架构演变

  从初期的上线结构推进到中期阶段,整个逻辑上没有太大的变化,但是UPSQL Proxy和UPSQL之间的内在联系变的更为紧密。

  SQL解析优化

  为什么要做SQL解析优化?

  关于Proxy,首先要做SQL解析,然后是路由计算、分布式执行计划计算、网络IO。这样,很明显可以发现Proxy主要资源开销和性能优化点为: SQL解析和网络IO。

  为避免硬解析,业界提供了两种方法:一种是Prepare,做一次语句解析后,使用语句编号与变量值进行交互。我们在最开始做的时候,Proxy已经支持了Prepare,Prepare协议的优点是不需要对它做SQL解析。

  另外一种方法是软解析,即如果是相互的语句,就可以复用之前的语法树,这样的话都能够避免SQL解析重复提高性能。

  相似性优化

  Prepare避开了语句输入这一层,软解析避开了词法解析这一层,那么我们就考虑能不能只做词法解析不做语法解析。于是我们提出了相似性优化,即输进去的语句通过词法解析之后,如果发现语法树相似就直接复用之前的语法树。

  相似性分析

  词法分析的结果是一组单词序列,单词序列的每个单词由单词类型和值组成。

  MySQL的SQL解析过程中,词法和语法解析耦合在一起,即语法解析的移进规约动作触发词法解析,而不是先完成词法解析,再开始语法解析。我们将流程修改为先完成词法解析获取单词序列,然后进行语法解析。

  另外是我们定义的相似性规则。什么是相似呢?就是对比两个SQL的单词序列,满足单词类型相同,如果是类型非参数,则要求单词的值相等(忽略大小写)。我们根据这个相似性规则制定了一个相似性hash算法,用于提升相似性查找性能。

  相似性复用

  需要实现该功能点,需要修改LEX_STRING。LEX_STRING是词法分析和语法解析的最基础数据。这里增加两个字段,一个是index,即其在词法解析中的位置。另一个是next_lex_string(MySQL的SQL语法中,它可以把连续的两个字符串拼接到一起,next_lex_string表示的是字符串后续的下一个字符串)。

  复杂查询

  如何解决复杂查询(例如典型的跨库Join)?

  基于MySQL有两条路,一条是强化Proxy层构建支持复杂查询的执行计划,但是这样做代价很高,并且无法保证SQL解析知识的完备性以及分布式计划的症状性,风险性很高。

  另一条路是使用分布式储存引擎,MariaDB Spider和MySQL NDB。但是MariaDB Spider同步调用较多、性能不佳,而MySQL NDB缺乏实践经验。MariaDB Spider是一个比较完善的解决方案,但其水平扩展主要是扩展存储容量,性能上的提升不明显。

  因此我们就考虑做一个类似于MariaDB Spider的方案。

  设计思路(Federated+Proxy)

  设计思路:Federated+Proxy

  核心思路是,由Federated引擎外挂UPSQL Proxy,由UPSQL Proxy实现数据拆分和事务管理。这样UPSQL Proxy把拆表的过程隐藏起来,Federated连上去的时候只看到一个单表,就可以轻松实现复杂查询了。

  这样的改动逻辑简单,功能可控。但是缺点也很明显:Federated是使用同步请求访问UPSQL Proxy,且UPSQL Proxy被屏蔽在Federated之后,导致整体性能低下。

  设计思路(语句分类路由)

  设计思路:语句分类路由

  核心思路是,OLAP和OLTP的融合技术。一条应用语句过来了之后,我们做一个语句分类,把复杂语句交给OLAP,简单语句交给OLTP,然后最终都访问同一个事务管理模块和执行模块,这样可以保证无论什么样的语句都居于统一的事务之中。

  设计思路:(协处理器:XProxy)

  在我们的方案中,我们研发了一个XProxy协处理器。

  核心思路是,UPSQL 和Proxy拿到语句后,做一个解析和分类执行,如果是一个复杂语句,就丢给MySQL Server,它会把复杂语句拆解成简单语句,然后反向的丢回给UPSQL 和Proxy。这个路径很长,但是优点是简单语句可以通过Proxy直接到数据库上,同时也可以支持复杂语句。但是缺点是交互次数变多,性能较差。

  伙伴部署

  在实际部署中我们会考虑两种部署方案,一种叫做伙伴部署。

  把UPSQL 、Proxy和MYSQL一一对应部署在一起,但是缺点是需要额外增加一个资源。

  集群部署

  第二种方案叫集群部署,也就是在伙伴部署的基础上做了一些改动。将XProxy引擎下沉到数据存储节点,实现资源复用。这样可以实现UPSQL 、Proxy和数据节点之间是集群和集群的关系。一般情况下我们会把它的复杂查询入到下面的一个备库,这样可以保证性能和资源。但是这种集群关系必须要保证Proxy转发请求到回来的这种双方的关联关系。

  复杂查询总结

  • XProxy实现要点:

  1.   使用STMT协议访问UPSQL Proxy

  2.   会话关联(UPSQL Proxy与XProxy双向话关联),事务管理器关联

  3.   在线DDL

  • 进一步优化(生产实践优先级低):

  1.   ICP(Index Condition Pushdown)

  2.   ECP(Engine Condition Pushdown)

  • 该方案在如下场景下性能极差,复杂查询下:

  1.   不带过滤条件的分页– 需要增加存储引擎接口

  2.   统计运算- 可以借鉴MariaDB Spider的MapReduce方案

0
相关文章