深入浅出全链路压测
上QQ阅读APP看书,第一时间看更新

2.1.3 影子库与影子表

影子库与影子表的建设思路是类似的,两者都是将压测数据转存至一个独立的数据区域,与真实数据隔离开,只不过影子库是基于数据库维度建立这个数据区域的,而影子表则是基于数据表维度建立这个数据区域的。然而,虽然建设思路类似,但是两者的技术实现方案还是有所差异的,影子库的实现更容易理解,改造成本可控,但需要花费双倍的数据库资源(哪怕是只读表也需要冗余资源);影子表的实现相对复杂,需要中间件支持,但冗余成本低(即冗余的资源少)。

我们先来看影子库的技术实现步骤,如下所示。

(1)创建一个新的数据库作为影子库,影子库的名称没有特别的要求,但推荐在原数据库名中增加前缀或后缀作为影子库的名称,以便统一管理。例如,原数据库名为Foo,影子库可以命名为SFoo。影子库建立完成后,我们会得到一个新的数据源。

(2)在影子库中建立与真实表结构完全一致的数据表,并将真实表的数据脱敏或偏移后全部写入新的数据表,保证两者数据规模一致。

(3)根据压测标识实现数据源的动态切换,确保压测请求触发的数据操作都能命中影子库。

其中,第(3)步的实现比较关键,我们希望压测尽可能减少对业务代码的侵入性。一种常见的第(3)步的实现方法是将数据源切换的功能集成到一个SDK中,业务代码使用这个SDK连接数据源,达到动态获取数据源的目的,这样改动的业务代码量非常少。还有一种更先进的实现方法是通过字节码增强技术,在代码运行时改变连接的数据源,从而达到根据压测标识动态切换数据写入区域的目的,这样几乎不需要改动业务代码,当然,这种方法实现难度更大一些。

安全起见,影子库一般需要在独立的实例上搭建。部分企业在实践中会将影子库与真实库建立在同一个实例上以节省成本,但这样做会带来一定的风险,一旦影子库的流量压力过高,可能会影响真实库的性能,因此需要对相关指标进行严密的监控。

我们再来看一下影子表。通俗地说,影子表就是一张与真实表结构一致的新数据表,我们需要确保两者存储的数据规模是一致的,否则会影响压测结果的可信度。

如图2.4所示,影子表的建立过程可以参照以下步骤:

针对某张真实表建立相应的影子表,两者的表名可以通过在影子表的表名上增加前缀或后缀区分,例如,原表名为User,影子表名可以设为TUser;

将真实表的数据进行脱敏,部分ID类字段需要进行偏移,以免字段增长后与真实表的字段冲突,例如,真实表中的订单号均以1开头,那么影子表中的订单号可以偏移为以9开头;

将脱敏和偏移后的数据导入影子表;

进行完整性检查(如数据量、表结构等内容),确保数据无误。

图2.4 影子表的建立过程

影子表建立过程中的各环节,需要在数据库中间件或独立平台中实现并串联。此外,根据压测标识将数据操作路由至相应影子表,也需要改造数据库中间件或在ORM框架中定制路由逻辑,可见影子表所需的改造工作量较大。我们通过表2.1看一看影子库和影子表的对比情况。

表2.1 影子库和影子表的对比情况

对于影子库和影子表的选型,笔者给出的建议是:如果数据库中只读表的数量明显高于需要执行写操作的表的数量,可以选择影子表的技术实现方案,可节省成本;反之,选择影子库的技术实现方案,可降低配置和改造成本。

最后,还要提醒一点,我们无论是采用影子库还是影子表的技术实现方案,在数据准备完毕后,都需要使用小流量执行一次全链路压测,这样一方面可以检查数据的正确性,另一方面也可以作为一种预热操作。影子表中的数据都是“冷”数据,它们不像真实表中的数据一样会被加载到缓冲池里以提升访问和操作的性能,如果不执行预热操作,很容易得出“偏劣”的性能结论。