`
IXHONG
  • 浏览: 438769 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

【转】京东云缓存JIMDB建设之路

阅读更多

 [京东技术]声明:本文转载自微信公众号“IPDCHAT”,转载务必声明。

 

缓存的大背景

 

缓存在软件应用特别是在互联网应用中无处不在,从数据库到应用服务、再到前端的页面每一层都会使用缓存进行加速,即使是硬件产品比如CPU、磁盘、网卡等也都会有相应的缓存或缓冲区。

 

当一个网页被打开时,为了提供良好的用户体验,提高用户购买的转化率,往往一个纯静态的页面已无法满足业务的需要,后台会有几十上百个服务为这个页面提供动态的个性化的数据。比如根据用户过往的购买记录和上网的浏览信息帮他推荐感兴趣的商品,告诉用户这些商品购买比例如何,好评度怎么样,什么时间段可以送货到家,这个商品有没有促销,能不能用券,如果缺货需要提醒用户这个商品当前是预定状态,还有很多就不一一列举,这么多的服务需要调用,而且要在每秒成千上万次请求的情况下,毫秒级将结果展示在用户的显示屏中,除了良好的架构方案,缓存的极致使用是必不可少的。

 

前期有些业务团队使用memcached服务,固定部署几个实例进程,客户端通过hash算法把数据切分几份,由于业务量相对小一些,能应付当时的业务场景。服务端也没做主从热备或者自动故障恢复等方案,服务挂了让运维上去重启一下。当数据内容增多了需要扩容,就找一个业务访问量低的时间段,暂停一小会服务把数据整体重新分布一下,有的小组会写一个迁移工具,有的直接从数据库加载数据重新推送到memcached中。

 

后来redis慢慢流行起来,相比memcached,支持丰富的数据结构,业务使用起来更加方便,大家也就慢慢地把服务从memecache迁移到了redis上。但是在用法上并没有什么改变。大家还是按照以前的思路,在客户端做一些封装,服务端地址直接写死在客户端应用的配置文件中,服务端地址改变了就重启一下客户端程序。

 

缓存的规模越来越大,线上故障变得频繁起来,同时客户端实例数也在增加。想象一下,半夜有一台缓存服务器发生硬件故障,负责业务的同学第一时间会收到业务异常或者性能等方面的报警,然后通知运维进行问题排查和确认,确定故障机器后把缓存服务更换到另一台机器上,由于运维可能不太熟悉业务应用的配置文件,又需要让业务人员登陆VPN修改客户端程序的配置文件,再对客户端服务全部重启一遍。整个过程下来短则十多分钟,长的可能需要半个小时或者更久。还得庆幸大家半夜头脑清醒,VPN网络通畅,配置文件没有修改出新的问题。一个具有以下能力的缓存平台对业务来说是非常需要的:能够自动进行故障恢复、可以在线扩容、自动负载均衡等,而且这些过程完全可以在客户端无感知的情况下完成。

 

JIMDB登场

现有系统的整体架构

 

  • JIMDB的特性

一键创建集群实例

在线全自动弹性伸缩

部分复制扩容,缩短扩容时间

在线平滑升级

全自动故障恢复

支持多语言接入

支持多种读取策略

容器化部署,docker镜像管理

增量复制

 

  • JIMDB架构

     



 

用户在使用JIMDB平台时,先在管理端创建一个集群,然后内嵌JIMDB提供的SDK,或者通过支持REDIS协议的客户端接入代理层(Access Points)。客户端应用启动后JIMDB的SDK会有后台同步配置信息的线程从配置管理模块(config server)上拉取集群的拓扑信息,业务访问某个KEY时,按照指定的路由算法访问拓扑中的某个节点。当服务端发生扩容或故障切换后,配置管理模块会更新拓扑信息,同时通知客户端更新拓扑信息。

 

JIMDB平台主要包括:web console\configserver\scaler\failover\info collector\sentinel\resource manager\ap等模块。

 

server:服务端提供KV服务,支持一主多从,读写分离。一般主从之间为异步复制,但是服务端提供了等待从复制完成的命令支持。扩容分裂时以slot为单位进行部分数据迁移。

Config server:配置中心负责集群拓扑的维护,拓扑变更后负责通知客户端及时变更拓扑。

Sentinel:哨兵用于判断服务端实例存活状态,同机房多实例跨机架部署,避免网络分割。

Failover:负责角色切换和故障实例的替换。

Scaler:当内存容量或者流量等达到阈值时对分片进行分裂扩容。

Info Collector:负责监控数据的采集。

ResourceManager:负责物理机资源的管理和容器的创建。

 

第一版

JIMDB在这样的一个大背景下被提出来,在2014年初决定做一个缓存的平台,主要解决以下几个问题:1、精确的故障检测和自动故障切换;2、无损扩容;3、提供监控和报警等服务。

 

为了满足扩容的需求,客户端采用了一致性哈希算法,预先在客户端对数据划分了一个比较大的slot集合,一段连续的slot对应一个shard,每个shard由一主一从或者一主多从组成,当主发生故障时可以让从提升为主,继续提供服务。

 

  • 故障检测

在故障检测和故障切换的方案中,比较容易想到的方案就是引入zookeeper,通过zookeeper的临时节点探测不存活的服务,但是由于需要修改服务端代码、不方便跨机房部署、watch数目和连接数过多有性能问题等原因最终没有被采用。决定自己写一个探测程序,这个探测程序主要是检测JIMDB实例的存活状态,但是他需要尽可能的解决由于部分网络不通时导致误判的问题。采用的方案就是,对探测程序部署多个,每个部署在机房的不同机架下。多个探测实例同时对同一个JIMDB实例进行探测,只要一个探测实例检测到服务端实例是存活的,那么该实例就认为是存活状态,当没有人反馈其为存活状态,且超过半数的探测实例认为该实例死亡时,则通知故障恢复程序进行主从切换,变更集群拓扑结构,并把新的拓扑结构通知给所有的客户端。故障检测和恢复的问题算是基本解决了,接下来就是需要解决扩容的问题。



 

 

  • 无损扩容

     

业务在刚上线阶段,访问量和需要缓存的数据量并不大,提前申请很多的缓存服务会导致资源的浪费,随着业务的增长或者需要搞活动促销,访问量和数据变大,缓存服务端需要扩容。按照一致性hash算法,需要让原来落在同一个shard上的一段slot区间进行分裂,变成两段区间,每段再各自对应一个shard。意味着这个shard上的数据有一部分需要迁移到新的实例上完成扩容。这些slot的信息在服务端并不存在,因此服务端也不知道哪些数据应该进行迁移,在扩容过程中还需要保证客户端能够正常访问。当时采用的方案就是采用代理的方式,当需要对某个shard进行扩容时,先下发一个新的拓扑信息给客户端,让访问该shard的请求全部变更为访问代理服务。代理服务再把请求转发给该shard。当所有客户端都连接上代理程序后,扩容就可以开始了,数据同步程序通过复制协议把数据从原来的实例上把数据复制下来,再过滤出需要迁移的数据转发到新的实例上。当代理程序检测到数据复制快要完成后会挂起所有的请求,等待复制完成,然后按照新的slot分布信息把迁移的KEY请求转发到新的服务器上,这样扩容就完成了最主要的步骤。接下来只要把分裂后的拓扑信息下发给客户端,客户端会重新连接服务端,把连接代理端的链接断开,扩容就算完成了。



 

虽然上面的流程能够完成扩容过程,但是在扩容时需要从服务端拉取全部的数据进行过滤,然后再转发;当服务端数据比较大时,在当时千兆网卡下,为了不影响正常业务需要控制数据过滤迁移的速度,避免把原服务端的网络打满,复制时间会比较长;另外整个流程涉及到的模块比较多,代理层对客户端性能也有一些影响;扩容后有可能存在某些异常情况下,客户端的请求任然会访问老的分片,但是服务端由于没有slot信息,无法判断该KEY是否应该属于自己,导致数据混乱。

 

为了解决以上问题,开始对服务端进行改造,服务端引入slot的概念,数据按照slot进行组织,一个服务端实例服务端可以服务多个slot,客户端中维持一份与服务端一致的slot信息。由于服务端有了slot信息,因此可以判断哪个KEY是否在自己的服务范围内,还是已经迁移出去了,可以避免数据被写错。有了slot,迁移时服务端以slot为单位进行数据迁移,避免了从全部数据中找出需要迁移的数据,另外直接通过待扩容的服务端往新扩容的服务端写数据,避免数据需要通过过滤层的转发,也不需要多次对数据进行序列化和反序列化,大大提高了迁移速度。



 

 

第二版

随着集群规模进一步扩大,使用的业务越来越多,很多以前注意不到的问题逐渐暴露出来。

 

  • 自动弹性调度

业务流量突然飙升,容量不足等都需要运维通过管理工具进行扩容增加实例数,另外也有一部分业务申请了集群空间,由于业务调整等原因,访问量变小了或者停用了,平台管理人员比较难发现。为了提高平台自动化的能力,减少运维人员的工作量,需要让平台动起来,弹性伸缩的需求摆在了开发人员的面前。

 

为了让平台弹性伸缩起来,需要对集群的各项指标进行监控,比如OPS、内存使用率、网络流量等进行监控,统计这些指标一段时间内是否达到了设置的阈值,当超过扩容的阈值自动触发扩容,当低于缩容的阈值时自动进行缩容释放资源。

 

缩容的过程和扩容的过程基本一致,扩容是把一个实例上的部分slot迁移到新的实例上,缩容是把一个shard实例上的所有slot迁移到另一个实例上进行合并。

 

扩容时由于需要增加实例,增加的实例应该部署在哪台机器上才合适呢,为了选择出最优的机器,有一个采集程序会定期进行信息收集,然后根据CPU繁忙情况、网络流量、OPS、内存剩余空间、机器上的实例数等进行综合打分,各项指标都比较空闲的得高分,如果有一项指标不符合部署要求则直接淘汰,然后再从得分高的机器中选择一台机器进行部署。由于扩容在集群中是并发进行的,因此有可能会被多个处理线程同时把实例部署到同一台物理机上,当大家部署完成后可能实例数等指标就不符合要求了,因此需要有一个预分配资源的计算,对未使用的资源进行预占并被计算在内,如果部署失败需要把这些资源值做相应的扣除,避免并发部署出现使用资源超限的情况。

 

对同一个集群还需要控制每台物理机上最大可部署的实例数,避免同一个物理机部署实例数过多,当机器故障时对同一个集群影响过大。

 

为了防止同一个机房路由器故障或者断电等情况的出现,同一个shard的主从实例应该跨机架,对有跨机房需求的应用,同一个shard的主从实例还应该部署在不同的机房。



 

 

  • 服务端升级

当一个平台需要维护成千上万个进程,这些进程在升级过程中不能中断服务,而且这些进程还是有状态的,必须保证数据正确无丢失,不能通过简单的流量切换就能解决问题,升级程序成为了一项富有挑战的事情,同一台机器由于服务端升级需要同时运行多个版本,程序运行文件的分发和维护也变得不好管理,容易引起混淆,如果由运维人员手动处理,不但时间会变得非常漫长,而且容易出错。

 

为了解决版本控制和快速创建、销毁服务的问题,引入了docker,通过docker的registry管理服务端版本管理和分发。通过调用docker deamon进程的接口启动一个docker容器创建一个jimdb的服务端实例,销毁时也调用docker deamon程序销毁容器。

 

程序升级过程中涉及到数据的拷贝,为了加快升级速度,考虑到升级完成后旧的实例就可以销毁,因此在同一台物理机上创一个新版本的实例,当旧实例销毁后并不会导致物理机上实例数超限等问题,通过数据复制流程把旧实例上的slot全部迁移到新的实例上,由于数据在同一个物理机上流动,速度会比网络传输快很多。多个集群可以同时进行,也不会导致网络流量由于数据的迁移而暴涨。



 

 

  • 资源隔离

 

不同的业务呈现出不同的业务特点,有的数据量不大但是并发量大,有的数据量比较大但是访问相对不频繁,有的业务写入并发量大读少,有的业务间歇性访问量大,有的持续性访问量大,业务之间的重要程度也不尽相同,对性能的要求也不同。平台在部署集群时需要考虑这些因素,按照业务情况划分不同的资源分区,对性能要求高的,业务重要的集群,其部署的分区需要控制每台物理机部署的实例,单台物理机上的总实例数少一些。对重要程序相对低一些的集群,其部署的分区每台物理机可以多部署一些实例,实例数可以超过CPU的逻辑核数。管理端需要对物理机划分不同的分区,也需要给集群划分分区,在分区上可以设置单台物理机上最大可部署实例数,同一个集群部署在同一物理机上的最大分片数。

 

  • 大KEY扫描

由于服务端单进程单线程处理请求的特点,不适合一次处理的数据量过大,占用CPU时间过长的请求,比如频繁读取以M为单位计算的数据量,或者一次从集合中取出成千上万条数据项,任之删除一个特别大的集合都可能导致服务端处理几十毫秒或几秒,从而导致其他客户端的请求被堵塞,TP99性能变差,对性能要求严格的业务直接会导致访问超时。为了协助业务发现这些问题,除了定期地扫描slowlog外,还需要通过外部程序去扫描值比较大或者数据项比较多的集合,然后通过邮件等形式告诉业务负责人,这样业务可以及时地调整,避免在大促时流量增长后才发现问题,调整就已经来不及了。

 

  • 读策略

当业务的读请求OPS非常高时,如果简单的通过分裂扩容,降低单个shard服务的slot数量,对资源会有一些浪费,在业务允许有一定写延迟的情况下可以通过添加多个从的方式扩展单个shard读服务的能力。另外对单个KEY读访问量比较大时扩容并不能解决问题,添加多个从,让客户端的访问压力分摊到多个实例上解决热KEY的问题。读取访问可以有多种策略,比如master优先,当master访问异常时再访问slave,也可以轮询多个slave,或者随机访问一个实例。

 

 

后续可能的工作

 

缓存系统主要应对高性能的场景,把数据都存放在内存中。但是随着业务的发展,系统访问量越来越大,业务系统有更高的诉求。性能高是一个硬性的指标,但是在高性能的前提下,还希望能保障数据高可用,在发生故障时数据不丢失;为了应对大的访问流量,一份数据可以分布在多个节点,节点之间是否能够同步写入没有延迟;当数据分散在不同的分片上,业务需要同时对这些数据进行修改,能否保证修改的一致性,降低业务设计的复杂度;现在的业务系统使用缓存,一般都采用数据库做为数据的来源,然后再把数据直接或者做一定的处理后存放到缓存中,业务系统需要自己去保证数据库和缓存之间的一致性,是否可以把缓存和数据库结合在一起,业务应用不需要关系数据同步的问题。另外现在的缓存一般通过哈希算法进行散列,业务不方便进行范围扫描,如果需要有序的结果集就需要业务维护额外的数据结构或者自己处理。

 

随着硬件技术和材料学的发展,非易失的存储介质访问速度越来越快,不久的将来可能非易失的内存会应用到工业界,原来基于机械磁盘的软件架构和思维模式需要发生大的变化,通过硬件和软件的共同改进推出业务使用更快捷的产品。

 

现阶段,随着SSD硬盘的普及,分布式算法的完善,构建一个高性能的、分布式的、持久化存储的、支持分布式事物的K-V平台已不再是件十分困难的事情。

 

现有系统的完善和改进

 

  • 完善弹性调度

当前平台实现了弹性的扩容和缩容,但是相对还比较简单。未来会继续完善,比如对集群的各项指标通过离线计算合理规划一个扩容的时段,而不是当流量已经很大了才去扩容。集群直接的扩容现在是独立控制的,可能出现同一个物理机上的实例在扩容,导致物理机流量大影响正常的业务,需要有程序把整个平台的扩容协调处理。

 

  • 新特性

1、虽然现在已经有很丰富的数据结构,但是还是有很多业务希望有更丰富的数据结构可以选择,未来可能会支持比如类似于数据库表一样的结构。

2、数据结构支持版本号,让业务控制并发修改更方便等。

3、支持hashtag,让业务可以控制KEY的分布情况

 

  • 丰富的监控和性能统计数据

现在平台的监控数据查看还不方便,也缺乏一些性能的指标,当用户反馈访问慢时不能快速地定位是网络问题,还是服务器繁忙,还是有热KEY等其他原因。监控数据的收集和展示需要朝着分析问题和解决问题的方向思考。

 

  • 客户端增加本地缓存功能

现在热KEY主要还是通过业务自己进行本地缓存,有一些共同的逻辑其实可以通过平台去支持,另外有时热KEY只有在大促时才表现出来,但是业务程序没有做处理,大促时发现已经来不及解决了,如果平台可以通过管理端几个简单的操作快速启用客户端缓存,将可以缓解这一矛盾。

 

  • 新客户端支持异步发送

有一些业务需要更高的发送性能,客户端可以支持异步发送,对发送结果异步回调通知给业务程序,将可以大大提高单客户端的发送性能。

 

  • 大KEY的应急处理

当业务出现大KEY,严重阻塞其他命令的处理时,可以对单个大KEY进行迁移,保证其他的访问正常处理。

 

  • 支持KEY按范围扫描

现在KEY都是按hash分布,有些业务场景需要KEY有序排列,但是单个实例又不能满足其存储或并发的性能,需要分布在多个实例上,但是业务需要这些KEY有序分布,方便进行范围扫描。

 

新产品的预研

 

移动互联网和物联网的发展,终端数量在快速膨胀,并发访问规模快速增长,对性能的要求也越来越高。现在关系型数据库普遍做法就是分库分表,历史数据再转移到HBASE或者归档库等,业务在使用时要么自己处理分库分表的算法,要么接入一个中间代理层,当数据量增长需要扩容时不能很好地对业务进行隔离,需要业务配合一些做变更。如果有一个高性能的、可以持久化存储,支持海量数据,能够灵活查询的,可以处理在线联机事物的半结构化的数据产品,将能够解放一部分业务系统开发的生产力。这个产品本身就能够自动进行shard,对业务系统透明,根据物理机的负载情况可以对shard进行调度均衡。数据有一定的结构,但是不强制要求,业务可以灵活使用,业务修改时,不需要再单独申请对线上数据库表的修改。现在的JIMDB系统的一些实践经验可以很好地帮忙我们开发这样一个新系统。

 

 

案例分享

 

单次取出集合中全部数据项

应用在日常运行过程中单个KEY对应的list列表并不长,开发人员采用“lrange key 0 -1”获取全部的数据项,运行平稳。有一天另外的同事对业务进行了调整,单个key中的数据项暴涨超过了几十万条。单个查询严重堵塞其他命令。

建议:如果业务能限制单个KEY对应的长度最好,如果不行需要限制单次取出的数据项数量,分批执行。如果数据项过多KEY过期或主动删除单个KEY也会导致命令堵塞。

 

单个VALUE过大

业务在开发时为了使用上简单,可能存放一个比较大的VLAUE,比如几兆。访问频率低时没有发现问题,但是当业务新上线一个程序,增加了对这个KEY的访问次数,结果相应接口的TP99从2毫秒上升到了几秒。

建议:业务拆分VALUE

 

单KEY写操作与业务增长成线性或几何级增长需要警惕

统计访问流量,常用的方式就是访问一次调用一次自增。但是当访问量巨大时,导致单个KEY的访问性能根本达不到业务的要求。而且单个KEY写操作频繁无法通过扩容、增加slave等手段应急解决,只能业务修改代码(大促时只能眼看着服务被打死)。

建议:缓存操作是快,但是单个实例的服务能力也是有上限的。需要在业务设计时绕开,比如拆成对多个KEY的写,让流量分摊到多个shard上去,读的时候再汇总等。

 

热KEY本地没有做缓存

修改不频繁、调用频繁的KEY本地没有缓存,每次都通过远程获取,流量上去后TP99性能变差。

建议:对性能要求严苛的应用需要考虑在本地应用中进行缓存。

以上内容为IPD原创,如需转载,请注明出处~

 

JIMDB采访

写在前面Redis是一个C语言编写的开源、支持网络、基于内存、键值对存储、可持久化的高性能NoSQL数据库,同时提供多种语言的API,目前最新版本为3.0.5。由于性能优异,已被国内外众多公司采用构建缓存集群。其中,京东对Redis的应用实践可圈可点。

在2014年的QCon上海大会上,京东云平台首席架构师刘海锋介绍了京东分布式内存存储平台(RAM store platform)。经过两年多的演进,这套系统已经支撑起京东几乎所有的在线业务。近日,InfoQ采访了刘海锋,再探这个分布式内存存储平台的全貌。以下是采访实录:

InfoQ:你能简单介绍一下JIMDB这两年来的研发过程吗?

刘海峰:JIMDB是一个以内存为中心的键值数据库,目前在我们京东多个数据中心部署了几千台机器,支撑了京东几乎所有的在线业务。JIMDB的研发是一个逐步演进的过程,最初是从Redis改进而来,现在的数据模型是兼容但不仅限于Redis,可以理解为Redis的一个超集。JIMDB在京东的应用也不限于缓存,我们很多业务线已经把它当成唯一的存储。

具体来说,研发过程经历了三个阶段:

  • 第一个阶段是从2013年底开始。当时我们在使用Redis时遇到了一些问题,为了解决这些问题,我们把Redis这个优秀的单机软件变成一个健壮的分布式系统。比如我们要做故障的手动、自动切换;要实现横向扩展,动态分片并且不能影响网络流量;要实现灵活的异步复制、同步复制。虽然每个实例仍然是单个的Redis,但整个的集群能够实现故障的切换、横向的扩展和比较灵活的复制。

  • 第二个阶段从2014年下半年开始。用一句话来概括这个阶段就是把JIMDB变成了一个完全托管、自管理、自运维的存储服务。因为Redis的引擎比较简单,为了实现这个目标,我们重写了整个内存存储的引擎,使之可以支持更细粒度的分片;复制协议部分也作了改动,不但支持全量复制、增量复制,还支持了任意局部复制。例如,我们经常遇到某个分片上的热数据过载的问题,这时候的解决方案是把这部分数据复制到其他的实例上,从而分散流量,这样我们就实现了动态的分裂、合并。在运维管理上我们引入了Docker,部署环节实现了整体的调度。

  • 第三个阶段在2015年上半年启动规划,目前这个阶段还在进行中。我们已经把很多实现陆续推到了线上。即,不仅仅支持原来的哈希分片和原有的数据类型,我们还实现了在内存中支持B+树、支持按范围的划分、支持排序的遍历、复制协议也更健壮、更可靠,等等。第三阶段实现的特性将比Redis更加强大。当一个分布式存储支持按范围分片和复制的时候,这个系统无疑是更健壮和更可靠的。当然,这个阶段也存在很多的挑战,例如,哈希的分片相对来说比较简单,但我们要实现按范围、跨数据中心的异步和同步复制时,这个挑战就大的多了。

纵观这两年的发展,JIMDB从最初的几台服务器到现在的几千台、从单机Redis系统到自管理的分布式存储系统、用户只需要一个认证授权即可使用、支持京东线上一千多个业务(集群),是一个不断挑战的过程。

InfoQ:市面上已经有了大量的键值对存储数据库,并且已经在大公司的生产环境投入使用。为什么京东还要开发JIMDB?

刘海峰:这更多的是业务的需求。公司原来的存储应用百花齐放,有人用Redis、有人用MongoDB、有人用memcached等等。但现在的情况是,数据库用MySQL比较多;NoSQL几乎99%的都在用JIMDB。原因在于JIMDB有人维护、自管理、性能稳定,俨然已成为京东内部垄断性的应用。但在项目开始的时候我们也没想到会变成今天的规模,随着业务的发展,JIMDB一步一步演进成了今天的样子。

InfoQ:JIMDB在京东的主要应用场景是哪些业务领域?在这些应用场景里,跟其他内存存储相比JIMDB有哪些优势?

刘海峰:目前JIMDB的应用场景十分广泛。比如,现在依然有很多同事把JIMDB当Redis来用,很多人拿来当缓存用,也有很多人把JIMDB当唯一存储在用,用法不一而足。甚至,很多离线计算MapReduce的结果也直接存在JIMDB里;对京东的访问用户来说,大家看到的商品详情整个页面的所有元素、第三方show的广告、搜索推荐的结果等等,都是存在JIMDB里面。我们支撑了无线端、PC端很多业务。但总的来说,目前用量最大的业务是搜索和推荐的集群,另外单品页的用量也很大。因此这个系统的压力也很大。

跟其他内存存储相比,首先这个系统是从Redis演进过来的,Redis有的功能我都有,但是JIMDB在很多方面都比Redis更健壮、更稳定。其次,从Redis的视角来说,这是一个经过大规模生产环境验证的、更好的Redis。目前我们有六、七个人在维护这个系统。

InfoQ:对冷热数据的处理以及数据持久化JIMDB是怎么做的?在算法和数据结构方面JIMDB做了哪些优化设计?

刘海峰:持久化还是采用经典的方式——记日志、定期做快照。虽然这是一个很简单的方式,但是十分的有效。对于可预测的热数据我们会预先做分片,尤其是秒杀活动的内容;而对瞬时的热数据做动态调整是JIMDB最优先的特性实现,这跟我们电商的特性相关。目前京东的老机房大量部署了JIMDB,新机房也部署了很多。

算法和数据结构方面,刚才已经举过一个例子就是B+树。我们实现了KV的有序排列和按任意范围的分割、复制。这是我们新加的数据类型,其他的支持已经十分强有力了。

InfoQ:据我所知Redis所在机器物理内存使用率并不高(貌似没有超过实际内存总量的60%)。针对京东这么大规模的在线存储,内存管理方面JIMDB是怎么做的?

刘海峰:这一方面主要做的工作是内存分配器的优化。Redis的内存分配器是jemalloc,我们依然沿用了jemalloc,但是我们作了很多优化。此外,我们对比较大的Value做了特殊处理,比如我们不让大Value的存储占据内存,而把它写到磁盘中去。但是很多人会问SSD的问题,为什么不采用混合存储的架构?

其实在2014年的时候我们专门在线上测试过这个方案,当时大概对10%的集群采用了混合存储的方案。经过一段时间的测试发现:

  • 首先,虽然SSD的成本有一定的优势,但实施工程是有成本的。基于磁盘去写一个有丰富类型的键值数据库其实是比较复杂的,即使是简单的键值都比较复杂,在遇到Map、List、B+树等等,用磁盘的方式实现起来其实更为复杂、成本更高。也就是说,其实现和维护的成本太高,并且性能也不占优,整体工程成本远远超过内存存储。

  • 第二个原因在于,内存的价格在逐渐降低,我们是能承受这个成本的。目前我们使用的内存也越来越大,预计明年我们将在单机上使用1T的内存。

  • 第三个原因是系统架构方面的考虑。我们的数据中心有很多的机器,很多应用服务器和私有云服务器多数时候的内存利用率并不高,我们可以利用这些机器的资源做JIMDB的动态资源池,只需要在相应的机器上部署一个JIMDB的程序就好。这也极大地提高了整个数据中心的资源利用率。

基于以上三点考虑,我们选择用内存做存储。随着京东业务的发展,未来1~2年我们JIMDB的规模将达到1万台机器,当然,这1万台机器并不是专用的,而是JIMDB在整个数据中心的部署量。

InfoQ:你能谈谈JIMDB在“双11”大考中的表现吗?

刘海峰:JIMDB在“双11”的表现很好,系统稳定,性能平稳。我们的新机房启用了大量万兆网卡,以前千兆网卡被打满的情况终于得到缓解。但任何系统都不可能没有任何瑕疵,现在看来JIMDB的问题更多的不是技术性的,而是系统到了一定量级怎么去运维,尤其是系统预警和运维操作流程方面的工作需要加强。随着公司业务的发展,运维正变得越来越重要。

我们花了很多精力来做监控与运维。JIMDB核心的部分是一个个兼容Redis协议的Server,除了网络部分,我们几乎重写了整个存储引擎。还是拿一个具体的应用场景来说吧,假设我一台机器上跑了15个实例,但是发现其中一个实例的流量极大,把其他实例的带宽都给占了。这个时候我需要对这个有问题的实例进行分裂和迁移。但是迁移的时候存在一个问题,由于进出流量都很大,迁移有可能是迁不动的。这个时候我们有一个限流措施,保证在不影响其他实例带宽的情况下把有问题的实例迁走。

InfoQ:你怎么看待JIMDB未来的发展趋势?或者,你能否谈一下内存云存储(RAMCloud)的未来?

刘海峰:我非常坚信的一点是,内存是存储的未来,特别是对一些有结构化的数据来说。随着内存成本的降低,以及在内存上实现存储的简洁和高效,这个趋势势不可挡。而且我认为JIMDB这两年做的工作是一种更务实的RAMCloud实现,它是业务驱动的、经过大规模生产环境验证的。

未来随着系统的发展,在功能上我们会以内存为中心,做有序的、KeyValue的、丰富数据类型的大表支持。JIMDB未来有可能会加入一些SQL的支持。目前要先把规模、稳定和运维做好。

  • 大小: 29.4 KB
  • 大小: 18.6 KB
  • 大小: 30.4 KB
  • 大小: 13.8 KB
  • 大小: 11 KB
  • 大小: 105.9 KB
1
1
分享到:
评论
1 楼 IXHONG 2017-03-19  
JIMDB - JD IN MEMORY DATABASE

相关推荐

Global site tag (gtag.js) - Google Analytics