一、背景介绍
1.1 TDSQL-C ServerLess
在开始测评之前,先来认识下今天的主角吧——TDSQL-C ServerLess
关于该产品的说明,官方站点有非常详细且生动的介绍。
其实,某种程度上,我们就可以把这个产品理解成MySQL Plus或PostgreSQL Plus,也就是不用在关心,安装,性能,扩缩容,自动备份等复杂的配置性操作,这些将全部由腾讯云自动完成,我们只需要关注和业务的对接,就像办理手机套餐,只需月初月末关注下资费,真正做到即开即用。
更多介绍:👉https://cloud.tencent.com/product/cynosdb
截止到发文(2023.8.29,serverless形态的TDSQL-C只有MySQL版是可用的,PostgreSQL的产品架构正在升级,笔者其实对PostgreSQL相对更熟悉一点,这次没体验到PostgreSQL的版本有点小遗憾,后续可能还会有更多版本的数据库支持)
1.2 测评思路
根据活动的测评要求,我这里主要的测评目标是兼容性,并发和可靠性3个方面。
测评形式就是在本地跑通一个简单的项目,然后不修改任何代码,直接切换到TDSQL-C ServerLess产品,并分别进行一场压力测试,看看结果如何。
*注意:此次测评不涉及部分量化的性能指标,因为内网环境VS云端环境,有一个绕不开的变量,就是网络,在这方面内网肯定是碾压云端的,而此次测评的环境是在我本地的内网环境,不在腾讯云的服务器上,那通过外网链接到TDSQL-C Serverless数据库时,网络延时是避不开的,所以,我这次重点只关注刚提到的兼容性,并发和可靠性3方面。
1.3 开通服务
在开始之前,再简单介绍一下开通服务的流程,虽然本次活动主办方为开发者提供了免费账号👇
但了解过计费规则之后,我觉得,使用ServerLess形态的产品,按量计费完成这次测评顶天也就十来块钱不到的成本,所以决定真正的开一个服务来测试!
开通基本流程如下
- 进入TDSQL产品页👉https://cloud.tencent.com/product/tdsqlc,点【立即选购】
- 形态选Serverless,引擎目前支持MySQL和PGSQL,其他按需填写,自动暂停选项勾上(省钱省心省力),计费模式我就直接选的按量计费
- 填写集群的基础信息和配置,点【立即购买】即可
整体的流程其实也没啥可说,按页面提示一步步操作即可,我这边截了两个图👇
开通之后,到控制台界面,可以看到我们自己开通的数据库集群,需要注意的是,为了方便后面的使用,我们可能需要创建一个非root账号,创建流程就是点击下面👇,这个界面的【账号管理】,根据页面提示创建即可,非常简单,不再赘述。
配置好后,可以用本地的客GUI工具连接一下看下效果👇
好了,到此,云数据库就准备好了,接下来可以进入下一环节~
二、PK方案
我这里的测评PK方案是这样👇
- 先使用内网环境的数据库,跑通一个简单的业务
- 分表
- 编写一些简单的涵盖修改,查询数据库的操作,直接面向数据库,不经过任何中间件
- 跑一下性能测试
- 不修改任何代码,直接切换到TDSQL-C ServeLess地址,再跑一下性能测试
本地数据库使用的MySQL 8.0.34,因为用Mysql比较少,不太熟,稍微花了点时间安装,如果只是常规开发的话,还是直接用docker更方便一些。
通过测评,可以得出迁移到TDSQL的平滑程度也就是兼容性,并发能力和稳定性的相关数据,为我们实际的业务上云提供客观的数据支撑。
三、搭擂台
在测评之前,咱先来准备个简单的项目,为了尽可能把变量控制在数据库层面,我这里采用统一的项目框架(.net 6),统一的ORM(EFCore 7.0),分表中间件采用ShardingCore,建立2个数据模型,并通过显示声明外键的方式建立一对多关联(避免分表查询时产生笛卡尔积而造成连接数爆炸)。
除此之外,不再额外引用任何和数据读写相关的中间件,直接把请求打到数据库层,看看效果如何。
本篇会贴上一些关键的代码,并不完整,只为让此次测评过程更加的清晰,完整地址在这里:https://e.coding.net/tony_df/TestWeb/TestWeb.git。
3.1 创建项目
通过命令行,或者使用VS集成开发工具,创建一个测试项目,项目形态可以是web,控制台,或是.net框架支持的任何终端形态(现在的.net已经是全平台框架了哟~)
我这里选择的是web项目,下图是准备好的项目结构。
3.2 配置项目
用熟悉的方式,引入此次需要的一些包文件,我这里主要用到了Pomelo.EntityFrameworkCore.MySql,ShardingCore。
全部的包引用配置如下👇
ItemGroup>
PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="6.0.21" />
PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.10">
PrivateAssets>allPrivateAssets>
IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitiveIncludeAssets>
PackageReference>
PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" />
PackageReference Include="ShardingCore" Version="7.7.1.13" />
PackageReference Include="Spectre.Console" Version="0.47.0" />
PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
ItemGroup>
3.3 创建模型,配置分表规则
这里,我引入的模型关系是考试和试卷的关系,每场考试包含多张试卷,这里采用EFCore的CodeFirst模式来维护数据库模型,其中对试卷表(Paper)进行分表操作。
3.3.1 创建模型
public class Examination
{
//主键,采用Yitid插件提供的数字生成器ID
public string Id { get; set; }=YitIdHelper.NextId().ToString();
public ListPaper> Papers { get; set; } = new ListPaper> { };
//其他属性,节约篇幅这里就不一一列举了
...
}
public class Paper
{
//主键,采用Yitid插件提供的数字生成器ID
public string Id { get; set; }=YitIdHelper.NextId().ToString();
public Examination Examination { get; set; }
//显示声明考试id外键
public string ExaminationId { get; set; } = "";
//其他属性,节约篇幅这里就不一一列举了
...
}
3.3.2 FluentAPI定义模型属性
配置完成后,使用EFCore提供的FluentAPI,设定其模型关系
public class ExaminationEntityConfig:IEntityTypeConfigurationExamination>
{
public void Configure(EntityTypeBuilderExamination> builder)
{
builder.ToTable(nameof(Examination));
builder.HasKey(x => x.Id);
//...
}
}
public class PaperEntityConfig : IEntityTypeConfigurationPaper>
{
public void Configure(EntityTypeBuilderPaper> builder)
{
builder.ToTable(nameof(Paper));
builder.HasKey(p => p.Id);
//设定1对多关联关系,并声明外键Id
builder.HasOneExamination>(e => e.Examination).WithMany(p => p.Papers).IsRequired().HasForeignKey(p => p.ExaminationId);
//...
}
}
3.3.3 创建数据库上下文,引入ShardingCore扩展
创建一个类文件,继承AbstractShardingDbContext,如果不分库也不分表的话,继承DbContext就可以了,而ShardingCore实际上是DbContext的扩展,所以我们直接继承该扩展即可。
因为涉及到分表,所以还需要IShardingTableDbContext接口。
public class MyDbContext:AbstractShardingDbContext,IShardingTableDbContext
{
public MyDbContext(DbContextOptionsMyDbContext> options) : base(options)
{ }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
}
///
/// 分表路由的尾部命名规则,默认是{表名}_{从大到小的索引}
///
public IRouteTail RouteTail { get; set; }
}
到这里,如果我们不使用分表,就可以去注入服务,执行迁移来生成库表了,但因为我们想把测评的维度拉高一点,所以还要再多一些步骤。
3.3.4 配置分表路由
这里,对paper进行分表操作,通过对主键取模的规则来分表,更多关于分表的信息,大家可以参照ShardingCore的官方文档,这里由于篇幅有限,不过多介绍了~
///
/// 创建虚拟路由
/// 该路由为简单的取模hash路由,分表字段是string类型,接受3个参数,第一个参数表示后缀的位数,第二位表示取模的基数,第三位是取模后缀不足的左补字符.
///
public class PaperVirtualTableRoute : AbstractSimpleShardingModKeyStringVirtualTableRoutePaper>
{
public PaperVirtualTableRoute() : base(2, 3)
{
}
public override void Configure(EntityMetadataTableBuilderPaper> builder)
{
builder.ShardingProperty(o => o.Id);
builder.AutoCreateTable(null);
builder.TableSeparator("_");
}
}
3.3.5 注入服务,启动项目
因为是测试项目,我们采用默认方式注入服务(实际上,生产项目应该考虑集成AutoFac来完成容器注入)
我这里使用了.net 6 的顶级语句,没有之前常见的Startup.cs文件,为了保证入口文件的简洁,这里把配置服务也分到了一个单独的文件中,
主要代码如下
public static WebApplicationBuilder SetupServices(this WebApplicationBuilder builder)
{
_configuration = builder.Configuration;
builder.Services.ConfigureMvc();
//..其他服务
return builder;
}
private static void ConfigureSharding(this IServiceCollection services)
{
//添加分片配置
services.AddShardingDbContextMyDbContext>()
.UseRouteConfig(op =>
{
op.AddShardingTableRoutePaperVirtualTableRoute>();
}).UseConfig((sp, op) =>
{
var serverVersion = new MySqlServerVersion(new Version(8, 0));
op.UseShardingQuery((conn, builder) =>
{
builder.UseMySql(conn, serverVersion);
});
op.UseShardingTransaction((conn, builder) =>
{
builder.UseMySql(conn,serverVersion);
});
op.AddDefaultDataSource(Guid.NewGuid().ToString("n"),_configuration.GetConnectionString("DefaultConnection")
);
}).AddShardingCore();
}
以上配置完成后,就可以执行迁移,启动项目啦
四、擂台赛开始
4.1 Warm-up
正式测评之前,先来一个热身赛,项目准备好以后,我想先准备一些种子数据,虽然都是随机的,但我这里准备的多一点,10,000条考试记录搞里头,每个考试下再配10条试卷记录,共100,000条试卷记录,总共11万条记录,然后分别写入到本地库和ServerLess库,看看写入这批种子数据的效率如何
-
这是本地的执行截图👇
-
这是切换到TDSQL后的执行截图👇
这里实际已经可以印证TDSQL的兼容性了,我只是改了下连接串,然后分别启动了一下项目,10万+条记录,就这样平滑的插入了。
这里TDSQL的用时之所以比本地长是意料之中的,并不是它性能不行,而是网络因素影响,毕竟云端再快,也跑不过本地内网链接,所以这个执行时间具体的值参考价值不大,重要的是稳定。当然如果我把环境架到腾讯云的服务器,然后通过serverless提供的内网ip来测试,那情况就有不一样了,这里因为成本因素,我的测试程序还是在我本地的内网环境下运行。
除此之外,云服务的最大CCU我只开到了2,按官方的介绍,相当于2核4G内存,所以硬件配置也确实比不上本地内网服务器的配置,在此情况下,我分别执行了几次,所用时长几乎没有波动。
稳定性和兼容性可见一斑。
4.2 压力测试
4.2.1 测试接口
压测之前,我分别准备了三个接口,分别用于模拟实际的简单检索,复杂检索和入库操作
[Route("[controller]/[action]")]
public class TestController : Controller
{
private readonly ILoggerTestController> _logger;
private readonly MyDbContext _ctx;
public TestController(ILoggerTestController> logger, MyDbContext ctx)
{
_logger = logger;
_ctx = ctx;
}
public async TaskIActionResult> QuerySimple()
{
//模拟简单查询
//代码略
}
public async TaskIActionResult> QueryComplex()
{
//模拟复杂查询
//代码略
}
public async TaskIActionResult> Insert()
{
//模拟插入
//代码略
}
}
这部分具体的业务代码不在贴了,后续提供完整地址,主要就模拟2种不同程度的读操作和1种写操作
4.2.2 部署测试环境
这里我是把测试系统分别部署到了内网的两台服务器上,然后通过nginx做了一个负载均衡,如下图👇
如此,测试环境就准备好了
4.2.3 准备测试计划
测试计划如下,
- 目标运行环境:Kestrel(Windows server)
- 测试工具:JMeter 5.5
- 负载:2台
- UVs(线程数):200 * 2 (2台压力机,CentOS环境)
- 压测时长:300s(5分钟)
- 线程启动步长(ramp-up):20秒内全部线程启动
测试资源有限,暂时把压测参数定在这个范围
关键jmx配置信息如下👇
ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="tdsql-c测评" enabled="true">
stringProp name="ThreadGroup.num_threads">500stringProp>
stringProp name="ThreadGroup.ramp_time">20stringProp>
boolProp name="ThreadGroup.scheduler">trueboolProp>
stringProp name="ThreadGroup.duration">600stringProp>
ThreadGroup>
4.4.4 开整!
第一组:将数据库设定为本地的内网环境,测试结果如下
第二组:将数据库设定为TDSQL-C Serverless,测试结果如下
*注意,将连接串切换到云数据库时要在控制台修改最大连接数,我刚开始就时没有改,压测刚开始没多久就拉了,一看默认的最大连接数是80
通过压测结果可以得出
- 简单的单表查询和写入操作,本地库和云端库的并发能力相当,云端库弱一些,但差不了多少
- 涉及到分表的查询时,TDSQL-C的优势就发挥出来了,压力测试场景下的平均响应时长反超了本地数据库(本地是1745ms,云端是571ms,差了3倍)!
注意:压测结果里出现的一些错误,是并不都是数据库造成的,还有框架的并发能力,服务器本身性能,网络等因素等,因为我是把请求直接打到了数据库,没有缓存层,所以测评的结果数据并不亮眼,这个就暂时不考虑了。
更多关于使用JMeter压力测试的知识大家可以参考其官方文档,笔者也曾写过一篇相关的博客,👉:https://blog.csdn.net/juanhuge/article/details/130830055
同时,可以看一下TDSQL的一些监控图表
QPS👇
TPS👇
CCU自动扩缩👇
最后再看一下,从开通,到完成这次测评,中间还折腾了好几次,一共花了多少钱
1.75元!
五、总结
通过整个的开发,部署,测试环节的对比情况,前面提到的对TDSQL的测评维度,基本可以得出以下结论
- 稳定性 √
- 兼容性 √
- 并发能力 √
而且云数据库的控制台给出的各种监控图表非常丰富,可以帮助我们快速定位瓶颈所在,十分方便,如果服务器也在腾讯云,再搭配TDSQL-C一起使用,那性能应该会直接起飞。
好了,此次的测评就这样了.
最后的,一句话总结就是,TDSQL-C Serverless,可以闭眼入!
本篇同步发表于InfoQ:https://xie.infoq.cn/article/8fdd358dea4fbffadeaa4a15c