(四) MdbCluster分布式内存数据库——业务消息处理
离上次更新文章已有快5个月,我还是有点懒。但我们系统的研发并没有因此停下来。下面先简单介绍下MdbCluster最近的一些进展。
1. 提供了java语言的jdbc驱动。做这个目的是想将业务侧的现有自动化测试案例跑起来,以此扩大系统的测试场景。
2. 提供SQL命令行界面,解决日常分片数据查询操作问题。对于分片数据库,如果操作数据要到每一片去执行操作语句显然是不合适的。
3. 多业务接入。最早MdbCluster只接入一个业务系统,随着测试的深入我们接入了更多的业务系统。并在此基础上优化了在线扩缩容的能力。
下面我们继续讨论第二节中提到的最后一个问题:业务消息是如何校验、错误消息如何重定向、超时消息如何处理?
我们先回顾下业务消息的大概处理流程:在MdbClient、MdbAgent、MdbRWNode都会保存一份完整的SlotList列表,以标明每个数据分片对应的节点。当业务消息从App通过dbc驱动发给MdbClient后,MdbClient会根据partitionid来计算数据的所属分片。并根据计算结果将消息转发给不同分片节点的 MdbAgent,其会对收到数据进行第一次较验。如果有错,会将消息返回,并带上正确的分片信息。MdbClient收到分片错误回复后,会进行消息重定向。如果没错,消息会传给本节点的MdbRWNode进行执行,如果是Insert操作,MdbRWNode则会再次较检数据是否正确。
1. 业务消息如何校验,为什么需要校验?
上面提到了消息的校验过程。MdbClient进行的第一次较验是为了找到正确的分片信息。MdbAgent进行第二次较验是为了保证收到的数据都是归属本分片的。在插入操作时,MdbRWNode进行第三次较验,双重较验保证本分片的新增数据不会出错。
之所以设计这个看上去冗余的较验流程,主要是考虑在线扩缩容的场景下,MdbClient、MdbAgent、MdbRWNode三者的SlotList列表并非一直是一致的。当出现前两者(MdbClient和MdbAgent)不一致场景时,MdbAgent的较验保证分片节点入口数据的准确。而MdbRWNode除了接收MdbAgent的消息,还会收到其它进程的消息请求,因此需要增加校验保证不会往某个分片插入非本分片的数据(否则可能出现脏数据,这点是不可接受的)。
2. 错误消息如何重定向?
当进行扩缩容的数据迁移时,MdbAgent会最先收到某个slot的更新信息。MdbClient则最后才能收到。在MdbClient收到slot更新前,其所发出的关于这个slot的消息,都属于错误消息。考虑最大程度减少扩缩容时对正常业务的影响,MdbAgent在返回错误时,会带上正确分片的信息。MdbClient收到重定向消息时,会进行消息重定向,以继续正常流程。
3. 超时消息如何处理?
首先要讨论一下超时消息是如何产生的。当进行扩缩容操作,对某个slot进行数据迁移时,MdbAgent会对这个slot的数据进行锁定(可读不可写)。当数据迁移完毕后,才会放开。关于一个slot的锁定时间长短和库的大小成线性关系。假设库大小在163W条时,每个slot的锁定期大概为0.5秒(可以优化)。MdbClient在收到锁定期数据返回时,当然可以进行重试。但MdbAgent和MdbClient之间可能出现循环的消息风暴。并且App和MdbClient之间会有最大值为一个锁定期的消息队列堵塞(可能有300-500条堵塞消息)。对于一个实时系统来说这是不可接受的。由于在线消息刚好碰到某个slot锁定的概率(1/16384)较低,即当TPS压力为16384时每秒会遇到一条。因此,对于锁定消息,MdbClient会直接返回给App,由App进行相关处理。
4. 多分片消息处理
当一个查询为全表扫描或者涉及多个分片的数据操作时,MdbClient会分解这些操作,并将这些操作分别发向对应的分片节点。假设对一个有5个分片节点的库进行一次全表查询。MdbClient会给5个分片分别发送一条查询信息,在分别收到5条返回结果时,MdbClient会转发这5条消息给App的dbc驱动。由App的dbc驱动进行数据的汇总。最终,App会收到完整的数据。两个问题:
a) 分片信息对App是隔离的,App的dbc驱动是如何知道要等待多少条返回信息? 诀窍在于,MdbClient在拆分消息时,会将拆分条数放在请求消息里面。消息返回时,拆分条数也会跟着带回。dbc驱动根据这个条数就知道需要等待的消息数。
b) 为什么是dbc驱动等待,而不是MdbClient等待?由于dbc驱动的操作是同步的,MdbClient是异步的。所以放在dbc驱动进行消息等待并不会影响系统的效率。
下一章我们会讨论一下在线扩缩容的架构设计,以及迁移数据的状态控制。