异数OS-织梦师-老子罗盘(十) TPS从2个数量级到4个数量级的蜕变
文章目录
前言
《道德经》第06章 谷神不死,是谓玄牝。玄牝之门,是谓天地根。绵绵若存,用之不勤。
这可能是华夏文明最早对流水线概念的通俗理解,一般认为秦统一六国以少剩多靠的是集权体制,但这可能是表面理解,实际上他靠的应该是标准化流水线,这让他的生产能力相对对手可能有数量级的优势,在数量级的优势威慑下六国的合纵连横中庸套路变成了零和笑话。
老子认为天道是自然顺畅井然有序生生不息高度并发的,然而这实际上是美丽的谎言,实际上道德经第五章讲到天地不仁,以万物为刍狗,记得30年前母亲给我上的易经课程上制作了一个易经玩具叫做罗盘,上面有十种符号十二个扇区六个轮盘,每种符号之间有生克泄耗四种逻辑关系,每个轮盘上分散着十个符号,每个扇区代表着一个局,局内所有符号是同时并发的线程,局可以通过生克泄耗的符号关系确定出刑冲破害合会等六个局面,通过移动每一个轮盘可以得到新的时局分布,做好后,母亲教我推演出每一个扇区的局面,并提出了一个问题,是否有一种轮盘组合使得12个扇区同时在合会的局面上,但我无论如何努力,局面上都不会出现3个以上合会局面, 智商不够,那么只能作弊,我修改轮盘上每个符号的位置,做出了更多的合会局,当然这个问题后面没有了结果,我知道修改符号位置意味着修改了天道,这在现实中是不可能的。
一个经典问题的困惑
一个14亿人的元宇宙交易系统,有交易员A B C … 交易业务T(A,B),T(A,C),T(B,C)…交易发成错误时需要回滚,对于这一问题我们通常想到的是利用分布式实现多公共账号交易法和多房间交易法,这对前端的实现是靠谱的,可以提高并发请求能力,但最终问题并没有解决,交易问题是一个二元事务问题,和一元事务不同,一元事务通常可以使用行级锁高效应对,而二元事务存在并发投毒形成链式传染问题,最终演变为公共账号和房间对数据库的二元事务tps问题,而这样的需求在数据库实现中理论上不能用行级锁,即便1%的并发冲突率也会因为链式传染问题而退化为悲观情况,悲观情况下只能用表锁做全局串行化,最终这意味着14亿人的系统是不能并发的,只能用单线程串行化,这是地球最高效的redis单线程用到今天依然坚持单线程的主要原因。
异数OS-织梦师-老子罗盘中间件背景
2019年 异数OS 水母消息队列完成了400万iops的消息队列性能,他相当于linux等bug操作系统上mq redis等性能的两个数量级左右,但实际上水母消息队列的成绩应该不止如此,因为他只是单核实现的,如果在目前100核以上CPU上,他理论上是应该有望做到4个数量级的性能提升,只是水母消息队列在面对二元事务问题时只能和redis类似,粗暴悲观的使用全局串行化,因此在二元事务问题逻辑上他只能利用一个CPU,随后在2010年的异数OS管仲中间件中开始思考解决这一问题,管仲认为二元事务冲突时不可发起事务,因此需要生产者将事务分为本地事务(一元事务),局部并发事务,全局串行事务三类依次做出不同的消费策略,以达到利用多队列分载的目标,但是为了达成这一目标,却对系统设计带来了几个新的挑战,全局串行事务需要对事务做全队列冲突检查,这需要对队列入队做全局锁,全局串行事务需要在独立的队列中做再次冲突检测并尝试入队,在全房间交易情况下,重新入队面临大概率失败并反复尝试问题,这种持续饥饿会使部分请求面临超时,同时全房间消费场景二元事务冲突率激增,在冲突检查情况下也会面临队列饥饿问题使队列能力退化,这实际上让管仲中间件的实际效果退化到不如水母消息队列,最终我发现他实际上就是一个不得不面对的罗盘问题,老子罗盘实际上是一个很麻烦的综合性问题,所以一直耽搁到现在,直到老子罗盘中间件实现时间片资源轮转流水线解决二元事务并发问题后,这一单核魔咒约束才得以打破。
再次作弊,篡改天道
可以看出交易系统实际上是一个罗盘问题,只是罗盘上的符号序列一般是按照天道规律统计确定好的符号序列,但在我们自己创建的系统中,这种天道符号序列是可以被我们设计定义的,所以问题变成了一个找到所有扇区无冲突的轮盘局,有了这个无冲突罗盘,我们便可以实现无锁并发的交易系统。
鉴于老子对待这个问题如此乐观向上,因此本中间件名称引用老子为名,当然只有梦想不可以,解决问题还是要靠罗盘。
注意本文方法因为1998服务器操作系统基础理论约束,目前只能在异数OS平台实现,不适合其他bug操作系统借鉴抄袭,至于为什么,在本文最后章节有详细解释。
时代的谴责,1998服务器操作系统基础理论天花板下的蝇营狗苟
自1998年百兆以太网诞生以来,PC在硬件和操作系统的体系结构技术上就已确定,虽然经过大量创新,但基本都以失败而告终,在bug操作系统如linux上,mysql 5000tps仅能发挥百兆网卡iops性能的二十分之一,号称高性能的redis 10万tps也仅能发挥百兆网卡iops性能的三分之二,这导致很多新硬件在还未发挥出万分之一性能的情况下就已惨遭淘汰,比如本文使用的10年前的仅5元的e5v1 2670洋垃圾,已淘汰5年的洋垃圾Mellanox connect x4 100G网卡,他的理论iops性能高达1.4亿 iops,他们只是因为没有合适的服务器操作系统以及数据库能够发挥其万分之一的性能而惨遭淘汰,当然即便是异数OS下也仅能发挥五分之一的性能而已。
在此背景下,2024年的架构师面试风向居然变了,各路忽悠在1998基础理论约束情况下,在没有突破天花板的操作系统加持下,可能会面临尴尬的面试场面。
面试场景1
字节的架构师面试从过去千万tps下降到10万tps,很多人很奇怪,这是张一鸣务实了,自己都面不过,也别要求别人。
解释: 可能不是担心张一鸣面不过,而是担心bug被人拿出来踩爆,在操作系统iops没有突破1998百兆天花板情况下,业内通常期望采用分布式方案,但分布式多副本缓存必然存在幻读幻写缓存不一致的取舍问题,如果遇到八股狂魔,那么一定会把字节系统的bug拿出来踩爆,这可能导致不少架构师脸上难看,10万tps的原因是bug操作系统下redis的tps大概10万左右,这是1998年百兆服务器操作系统基础理论天花板魔咒,所以这是架构师包括张一鸣在内所有人的天花板,在bug操作系统下如果超过这个数,那只能招进来忽悠神棍。
面试场景2
一个阿里员工面试架构师的失败
被面试官问了一个题目,数据库能支撑多少tps。2万tps的并发,要怎么支撑
当时面试中,很纳闷这个问题。对于2万tps,在数据库层面,分库分表不就可以了,真不知道难度在哪里?
首先,tps是每秒事物数,也就是说,上层应用已经做好了优化,包括缓存,异步等等,打到数据库层面的流量,才能算作tps吧。所以对于2万tps,如果不能拆业务,就是分库分表来扛了。
接着一个问题,单库能支撑多少tps?我自己事后思考了下,事务有大有小,如果按一个事务5ms的时间来算,一秒是200个。数据库按64线程算,那么单库也能支撑过万tps了。所以对于单库tps的量级,取决于事务的耗时(这个也取决于数据库性能和业务复杂性),还有数据库的线程数。
解释:
首先他没明白tps的定义,他跟qps有很大的区别,tps更考察后端开发能力,而他的问题是把tps问题当做qps来对待了,tps意味着二元事务问题不能通过分表分库扩充性能,只能悲观全局串行化,因为他答错了,所以面试官会紧接着问单库tps,然后他又答错了说用多线程扩充性能,然而tps在bug操作系统下利用单线程做全局串行化是更高效的无奈选择,主流bug操作系统iops无法多线程扩充tps业务性能,所以tps性能在bug操作系统上就两种定义,落磁盘SSD的mysql 5000 tps,落内存的redis 10万tps,相当于百兆网卡的iops性能,如果再做pbft的话,就200左右,如果要提升数量级性能,只能升级操作系统到异数OS。
解决问题 异数OS-老子罗盘 时间片无冲突资源轮转流水线
准备工作
什么是多元事务?
多元事务来自于银行家问题,是指一次事务中需要锁定多行资源的行为,他是多行原子的,比如账号交易问题入库出库问题元宇宙世界物理交互等是二元事务,学生班级信息登记问题是一元事务问题,多元事务通常可以分解为多个子二元事务,事务系统需要确保子二元事务的顺序执行回滚,本文主要解决二元事务的并行执行效率问题。
什么是二元资源组?
为了确保二元事务的原子性和事务消费一致性,我们将资源分组,每个二元事务T(a,b)对应确定一个 二元资源组向量R(n,k),n k为访问的资源组ID,R(n,k)与R(k,n)等效,因此n组资源可划分为n*(n+1)/2个 二元资源组向量,对应它可以用于罗盘流水线的无冲突资源规划,以及并发事务队列ID向量的确定,简单的资源分组方案可以用(资源ID%n)+1,n为资源分组数量,如果请求中存在一元事务,需要将其占位补全为二元事务,如T(1)补全为T(1,1->n),对应二元资源组向量R(1,1->n)。
罗盘时间片无冲突二元资源组轮转流水线
如上图所示罗盘时间片无冲突二元资源组轮转流水线要求每一个时间列上,R向量中每一个标量唯一出现(向量内可以重复出现,因为组内事务是无冲突串行的),这种机制让流水线的内存事务消费可以保证同一时间片一个资源组只有一个流水线(CPU)访问,因此内存事务消费时无需任何锁保护。
什么是对偶二元资源组,对偶二元资源时间列
当流水线执行一个时间片时,时间片内每个流水线的负载任务规模都不相同,空闲的二元资源组流水线需要等待繁忙二元资源组流水线的完成,因此在执行单一任务的流水线效率通常难以超过50%,因此为了提高流水线效率减少时间片内气泡,我们需要流水线在一个时间片内可以执行两个二元资源组的可并发任务,这两种任务需要在负载热度排名上互补,我们称这样的两种任务是互偶并发任务,他们各自执行的二元资源组称为对偶二元资源组,所有流水线在一个时间片上执行的两个二元资源组列称为对偶二元资源时间列。
对于事务系统,通常有RAM消费任务,和持久化IO任务,因此我们需要为这两种任务生成对偶二元资源时间列,对偶二元资源组一般需要互斥,要实现互斥的对偶二元资源组则需要更多的资源分组,否则可能出现气泡,但通过影子里程碑方案,RAM消费任务和持久化IO任务是分开工作在不同副本版本上的,因此不需要对偶二元资源组实现两组互斥。
什么是一致性版本二元资源时间组
一致性版本二元资源时间组用于解决里程碑更新伴随的半消费问题,我们可以使用半消费方法解决半消费问题。
先看问题。
上图可以看出,在执行上面的流水线时,如果随意切换新的里程碑,则里程碑N中的Ver2时间片中紫色标记二元资源组会分别依赖里程碑N-1中的Ver0 Ver1两个资源版本,因此这样的流水线在里程碑持久化任务中不得不对两个版本都做保存,这仅仅是4流水线8资源组的场景,如果资源分组更多,则会出现更多的依赖版本,为了解决这个问题,我们引入一致性版本二元资源时间组,该二元资源组要求覆盖所有资源组,且每个资源组在流水线中只出现一次,当流水线执行完一致性版本二元资源时间组后,所有的后续时间片的资源组消费都会起始于一个统一的资源版本,一致性版本二元资源时间组实际上是一种半消费策略,比如4流水线 8资源组会有36个二元资源组,一致性版本二元资源时间组会消费4个二元资源组并覆盖全部8个资源组,而将剩余32个二元资源组消费任务推迟到即将到来的下一个里程碑中去消费。
上图是4流水线8资源组的一个一致性版本二元资源时间组,我们可以将它插入到里程碑切换前,这样就可以让所有资源组达到一个统一的版本。
通过上图观察可以发现,在里程碑N中Ver4 Ver5的最低版本依赖已全部基于一致性版本时间片Ver3。
什么是NM并发任务与三态向量无冲突阻塞并发状态机(来源于异数OS伏羲易经机中间件)
在一般的状态机理论中存在如下几个问题。
1.缺少对系统过程变量的建模,只有目标事物状态建模,这导致系统在运行状态机时缺乏系统对系统自身阻塞过程问题的处理,导致系统的设计很幼稚不茁壮。
2.标量状态,标量状态意味着他不能有效准确全路径的应对并发问题,对并发问题无法有效建模。
3.多头状态,一般状态机只有一个活跃状态,因此只能发起一项动作,而现实中并发任务通常是多头状态,这在传统状态机中无法表达。
为了应对上述问题,异数OS伏羲易经机按照易经原理实现了复杂任务多头并发向量状态机,易经状态机对目标和系统状态做自指统一一致建模,并处理多头并发向量问题,但易经状态机相对考虑的目标问题更加复杂,他需要根据目标状态和系统状态做出多个不同维度局面的关系推理,如刑冲破害合会,但显然对于事务以及简单的系统建模来讲,我们不需要对所有局面进行理解并建模,只需要对"冲"局问题建模即可,冲局就是冲突的局面问题。
NM并发任务,通常一个系统中会有N个可能并发的复杂任务,每个复杂任务可以分解为M个子任务,N个复杂任务之间的关系可分为,无冲突可并发,互斥冲突不可并发,互偶可并发等三类。
- 无冲突可并发,如果两个复杂任务,一个任务中M个子任务与另一个任务中M个子任务无冲突,则两个复杂任务无冲突可并行,使用P(A,B)向量表示AB两任务互偶。
- 互斥冲突不可并行,如果两个复杂任务,一个任务中M个子任务与另一个任务中M个子任务中任何一个子任务相同则冲突,两个复杂任务无冲互斥冲突不可并行,使用M(A,B)向量表示AB两任务互斥。
- 互偶可并发,如果两个复杂任务,一个任务中M个子任务与另一个任务中M个子任务中任何一个子任务相同冲突,但任务引用的资源可分时不冲突或可条件满足时部分可执行,这种情况叫做互偶可并行,使用D(A,B)向量表示AB两任务互偶。
举一个NM并发任务的例子,人走路任务可以按照NM任务模型分解出下表的任务。
N任务 | M任务 | 无冲突关系 | 互斥冲突关系 | 互偶关系 |
---|---|---|---|---|
上半身任务A | 1.右手抬起 2.左手落下 | P(上半身任务A,头部任务A) | M(上半身任务A,上半身任务B) | D(上半身任务A,下半身任务B) |
上半身任务B | 1.左手抬起 2.右手落下 | P(上半身任务B,头部任务A) | M(上半身任务B,上半身任务A) | D(上半身任务B,下半身任务A) |
下半身任务A | 1.右脚抬起 2.左脚落下 | P(下半身任务A,头部任务A) | M(下半身任务A,下半身任务B) | D(下半身任务A,上半身任务B) |
下半身任务B | 1.左脚抬起 2.右脚落下 | P(下半身任务B,头部任务A) | M(下半身任务B,下半身任务A) | D(下半身任务B,上半身任务A) |
头部任务A | 1.目标跟随旋转头部 2.目标跟随旋转眼球 3.表情反馈 | P(头部任务A,上半身任务A) P(头部任务A,上半身任务B) P(头部任务A,下半身任务A) P(头部任务A,下半身任务B) | NA | NA |
上面这个表可以看出在走路过程中,上半身任务A和上半身任务B可以互偶,但是有条件的,右手抬起任务的发起必须是左手抬起任务的完成状态时才可以发起,左手落下需要等待右手抬起空闲时发起,左脚抬起任务的发起必须是右手抬起任务的完成状态时才可以发起,因此他需要系统中对任务过程状态进行建模,为此我们引入系统三态状态来应对这一逻辑,分别为空闲状态,执行状态,完成状态,每种状态都需要阻塞完成,比如空闲状态迁移到执行状态需要发起任务,而发起任务需要等待互偶时机达成,完成状态迁移到空闲状态需要等待互偶任务条件满足时发起互偶任务,三态阻塞状态的引入使得多头并发状态机可以更准确的建模。
因此一个目标对象的状态再加入系统三态状态后,形成一个状态向量,比如我们定义一个左手状态向量。
//系统三态状态
enum SystemTristate
{
Idle=0,
Busy=1,
Complete,
};
//手状态向量
struct HandStateVector
{
enum HandObjectState
{
Up, //手在上面
Down, //手在下面
};
HandObjectState nHandState; //手状态
SystemTristate nSystemState; //系统状态
};
再看这个动作我们发现,手脚头只要在任务冲突约束满足情况下,即可并发执行,因此标量化的状态机是无法满足的,为此我们需要实现向量化状态,比如上面的人体走路状态机,我们可以定义如下向量状态。
异数OS-老子罗盘系统实现方案
总体结构
事务处理的五个阶段
事务由TThread发起,在资源准备阶段TThread需要对 KVCache做命中测试,命中测试不通过,则发起相关资源行读,并等待KVCache系统的惰性资源预读完成,资源准备阶段完成后TThread阻塞等待Pipeline的事务消费完成事件,待Pipeline的事务消费完成时,TThread进入持久化等待阶段,持久化后,TThread将完成事务请求,一般情况下,为了高性能低成本,我们仅仅在RAM事务消费后就完成事务,当然我们也可以区别对待,重要的业务我们可以等待里程碑持久化的完成,当然在用户量大,残差bitmap大的情况下,用户请求延迟将不确定。
TThread线程组(并发与局部串行化预备)
TThread为用户事务线程,接受用户的事务请求,每一个CPU节点一组,TThread确保用户事务的串行化发起,因此通过TThread来合理确定并发与串行保序的问题边界,由于TThread接受用户请求,因此TThread需要实现海量并发以及高性能的IOPS,每TThread线程组通常需要实现100万以上TCP HTTP链接线程。
TThread需要在用户安全层面解决双花问题,保证同一时间只提交用户的一个请求即可解决,另外TThread中不应该出于业务设计而有主动缓冲相关行资源副本的行为,这会存在幻读问题,但可以在流水线事务处理例程中拷贝资源副本,在流水线事务消费例程中,相关资源是版本一致的不变的原子的。
二元资源组事务队列(来源于异数OS水母消息队列中间件)
二元资源组事务队列需要按照二元资源组向量布局来实现命名的多队列,该队列需要实现多CPU生产者无锁入队,不要求保序(保序问题由TThread确定),单消费者出队,确保单消费者消费是由无冲突流水线机制实现。
无冲突多任务流水线
无冲突流水线Pipeline-N,通过NM并发任务与三态向量无冲突并发状态机设计理论实现流水线任务状态机,N型状态任务有负载任务,里程碑切换任务,M型任务分为RAM事务任务,持久化预备任务,持久化IO任务,每CPU节点一条流水线,NM并发任务与三态向量无冲突并发状态机的设计可以充盈流水线,解决流水线的短板气泡效应。
N任务 | M任务 | 无冲突关系 | 互斥冲突关系 | 互偶关系 |
---|---|---|---|---|
负载任务 | 1.RAM事务消费任务 2.持久化预备任务 3.持久化IO任务 | 里程碑切换任务 | 持久化预备任务 持久化IO任务 | |
里程碑切换任务 | 1.RAM事务消费任务 | 负载任务 |
基于流水线热度的L3负载均衡
异数OS为每一个TThread组分配一个节点IP,L3负载均衡需要根据TThread规模以及目标节点的流水线压力情况均衡的将用户请求派发到目标TThread组对应IP,L3负载均衡不需要解析L5业务,因此性能高成本低。
基于BitmapTree的KV索引存储
可以看出流水线并发无锁的前提是资源分组,因此在索引结构上需要考虑分区分组,一般基于B树或跳表的结构使用关键字做排序没有考虑资源分组,因此无法用于并发场景,所以老子罗盘使用BitmapTree来实现hash分组,这样就可以适用于并发流水线结构,BitmapTree通过自增连续索引实现插入操作,因此无法实现关键字排序的能力,删除操作会分为在线和离线来做处理,在线仅作Bitmap标记,离线需要根据Bitmap合并收缩,另外BitmapTree相对B树或跳表还具有无索引的优势,尤其是稠密情况下效率更高,一个2^36行的库BitmapTree仅需要24G索引结构,而B树需要280G索引,跳表如果不分类做大小索引,则会比B树高1个数量级,同时BitmapTree的索引结构不需要持久化,只需要运行时Cache预热生成,这大大的提高了可用性,降低IO需求。
异数OS-老子罗盘流水线调度方案
计划评估静态调度法
实现静态资源轮转表,使用静态模拟退火这种NP问题枚举器生成无冲突的二元资源向量流水线时间片,下表是其中4流水线8资源组的一个例子,每个资源组概率相同,当然你也可以根据离线资源组热点情况按照概率分布来生成,该方案的优点是不需要系统实时调度,生成流水线后放上去循环跑就行了,当然缺点是无法根据市场热点情况实时变更流水线热点布局,IO持久化负载没有热点优先调度依据,只根据RAM消费任务分布来形成互偶布局,因此理论上不能让流水线满载,在流水线较多时二元资源组规模爆炸会导致事务消费延迟大增,二元资源组事务队列缓存需求增加,L2 L3加速失败等问题。
1.RAM消费任务时间片
Row Name | 时间片-0 | 时间片-1 | 时间片-2 | 时间片-3 | 时间片-4 | 时间片-5 | 时间片-6 | 时间片-7 | 时间片-8 | 时间片-9 | 时间片-10 | 时间片-11 | 时间片-12 | 时间片-13 | 时间片-14 | 时间片-15 | 时间片-16 | 时间片-17 | 时间片-18 | 时间片-19 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Pipeline-0 | R(1,1) | R(7,4) | R(4,2) | R(5,1) | R(8,2) | R(3,2) | R(7,2) | R(8,7) | R(5,5) | R(1,1) | R(7,4) | R(4,2) | R(5,1) | R(8,2) | R(3,2) | R(7,2) | R(8,7) | R(5,5) | R(1,1) | R(7,4) |
Pipeline-1 | R(8,8) | R(5,2) | R(3,1) | R(8,4) | R(7,1) | R(7,6) | R(8,1) | R(4,3) | R(6,6) | R(8,8) | R(5,2) | R(3,1) | R(8,4) | R(7,1) | R(7,6) | R(8,1) | R(4,3) | R(6,6) | R(8,8) | R(5,2) |
Pipeline-2 | R(4,4) | R(6,1) | R(8,6) | R(7,3) | R(5,3) | R(8,5) | R(5,4) | R(6,5) | R(7,7) | R(4,4) | R(6,1) | R(8,6) | R(7,3) | R(5,3) | R(8,5) | R(5,4) | R(6,5) | R(7,7) | R(4,4) | R(6,1) |
Pipeline-3 | R(2,2) | R(8,3) | R(7,5) | R(6,2) | R(6,4) | R(4,1) | R(6,3) | R(2,1) | R(3,3) | R(2,2) | R(8,3) | R(7,5) | R(6,2) | R(6,4) | R(4,1) | R(6,3) | R(2,1) | R(3,3) | R(2,2) | R(8,3) |
2.互偶持久化IO任务时间片
Row Name | 时间片-0 | 时间片-1 | 时间片-2 | 时间片-3 | 时间片-4 | 时间片-5 | 时间片-6 | 时间片-7 | 时间片-8 | 时间片-9 | 时间片-10 | 时间片-11 | 时间片-12 | 时间片-13 | 时间片-14 | 时间片-15 | 时间片-16 | 时间片-17 | 时间片-18 | 时间片-19 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Pipeline-0 | R(5,5) | R(4,3) | R(3,2) | R(7,2) | R(5,1) | R(8,2) | R(4,2) | R(7,4) | R(2,2) | R(5,5) | R(4,3) | R(3,2) | R(7,2) | R(5,1) | R(8,2) | R(4,2) | R(7,4) | R(2,2) | R(5,5) | R(4,3) |
Pipeline-1 | R(6,6) | R(8,7) | R(7,6) | R(8,1) | R(8,4) | R(7,1) | R(3,1) | R(5,2) | R(4,4) | R(6,6) | R(8,7) | R(7,6) | R(8,1) | R(8,4) | R(7,1) | R(3,1) | R(5,2) | R(4,4) | R(6,6) | R(8,7) |
Pipeline-2 | R(7,7) | R(2,1) | R(4,1) | R(5,4) | R(6,2) | R(5,3) | R(8,6) | R(6,1) | R(8,8) | R(7,7) | R(2,1) | R(4,1) | R(5,4) | R(6,2) | R(5,3) | R(8,6) | R(6,1) | R(8,8) | R(7,7) | R(2,1) |
Pipeline-3 | R(3,3) | R(6,5) | R(8,5) | R(6,3) | R(7,3) | R(6,4) | R(7,5) | R(8,3) | R(1,1) | R(3,3) | R(6,5) | R(8,5) | R(6,3) | R(7,3) | R(6,4) | R(7,5) | R(8,3) | R(1,1) | R(3,3) | R(6,5) |
市场评估动态调度法
市场评估动态调度法在时间片结束时,请求调度器仲裁得到下一个时间片的二元资源组表,调度器需要根据N-1时间片的二元组资源事务队列的负载热度以及饥饿度做市场价值评估,使用动态退火TopK算法计算得到N+1时间片的无冲突二元资源组表,由于是N-1时间片的状态计算得到N+1的时间片,因此可后台覆盖计算延迟使流水线不因等待动态调度而阻塞,市场评估动态调度法可以动态剔除冷二元组资源,他不需要向计划调度法那样产生一个满足概率分布的流水线,只需要时间片内无冲突即可,因此他大大的减少了无解气泡的产生,这可以降低流水线级数,也因此提高流水线效率降低了事务消费延迟,有利于L2 L3加速,但市场评估调度法目前存在一些缺点,或许卷NP问题的大能可以解决,动态退火在TopK算法上还是有些压力的,当流水线规模增大时,二元资源组规模也极具增加,二元资源组规模为组合数C(2*n,n)+n,n是流水线规模,32流水线意味着需要2080个资源组,规划性能会大大降低,致使时间片不得不增大,较大的时间片意味着需要更多的队列缓存,并失去L2 L3的加速能力,当然可能可以通过N-K规划N+K实现多核并行规划的算法,但减小时间片的效果可能并不会很明显,因为缺口可能有1到2个数量级。
低成本持久化 惰性残差bitmap里程碑
里程碑写盘模式不会因为负载的多寡而使IO规模不确定导致过载,IO负载规模由里程碑的残差bitmap规模来决定,而流水线三态状态机决定了里程碑的发起是根据IO完成能力自适应发起的,因此无论IO规模多大,磁盘性能如何,都不会产生过载问题,另外使用影子行惰性完成机制可以使得流水线在完成持久化预备任务时无需一次性阻塞锁定拷贝残差行,并使得RAM事务消费任务可以与持久化任务并行无锁执行,里程碑生成使用一致性二元资源时间组来解决跨里程碑的半消费问题。
异数OS-老子罗盘资源轮转流水线的优点
- 无锁,硬件锁需要依赖总线,一般无法多核扩充锁性能,而时间片是逻辑上排序的无锁结构,因此没有效率约束,也无硬件体系结构约束(NUMA分级总线)因此没有性能约束以及特定硬件架构约束。
- 可根据流水线数量多核线性扩充性能,无性能天花板约束。
- 由于流水线时间片轮转所有资源组,因此可以实现不分区不分表,不需要负载均衡根据资源ID做定向节点请求,提高了可用性,局部节点宕机不导致局部资源不可用,也因此可以解决热点资源无法分载的问题,负载均衡可根据流水线负载情况将请求提交给不忙的流水线。
- 不需要实现高成本的日志系统,对于高频的交易平台,日志的成本是昂贵的,动辄数以百亿计,经常需要做日志版本合并快照管理工作,这对存储系统带来了巨大压力挑战,而在共享内存中做TPS业务,使用对偶资源写盘流水线策略则可以降低日志数量甚至不做日志存储,无论TPS业务量多大,磁盘IO都不会线性成长,同时也降低了存储成本,使用该策略可以使用传统硬盘代替SSD实现更大容量和更低成本,相比日志式存储系统,他的成本可以降低2到10个数量级,对,没错,可以是10个数量级。
- 使用惰性影子行方案,避免了不确定规模的副本IO带来的雪崩停机问题。
场合约束
- 要求请求之间没有顺序约束相关性,可以无序完成,如果有顺序约束,需要封装成事务在事务线程中阻塞完成。
- 需要冷热分离,资源组时间分片表静态,因此冷资源组的存在会降低流水线的利用率,需要定期根据热点资源生成新的资源组时间分片表。
- tps业务复杂度为可确定常数,在规定时间片内可常数时间内完成,否则会造成流水线阻塞失活。
- 只能二元tps业务,多元需要事务分拆为二元子tps业务。
- 不是流水线越多越好,流水线越多,需要的资源组越多,L2利用效率会降低,资源组热点也会降低,这会导致时间片不充盈的情况发生,降低时间片利用效率。
- 没有关键字查询排序业务要求,因为还没找到应对资源分组情况下的排序数据结构,BitmapTree+B+或跳表实现类似hashmap的方案可能可以应对关键字排序需求,但出于复杂原因没有深入考虑。
- 无冲突效率问题,二元事务在并发是有冲突传染问题,致使系统悲观退化到串行化,但并行流水线反而不关心冲突传染问题,二元事务的并发规模越高流水线效率越高。
性能
硬件 e5v1 2670 8核洋垃圾+mcx455a,使用市场评估动态调度法,异数OS虚拟网卡交换机下,所有资源组均衡请求情况下,每流水线64链接线程池性能600万tps左右,8流水线 4800万tps,在异数OS+mcx455a 100G网卡下,模拟50ms互联网延迟请求每流水线50万链接线程池,总计400万链接,交易性能280万tps,8流水线2240万tps,换算成tpmC 13.4亿, 每核1.68亿tpmC, 对比oceanbasev2 1557台服务器,每服务器84核,tpmC 7.07亿,每核0.54万tpmC,性能提高3.1万倍,相当于4到5个数量级,实现了从2个数量级到4个数量级蜕变的承诺。
为什么只适用与异数OS
罗盘时间片资源轮转流水线看似已经很完美的解决问题,但实际上他只能在异数OS上实现,脱离了异数OS,则只能躺在教科书中,虽然本文介绍的方案大部分都可以在教科书中找到,但教科书中的东西未必就可以在实际攻城中真正使用起来,由于1998百兆服务器操作系统基础理论限制,在传统bug操作系统下,TThread 的并发性能以及IOPS存在严重瓶颈限制,因此在bug操作系统下,无论是类Redis方案还是分布式系统方案都将面临无法逾越的1998百兆服务器基础理论挑战而无法使用该方案。
linux 等bug操作系统下的类redis无法使用该方案
罗盘时间片资源轮转流水线 二元资源组AB事务队列可以运行在CPU的本地L2上,因此每核可以达到100M TPS以上,且能多核线性扩充算力,所以关键要看二元资源组AB事务队列的入队性能,他由TThread 组的并发性能和操作IOPS决定,TThread是用户会话业务线程,由于互联网延迟的存在(100ms),每会话最多10TPS左右,因此要满足100M TPS的需求则需要1000万用户会话线程,因此操作系统的并发链接数量和iops性能决定着罗盘时间片资源轮转流水线性能的天花板瓶颈,一般在linux这种bug操作系统上,tcpip的iops仅10万,这仅能满足1万链接每户10TPS或者10万链接每户1TPS的需要,在配合时间片资源轮转流水线性能缺口达到99.9%,iops性能需求缺口差距3个数量级,如果是100核CPU,则iops性能需求缺口5个数量级,因此如果操作系统的iops受限,则单线程的redis依然是bug操作系统下的最高效解决方案,使用时间片资源轮转流水线不仅不会有效率提升,还会引入场景约束问题。
分布式系统无法使用该方案
你可能试图使用分布式来实现该方案,但分布式则意味着需要面对两个无法解决的问题。
- 需要寻找可靠高性能的多副本TPS方案,代替基于共享内存实现的无锁无副本的二元组资源事务队列,当然这是不可能找到的,即便使用异数OS。
- 需要寻找无副本局部分区分表方案,这意味着二元资源组需要根据资源ID定向到分区节点,这需要配置L5负载均衡(L5负载均衡性能通常比L3差4个数量级),这导致负载均衡成为性能瓶颈,流水线有可能因为冷热布局变化无法动态分载而退化到单流水线,并且会因局部宕机降低系统用可用性。
- 分布式的流水线时间片性能退化,一个32并发的流水线需要256级以上流水线,在linux这类bug操作系统下,IO延迟通常在1ms级别,时间片分布式同步需要2PC 4RTT,因此最小时间片一般需要5ms,256级流水线意味着请求的最差延迟情况将达到1.3s,平均也需要650ms,对于用户来讲这可能是无法接受的,而在异数OS操作系统下,分布式时间片根据网卡性能4RTT可以做到10us,在非分布式情况下可以做到1us以下,这将大大降低流水线延迟,较大的时间片还意味着较大的二元组资源事务队列,这会降低L2 L3的效率致使流水线无法利用L2 L3加速,性能也会下降1到2个数量级,增加访存负担。
难以负重的KV Cache
虽然本方案使用里程碑方案可以大大降低磁盘写需求,但并不能应对所有场合,tpmC测试包括新建订单和消费订单,和消费订单(Update)只需要内存消费里程碑写盘不同,新建订单(Insert)需要每笔都写入磁盘,假设每订单64字节,13.4亿tpmC,则12分钟测试将产生160.8亿条订单1029GB订单数据和2500万iops的IO写需求,这对于LRU缓存基础上的本地SSD以及linux这类bug操作系统显然无法应对,因此本文的KV Cache系统是建立在异数OS 水桶 RAM网盘基础之上的,异数OS 水桶 RAM网盘实现8个分区,每分区提供350万iops的读写能力和128G磁盘缓冲以完成12分钟的订单写任务,而水桶 RAM网盘的客户端由于iops限制,是无法在linux 这类bug操作系统上实现的。