当前位置: 首页 > news >正文

违法网站开发人员代码外包平台

违法网站开发人员,代码外包平台,项目计划书封面设计,信专业广州网站建设KV 存储作为美团一项重要的在线存储服务#xff0c;承载了在线服务每天万亿级的请求量#xff0c;并且保持着 99.995% 的服务可用性。在 DataFunSummit 2023 数据基础架构峰会上#xff0c;我们分享了《美团大规模 KV 存储挑战与架构实践》#xff0c;本文为演讲内容的整理… KV 存储作为美团一项重要的在线存储服务承载了在线服务每天万亿级的请求量并且保持着 99.995% 的服务可用性。在 DataFunSummit 2023 数据基础架构峰会上我们分享了《美团大规模 KV 存储挑战与架构实践》本文为演讲内容的整理。文章主要分为四个部分第一部分介绍了美团 KV 存储发展历程第二部分分享了内存 KV Squirrel 挑战和架构实践第三部分阐述了持久化 KV Cellar 挑战和架构实践最后一部分介绍了未来的发展规划。希望这些内容能对大家有所帮助或启发。 1 美团 KV 存储发展历程 2 大规模 KV 存储的挑战 3 内存 KV Squirrel 挑战和架构实践 3.1 Squirrel水平扩展的挑战 3.2 Gossip优化 3.3 Squirrel 垂直扩展的挑战 3.4 forkless RDB 3.5 工作多线程 3.6 Squirrel可用性的挑战 3.7 两机房容灾 3.8 跨地域容灾 3.9 双向同步冲突自动解决 4 持久化 KV Cellar 挑战和架构实践 4.1 Cellar垂直扩展的挑战 4.2 Bulkload 数据导入 4.3 线程调度模型优化 4.4 线程RTC模型改造 4.5 内存引擎无锁化 4.6 Cellar可用性的挑战 4.7 双向同步冲突自动解决 5 发展规划和业界趋势 1 美团 KV 存储发展历程 上图就是美团第一代的分布式 KV 存储的架构可能很多公司都经历过这个阶段。在客户端内做一致性哈希然后在后端部署上很多 Memcached 实例这样就实现了最基本的 KV 存储分布式设计。但这样的设计存在很明显的问题比如在宕机摘除节点会时丢失数据此外在缓存空间不够需要扩容时一致性哈希也会丢失一些数据这样会给业务的开发带来很大的困扰。 随着 Redis 项目的成熟美团也引入了 Redis 来解决我们上面提到的问题进而演进出来上图这样一个架构。可以看到客户端还是一样使用一致性哈希算法在服务器端变成了 Redis 组成的主从结构。当任何一个节点宕机我们可以通过 Redis 哨兵完成 failover实现高可用。但有还一个问题还是没有解决如果扩缩容的话一致性哈希仍然会丢失数据。 这时我们发现业界有一个比较成熟的开源 KV 存储也就是阿里巴巴的 Tair 。2014年我们把 Tair 引入到技术内部去满足业务 KV 存储方面的需求。Tair 开源版本的架构主要是三部分最下边的是存储节点存储节点会上报心跳到它的中心节点中心节点内部设有两个配置管理节点会监控所有的存储节点。如果有任何存储节点宕机或者扩容之类的行为它会做集群拓扑的重新构建。客户端启动的时候它会直接从中心节点引入一个路由表这个路由表简单来说就是一个集群的数据分布图客户端根据路由表直接去存储节点读写。之前我们 KV 遇到的扩容丢数据问题它也有数据迁移机制来保证数据的完整性。 但是在使用的过程中我们还遇到了一些其他问题比如它的中心节点虽然是主备高可用的但它没有分布式仲裁之类的机制所以在网络分割的情况下它是有可能发生“脑裂”的这种情况也给我们的业务造成过比较大的影响。在容灾扩容的时候遇到过数据迁移影响业务可用性的问题。 另外我们之前用过 Redis 业务会发现 Redis 的数据结构特别丰富而 Tair 还不支持这些数据结构。虽然我们用 Tair 解决了一些问题但是 Tair 同样也无法完全满足我们的业务需求。于是我们认识到在美团这样一个业务规模大、复杂度高的场景下很难有开源系统能很好满足我们的需求。所以我们决定在已应用的开源系统之上进行自研。 时值 2015 年 Redis 社区正式发布了它的集群版本 Redis Cluster。所以我们紧跟社区步伐并结合内部需求做了很多自研功能进而演进出本文要介绍的全内存、高吞吐、低延迟的 KV 存储 Squirrel。另外我们基于 Tair加入了很多美团自研的功能演进出本文要介绍的持久化、大容量、数据高可靠的 KV 存储 Cellar 。 Redis 社区一直都很活跃所以Squirrel 的迭代是自研和社区并重自研功能设计上也会尽量与社区架构兼容。Tair 开源版本已经多年没有更新所以Cellar 的迭代完全靠自研。后续内容上大家也能看到因为这方面的不同Cellar 和 Squirrel 在解决同样问题时可能会选取不同的方案。 这两个存储其实都是 KV 存储领域的解决方案。实际应用上如果业务的数据量小对延迟敏感建议用 Squirrel 如果数据量大对延迟不是特别敏感我们建议用成本更低的 Cellar 。 2 大规模 KV 存储的挑战大规模 KV 存储的业务挑战主要有两点 一个是扩展性。随着业务规模持续变大业务会要求使用容量更大的集群。这个容量包括两方面一方面是数据量还有一方面是调用量。扩展容量最常见的方法就是把集群水平扩展到更多的节点但是当集群节点数达到一定规模后再想扩展新节点也会遇到很多困难这是扩展性上的第一个挑战。 还有一个问题是有些业务场景的调用容量是无法随着集群水平扩展而扩展的。比如很多业务会使用 mget 进行批量读取。但随着集群节点数的增加由于“木桶效应”整个 mget 请求的长尾延迟会越来越高进而导致服务的请求超时率持续上升。等集群达到一定规模之后长尾延迟造成的可用性降低就超出业务的承受能力了。所以在水平扩展之外我们还需要解决好节点垂直扩展上的挑战来支持这种批量操作的业务场景。 另一个是可用性。随着集群规模变大要保证可用性维持在与小规模集群同等的水平其实是很困难的。但业务服务却不会因为集群规模变大而能接受可用性有所降低。所以美团的挑战是如何保证集群可用性不会随着规模的变大而有所降低。 3 内存 KV Squirrel 挑战和架构实践 上图是美团的 Squirrel 架构。中间部分跟 Redis 社区集群是一致的。它有主从的结构Redis 实例之间通过 Gossip 协议去通信。我们在右边添加了一个集群调度平台包含调度服务、扩缩容服务和高可用服务等它会去管理整个集群把管理结果作为元数据更新到 ZooKeeper。我们的客户端会订阅 ZooKeeper 上的元数据变更实时获取到集群的拓扑状态直接对 Redis 集群节点进行读写操作。 | 3.1 Squirrel水平扩展的挑战 但是基于 Redis Cluster 架构的水平扩展会有如下问题 一个是 Gossip 的消息通信量是节点数的平方随着集群节点数的增加Gossip 通信的消息量会急剧膨胀。比如我们实测对于一个 900 节点的集群Gossip 消息的 CPU 消耗会高达12%远高于小集群的 Gossip 资源消耗这样会造成极大的资源浪费。 除了资源的浪费以外Gossip 消息过多也会更多抢占用户请求处理线程的资源进而会导致用户请求经常被 Gossip 消息的处理所阻塞再导致用户请求产生更多的超时影响服务可用性。 | 3.2 Gossip优化 为了解决上述的扩展性问题我们对社区的 Gossip 方案进行了优化。首先针对 Gossip 传输的消息我们通过 Merkle Tree 对其做了一个摘要把集群 Gossip 通信的数据量减少了90%以上。服务端节点仅需要对比 Hash 值即可判断元数据是否有更新对于存在更新的情况也能快速判断出更新的部分并仅对此部分元数据进行获取、更新大幅降低了 Gossip 消息处理的资源消耗。同时我们还增加了一个周期性的元数据全量同步功能来解决可能因 Hash 冲突导致元数据无法更新的问题。 针对上述提到的 Gossip 消息处理影响业务请求的问题我们把 Gossip 消息处理功能剥离到一个单独的心跳线程里并且由心跳线程来更新集群拓扑的元数据。对于处理用户请求的工作线程仅需要对元数据进行读操作可以做到无锁读。这样的话Gossip 请求处理就对业务请求完全没有影响了。 | 3.3 Squirrel 垂直扩展的挑战 对基于 Redis 研发的 Squirrel 来说垂直扩展会存在如下问题 首先是数据容量的问题。对一个内存存储来说节点容量过大的话很容易影响服务的可用性。例如在主从节点要做数据同步时Redis 节点需要通过 fork 产生子进程来生成全量数据的 RDB 快照。当一个 8GB 的节点做 fork 调用时会由于页表项过多造成进程出现 500 毫秒的阻塞。对于平均耗时只有几毫秒的 KV 请求来说这 500 毫秒的阻塞会造成大量的超时。 还有就是处理量的扩展问题。虽然我们可以通过加从库去扩展集群的读能力上限但主库的写处理能力却还是无力扩展的。而且受限于主库的处理能力和机器带宽限制加从库来扩展读能力也是有上限的。 | 3.4 forkless RDB 针对上述节点过大fork 生成 RDB 会导致可用性降低的问题。我们实现了 forkless RDB 方案这是一个不基于 fork且不会中断服务的生成数据快照 RDB 的方案。 如上图所示forkless RDB 的生成期间它首先会停止哈希表的 rehash 过程避免数据在哈希表之间的搬迁影响快照的一致性。然后它会从头开始对整个哈希表的 key 做迭代每迭代一个 key 就会把它 dump 一份出来放到复制队列里边。在迭代 key 的同时它会对迭代的位置记录一个游标。 如果在迭代哈希表的过程中里面的 KV 有变更的话在这个游标之前的  KV 变更也会把它放到复制队列里边确保已经复制的 KV 能够持续获得后续的变更。如图所示RDB 游标在 key 3它会把之前已经迭代过的 key 1 更新、key 2 删除操作也插入到复制队列里边。在游标之后的 key因为还没有做数据复制所以等后续迭代到这个 key 时把其最新值 dump 到复制队列就好。通过这样的方式就实现了一个不需要 fork 就能获得一个一致性数据快照 RDB 的过程。 这个方案的优点很明显生成 RDB 的过程不会阻塞服务请求处理并且因为是实时的发送一个个 KV 数据所以就不需要等 RDB 生成好就可以向从库复制数据了大幅提升了数据同步的速度。但因为全量数据迭代、复制是在工作线程去做的而不是在子进程内。所以该方案会占用一部分工作线程的资源。另外因为是以 KV 为粒度做复制的所以如果哈希表里面有大 KV 的话可能会因为工作线程复制大 KV 耗时过长造成用户请求等待耗时的上升。 | 3.5 工作多线程 对于处理量的扩展社区有一个 IO 多线程的解决方案。但这个 IO 多线程只是把网络收发部分做了多线程处理所以其扩展能力是比较有限的。比如 4个 IO 线程下它只能把整体的吞吐提升一倍就到极限了。而且因为此时工作线程已经到瓶颈了再往上去加 IO 线程不仅无法提升性能反而会消耗更多的 CPU 资源。对此我们的解决方案是工作多线程也就是说把请求处理的过程也多线程化。 如上图所示在工作多线程方案下每个线程都会去处理请求并且每个线程会完成从收包到请求处理然后到发包的整个过程是一个 Run-to-Completion 线程模型。相比 IO 多线程它会减少很多线程切换节省很多的 CPU 资源。同时对于请求处理的过程我们也通过细致的梳理尽量缩小了临界区的范围以保证大部分的请求处理过程是在临界区之外的来提升处理并发度。 如果一个工作线程需要加锁的话它会先 try lock。如果加锁成功就继续执行了但如果加锁失败的话这个工作线程也不会阻塞等锁。它会先去注册一个管道的通知消息然后就继续处理网络的收发包还有非临界区的请求了。等到锁被释放的时候这个工作线程会通过 epoll 获得管道里面的锁释放通知然后去拿到这把锁。这个时候它就可以去处理临界区的请求操作了。 这样的话在整个加锁、解锁的过程中工作线程没有任何阻塞仍然可以继续做网络收发、非临界区请求的处理获得最大限度的处理能力。另外对于新建 socket、数据复制等工作跟工作线程的耦合很低我们将其放到了单独的线程去执行以尽量降低工作线程的负载。 通过实测工作多线程方案的吞吐比社区 IO 多线程提升了 70%相对于社区单线程提升 3 倍多。 | 3.6 Squirrel可用性的挑战 基于 Redis Cluster 的大规模集群可用性挑战主要是维持机房容灾部署很困难。如上图所示由于 Redis Cluster 是去中心化的架构所以部署上要求至少是三机房分布以此来保证任何一个机房挂掉的时候剩余的两个机房仍然能有过半的节点来选出新的主节点。比如一个上千节点的集群要扩容的话可能需要几百个分布在三个机房的节点一时之间其实很难凑齐这么多机房的资源。而当业务大促容量需求很急时我们有时候只能牺牲机房容灾能力来满足业务的容量需求。 还有在成本方面对于一些数据可靠性要求较低的业务只需要两副本冗余就够了极端情况下丢一点数据也是可以接受的。但受限于容灾要求这些业务也只能使用三机房三副本部署从成本角度考量很不划算。 | 3.7 两机房容灾 受 Google Spanner 的见证者节点启发我们在 Squirrel 集群也引入了见证者节点角色。同 Spanner 一样Squirrel 见证者节点也不会存储数据所以它无法作为正常的主从库提供请求处理能力也不能发起选主投票。但见证者节点可以在集群选主时参与投票帮助存活的机房节点完成过半选主过程。 见证者节点还可以设置权重这样只需要一个或几个高权重见证者节点就能满足一个大规模集群的容灾部署需求了。由于见证者节点不存储数据且节点数很少虽然集群还是三机房部署但实际几乎只需要两机房的资源就能满足机房容灾部署需求了这样就大幅降低了集群维持容灾部署的难度从而节省大量的机器成本。 | 3.8 跨地域容灾 Squirrel 跨地域容灾的架构如上图所示它通过一个集群间同步服务在两个不同地域的集群之间做数据同步。这个同步服务首先伪装为上游集群节点的 slave 把它的 RDB 和增量 log 拉取过来然后再把拉取到的数据转化成写请求发到下游的集群从而实现了一个集群间的数据同步。通过这样的架构我们解决了服务的跨地域容灾问题。并且通过在集群间搭建正反两个方向的两个同步任务就能实现集群间的双向同步。这样的话用户服务就可以只在本地域写但同时能读到两个地域分别写入的数据解决了单向同步需要跨地域写的问题。 双向同步有两个经典问题需要解决 一个是循环复制问题。我们为每个 Squirrel 集群标记了不同的 cluster id并且记录了每个 KV 的初始写入 cluster id同步服务会过滤掉与目标集群 cluster id 相同的数据以避免发生循环复制。 还有一个是数据冲突问题。我们一开始是通过业务层面保证在每个地域写不同的 Key 来解决的。但是在双向同步的运行过程中还是会有一些极端场景可能会出现两个地域并发写同一个 Key。比如像机房网络故障场景业务会把故障机房的所有写入都切到正常机房。 但由于我们的集群间复制是异步的可能故障机房有一些最新的 Key 变更还没有复制到正常机房的集群。而如果在业务将写切换到正常机房后又写入了相同 Key 的不同变更就会产生两个同步集群的数据冲突。在机房网络恢复之后业务还是要把一部分流量切回到之前故障的集群上恢复到跨地域容灾的架构。 但由于两个集群可能已经有数据冲突了所以在业务切回之前就需要对数据做冲突校验和修复。但是对大数据量集群来说数据校验和修复的耗时可能会长达数天。在这样长的时间内只有一个单地域集群来支撑业务无论是从容灾还是容量的角度来看都是有较大风险的。 | 3.9 双向同步冲突自动解决 为了解决上述的双向同步数据冲突问题我们实现了一个基于数据写入本地时间的 last write win 冲突自动解决功能。 如上图所示在 T1 时刻 Key money 的值在 A、B 两个集群都是 100。T2 时刻money 的值在 A 集群更新成了 120。但是在 A 集群的新值还没复制到 B 集群的时候B 集群在 T3 时刻把 money 的值更新成了 130。这时候 A、B 集群会互相向对方复制各自写入的新值A 集群收到 B 集群的值 130 后会发现 B 集群 money 的更新时间大于自己T3 T2它就会更新自己的 money 值为 130B 集群也会收到 A 集群复制过来的 money 值 120但它会发现这个值的更新时间小于自己本地值的更新时间T2 T3就会忽略这个复制请求。通过这样一个基于更新时间的 last write win 策略就可以达到最终一致性。 上述方案看起来简单但是在复杂、大规模的业务场景下还有很多问题要处理所以我们还做了以下的工作 保存最近更新的时间戳当发生时钟回退时我们会继续使用自己保存的时间戳避免使用本地回退的时间导致数据也跟着发生了回退。PS对于时钟回退问题我们调研过最新的 NTP 时钟同步不会像以前一样造成本地时钟的回退或跳变现在它通过把时钟 tick 调快或调慢来完成类似的调整所以前述关于时钟回退的解决方案在最新的 NTP 同步机制下就不是必要的了。不过为了保证我们的服务在任何系统下都能正常运行我们最终还是实现了这个功能。 记录写入数据的集群 id我们会为所有写入的 Key 保存写入的集群 id。当两个值的更新时间相同时我们会比较集群 id如果也相同我们就知道是同一个集群先后写入但获取到相同本地时间的数据会允许其写入如果不同我们仅会让集群 id 更大的值写入来保证数据最终一致性。 由复制操作改为复制变更后的数据像 INCR 类接口A 集群的 money T1 时刻通过 INCRBY money 20 变成了 120然后 B 集群 T2 时刻通过 INCRBY money 30 变成了 130。A 集群收到 B 集群的复制时因为时间戳比自己的本地值大它会执行 INCRBY money 30 变成 150然后 B 集群收到 A 集群的复制时因为时间戳比自己的本地值小它会把这个复制请求给忽略掉就造成了数据冲突。针对这个问题我们将所有操作的数据复制都改成了复制操作后的数据而不是这个操作本身来解决类似 INCRBY 这种接口的数据冲突问题。 保存最近删除的 Key像删除类接口A 集群 T2 时刻写入了 money120然后 B 集群在 T3 时刻删除了 money 这个 Key。A 集群收到 B 集群的复制时由于其时间戳比本地值大A 会把数据删了但 B 集群收到 A 集群的复制时由于本地已经不存在 money 这个 Key 了它就会把 money 当做一个新 Key 进行写入就造成了数据最终不一致。针对这个问题我们通过保存最近一段时间删除掉的 Key 及删除时间戳以便在删除集群收到对端复制过来的旧 Key 时进行甄别。 4 持久化 KV Cellar 挑战和架构实践 上图是我们最新的 Cellar 架构图它跟阿里开源的 Tair 主要有两个层面的不同。 第一个是 OB第二个是 ZooKeeper。我们的 OB 跟 ZooKeeper 的 Observer 是类似的作用提供 Cellar 中心节点元数据的查询服务。它实时的与中心节点的 Master 同步最新的路由表客户端的路由表都是从 OB 去拿。这样做的好处主要有两点第一把大量的业务客户端跟集群的大脑 Master 做了隔离防止路由表请求影响集群的管理第二因为 OB 只提供路由表查询服务不参与集群的管理所以它可以水平扩展极大地提升了路由表的查询能力。 第二个是我们引入了 ZooKeeper 做分布式仲裁解决了上述提到的 Master、Slave 在网络分割情况下的“脑裂”问题。并且通过把集群的元数据存储到 ZooKeeper从而提升了元数据的可靠性。 | 4.1 Cellar垂直扩展的挑战 在 Cellar 架构下不存在水平扩展的问题但与 Squirrel 一样它也有垂直扩展方面的挑战。而由于 Cellar 是持久存储它也很少遇到单机数据容量的问题而要解决的问题主要是处理容量的垂直扩展。而且由于 Cellar 是持久化引擎、多线程模型它要解决的处理容量扩展问题也是不一样的具体如下 引擎读写能力的不均衡性Cellar 是基于 LSM-Tree 引擎模型的持久化存储这种引擎的多 Level compaction 会导致写放大问题进而会造成其写处理能力比读低很多。所以在一些写相对较多的场景机器资源虽然还有空闲但写处理能力却已经到瓶颈了。 线程间同步的开销想要提升处理容量就需要增加线程数。而随着线程数的增加线程间同步的开销在整个服务的 CPU 使用占比也会越来越高。所以如果解决不好线程间同步的问题想单纯地增加线程数来提升处理容量行不通。 | 4.2 Bulkload 数据导入 对于上述提到引擎写压力达到瓶颈的集群我们调研后发现其在线的实时写入一般都是比较少的高写入量主要是用户从离线批量写数据到线上 Cellar 集群带来的。基于此我们开发了 Bulkload 数据导入能力来解决这个问题。 Bulkload 整体架构如上图所示它在普通写入流涉及的客户端和存储节点之外还引入了 S3 对象存储来做导入数据的中转。下面我们看下 Bulkload 具体的写入流程Bulkload 首先会在客户端进程内生成分片内有序的数据文件并写到本地硬盘上。等客户端的数据文件写好之后它会上传到对象存储利用对象存储做数据文件的中转解决了客户端与服务端之间直传大文件容易失败的问题。 分片 1 的数据文件写入到对象存储之后客户端会将数据文件的存储地址告诉分片 1 的主所在的存储节点 DS1。然后 DS1 就会从对象存储下载分片 1 的数据文件并把它直接插入到 LSM-Tree 引擎里面。因为这是一个完整的文件插入所以它可以消除引擎在普通写入时的内存排序和刷盘压力。同时因为这个文件的数据是分片内有序的所以它在参与 Level 间 Compaction 时会与其他的引擎文件交叉很少可以大幅减少多 Level compaction 的压力。 然后 DS1 会把分片 1 数据文件的对象存储地址复制发送到分片 1 的从所在的存储节点 DS2 。因为存储节点的复制只是传输数据文件的地址所以复制速度是特别快的也节省了很多传输的带宽。DS2 收到了分片 1 的地址后同样会从对象存储下载数据文件并插入到引擎里面。 通过 Bulkload 解决方案我们整体把数据离线导入的性能提升到旧版的 5 倍。比如我们的一个存储广告特征的客户使用 KV 方式从离线导数据到在线需要 14 小时受限于在线高峰期无法导数据如果需要继续增加特征数据就需要扩容集群了。而扩容集群一方面会因为“木桶效应”导致请求长尾延迟问题另一方面 Cellar 成本的上升也会抵消一部分广告收益。而在 Bulkload 功能加持下该客户导入相同规模数据仅需不到 3 小时它可以在不增加 Cellar 资源的情况下将广告特征规模增加数倍大幅提升了广告的效果。 | 4.3 线程调度模型优化 我们最初的线程模型与开源版 Tair 一样网络线程池做收发包收到的包经过一个队列转出到一个大的工作线程池做请求处理。这样的线程模型很容易发生请求间的互相影响。比如用户有离线数据导入到 Cellar 的时候就很容易导致在线读请求的超时。又比如当有大 Value 读写的时候工作线程处理会比较慢、占用线程的时间会很长导致正常 Value 读写的快请求只能在队列等待进而导致大量超时。 所以为了隔离在离线请求、快慢请求的处理让服务资源优先保证核心流量的处理我们后来把线程模型改造成如上图所示的 4 个队列 4 个线程池的结构将请求分成 4 类读快、读慢、写快、写慢分别放到不同的队列和线程池去处理进而来提升服务核心流量的可用性。 但是工作线程池按照请求类型分离之后带来一个问题就是不同业务场景、甚至同一业务的不同时段不同类型请求量的占比是不一样的。所以给每个线程池分配多少线程是一个很棘手的问题。 针对这个问题我们增加了一个线程动态调度的逻辑每个线程池都有一部分线程被设定为可共享线程如果线程池比较空闲共享线程就会去轮询其他的队列处理一些繁忙线程池的请求这样就达到了自适应调整各线程池资源的效果。但是在这样的架构下虽然解决好了请求隔离性和不同请求类型线程资源的动态分配问题但我们发现随着节点流量的上涨共享线程对于其他队列的轮询会消耗越来越多的 CPU 资源而且集群业务的负载分布与默认的线程数设置差异越大这个消耗的占比也会越高。 为了解决上述线程池资源自适应调度带来的 CPU 消耗问题我们对分离后的线程、队列模型做出了如上图的改造。改进后的线程模型最主要的特点是引入了一个调度线程和一个空闲线程池这个调度线程会实时统计每个线程池的负载来评估每个线程池是否需要增加或减少线程并做出调度动作空闲线程池用来存放当前空闲的可用于调配的线程资源。 当调度线程评估后决定做线程资源调配时它就会发送调度指令到相应队列中当线程池里的线程获取并执行了这个指令后就实现了线程资源的调配。比如它想给读快线程池增加线程就会给空闲线程池的队列发送一个调度指令空闲线程池的线程取到这个指令后就会将自己加入到读快队列的线程池里面去处理读快队列的请求。 当调度线程想对读慢线程池调减线程时它会向读慢队列发送一个调度指令读慢队列的线程获取到这个指令后就会离开读慢线程池加入到空闲线程池。通过调度线程准实时的毫秒级负载统计、调度我们实现了线程池资源的快速动态分配。对于每一个线程池的共享线程也不再需要去轮询其他线程池的队列了只需要专心处理自己队列的请求即可大幅降低了线程池资源调度的 CPU 消耗。 通过上述的线程队列模型优化服务在高负载场景下可以提高 30% 以上的吞吐量。 | 4.4 线程RTC模型改造 上图左侧画的是我们服务请求的 IO 处理路径一个请求的处理流程会经过网络线程、请求队列、工作线程、内存和硬盘引擎。这个设计的问题是请求在不同线程之间流转会造成大量的 CPU 切换以及 CPU 高速缓存的 Cache Miss进而造成大量的 CPU 资源消耗。在大流量场景下这样的 CPU 消耗也是很可观的一笔资源。 针对这个问题我们对线程队列模型又做了如上图右侧所示的改造。新的模型下我们让网络线程直接去做读请求的处理对于能够命中内存引擎的读请求其处理模型就是一个 RTCRun-to-Completion模型。 具体来讲当网络线程收到一个请求之后会先判断是否为一个读请求如果是就会直接去读内存引擎。我们服务的内存引擎会缓存硬盘引擎上的热点数据如果内存引擎命中的话网络线程就可以直接返回结果给客户端。这样在网络线程内就实现了请求的闭环处理相比原来的模型可以去除所有因请求流转造成的 CPU 资源消耗。而对于写和读未命中内存引擎的请求仍然需要经过原来的请求处理路径去硬盘引擎读或者写数据。 新的线程模型经实测在 80% 内存引擎命中率场景下服务读吞吐可以提升 30%。虽然新的线程队列模型只实现了读缓存命中请求的 RTC但其实在线流量大多都是读多写少且热点数据明显、内存引擎命中率比较高的场景所以新模型上线后在大多数的业务集群都取得了明显的性能提升。 | 4.5 内存引擎无锁化 当单机请求量达到了一定规模之后我们发现服务内的锁操作会占用很多的 CPU 资源。经分析发现大多数的锁操作都发生在上节内容提到的内存缓存引擎上。如上节所述所有请求都会经过内存引擎且大部分请求都会在内存引擎命中并返回结果给客户端。所以大部分请求都是纯内存处理这个过程中的锁操作就很容易成为瓶颈。针对这个问题我们对内存引擎做了无锁化改造其改造后的结构如下图所示 整体改造主要跟上图的 HashMap 和 SlabManager 两个数据结构有关其他数据结构在图中已略掉。HashMap 是存储 KV 数据的核心结构它把 Key 通过 Hash 算法散列到不同的 Slot 槽位上并利用链表处理 Hash 冲突SlabManager管理不同尺寸内存页的申请和释放它利用链表把相同尺寸的内存页放到一起管理。 对于 HashMap我们做了单写多读的无锁链表改造。同时通过引入 RCU 机制实现了异步的内存回收解决了读请求与写请求内存释放操作的冲突实现了读请求处理全程的无锁化。写请求虽仍需要加锁但我们对写做了锁粒度的优化可以大幅提升并发度。比如我们把 SlabManager 的访问由一把大锁改成每个内存尺寸的管理链表单独一把锁这样在分配和释放不同尺寸内存页的时候就可以实现并发。同时 RCU 机制下的内存异步回收也解决了写线程回收内存时可能被阻塞的问题进一步提升了写性能。 内存引擎通过无锁化加 RCU 技术的改造读处理能力提升了 30% 以上。 | 4.6 Cellar可用性的挑战 同 Squirrel 一样Cellar 也通过建设集群间数据同步能力实现了跨地域的容灾架构。不同的是Cellar 因为是自研无需考虑与社区版本的兼容性同时为了简化部署结构、降低运维成本它把集群间数据同步功能做到了存储节点内部。如上图示例的北京集群 A 节点、上海集群 H 节点在接收到写入之后除了要做集群内的数据同步以外还需要把写入数据同步到跨地域的另一个集群上。 Cellar 也可以通过配置两个方向的跨集群数据同步链路实现完全的本地域读写。Cellar 由于采用了存储节点内建的方案它的集群间复制通过使用定制的复制包来甄别客户写入和复制写入并只为客户写入生成复制 log 来避免循环复制相对Squirrel 会简单一点。但同样的这种架构也会遇到极端情况下双向同步导致的数据冲突问题。 | 4.7 双向同步冲突自动解决 如上图所示Cellar 也实现了类似 Squirrel 的基于数据写入本地时间的 last write win 冲突自动解决方案。但 Cellar 的方案有一点区别是它没有通过在每条数据记录 cluster id 的方式解决时钟回退、两次变更写入的本地时间相同的问题而是引入了 HLCHybrid Logic Clock时钟来解决这个问题。 因为 HLC 可以保证每个集群写入数据的时钟是单调递增的。所以接收端是不用担心对端复制过来的数据有时间戳相同的问题。而对于两个集群分别写入时间戳相同且 HLC 的逻辑时钟刚好也相同的情况可以通过比较集群配置的 cluster id不会存储到每条 KV 数据内来决定最终哪个数据可以写入。 5 发展规划和业界趋势 未来根据技术栈自上而下来看我们的规划主要覆盖服务、系统、硬件三个层次。 首先在服务层主要包括三点 第一Squirrel Cellar 去 ZK 依赖。如前所述Squirrel 集群变更到客户端的通知是依赖 ZK 来实现的Cellar 的中心节点选主和元数据存储也是依赖 ZK 实现的。但 ZK 在大规模变更、通知场景下它的处理能力是无法满足我们的需求的很容易引发故障。所以Squirrel 会去掉对 ZK 的依赖改为使用公司内的配置管理、通知组件来实现集群变更到客户端的通知。Cellar 会通过在中心节点间使用 Raft 协议组成 Raft 组来实现选主和元数据多副本强一致存储注本文整理自 DatafunSummit 2023 演讲此工作当前已完成开发处于灰度落地阶段。 第二向量引擎。大模型训练、推理场景有很多向量数据存储和检索需求业界很多 NoSQL、SQL 数据库都支持了向量引擎能力。KV 存储作为高性能的存储服务如果支持了向量引擎可大幅提升大模型训练、推理的效率。 第三云原生。当前美团的 KV 服务规模很大相应的运维成本也比较高。所以我们计划做一些服务云原生部署、调度方面的探索向更高运维自动化水平迈进。 其次是系统层计划对 Kernel Bypass 技术做一些探索和研发落地比如新版内核支持的 io_uring、英特尔的 DPDK、SPDK 技术等。由于 KV 存储是典型的高吞吐服务它的网络 IO、硬盘 IO 压力都很大Kernel Bypass 技术可以大幅提升服务的 IO 能力降低访问延迟和成本。 最后是硬件层计划对计算型硬件的应用做一些探索比如配备了压缩卡的 SSD可以将服务引擎层使用 CPU 做的数据压缩工作卸载到压缩卡上释放出 CPU 资源做更高价值的计算工作。KV 服务是典型的低延迟、高网络负载的服务。所以我们也计划对 RDMA 网络做一些探索以期进一步降低服务访问延迟、提升网络处理能力。 6 本文作者 泽斌来自美团基础研发平台/基础技术部。 ----------  END  ---------- 推荐阅读 | 美团万亿级 KV 存储架构与实践 | Java系列 | MJDK 如何实现压缩速率的 5 倍提升 | 美团技术年货 | 600页电子书前端、后端、算法、测试、运维系列大合集
http://www.dnsts.com.cn/news/161033.html

相关文章:

  • app网页设计网站全球软件公司排行榜
  • 网站托管服务适合安徽省建设质量安全协会网站
  • 如何增加网站会员现在有什么网络游戏好玩
  • 做电商网站微信号是多少磁力猫引擎
  • 学校网站建设意义有哪些方面企业网站建设方案市场
  • aspx网站开发教程国内互联网公司排名2021
  • 成都建工路桥建设有限公司网站谷歌seo是什么
  • 西安自助建站公司做网站大概价格
  • 装潢公司网站设计与制作网站如何管理
  • 网站建设龙头企业北京室内设计
  • 专门做酒的网站青海建设厅通报网站
  • 盐田网站建设企业信息系统查询
  • 如何更改网站模板重庆专业的网站建设
  • 做网站要固定电话做程序开发的网站
  • 网站开发好学不广西网站建设智能优化
  • 上海专业的网站建设公司杭州化工网站建设
  • 制作医院网站网站建设公司 电话销售没什么效果
  • 网站flash效果建设厅和住建厅有什么区别
  • 手机怎么做自己的网站网站设计方案模板
  • 个人免费网站建设教程交换链接名词解释
  • 用dw制作网站模板个人网站可以做淘宝客吗
  • wdcp新建网站wordpress网站有哪些
  • 上海建设人才网站行业门户网站
  • 亿建联网站是谁做的网站建设的收获体会
  • 网站怎么建设高端公司公众号运营岗位职责
  • 网站备案信息如何注册公司官网
  • 二手购物网站建设方案秦皇岛网站制作与网站建设公司
  • 做代销的网站宁波网站建设rswl
  • 建设银行车主卡网上交罚款网站wordPress 要开放评论吗
  • 如何建设网站首页做网站需要的条件