Redis分布在微博内部的各种应用场景,比如现在春晚比拼的“红包飞天”活动,还有粉丝数、用户数、阅读量、转发量、评论量、广告推荐量、差评、音乐排行榜等都使用Redis。图片来自Pexels,我从以下三个方面来分享:微博Redis应用场景微博Redis优化未来展望微博Redis应用场景业务&规模&挑战线上业务有上述信息流媒体、广告、用户关系等等,还有你现在可能比较感兴趣的热搜,用户平时去看什么,还有引爆阅读量的话题,还有现在兵家必争之地的视频。Redis用于大型和小型企业。在线规模上,微博拥有100T+存储,1000+物理机,10000+Redis实例。对于我们面临的挑战,我们每天有万亿次的读写,对在线响应时间要求比较高。举个简单的例子,我们跨机房部署资源,但是有些业务部门甚至不得不吐槽跨机房部署多了2毫秒的延迟(这真的是小妾做不到的,单机房的话failsWhat?有些业务方真是异想天开)。基本上四个9的响应时间是20毫秒。在成本方面,因为我们线上有大量的T需求,所以成本压力其实特别大。技术选型上图为微博数据库的技术选型。可以看到它不仅包括Redis等NoSQL,还包括队列和存储。优化从2010年开始,我们在正式版2.0的基础上引入了Redis,到现在已经9年了。我们主要做了以下几个方面的改进:Redis编码格式,在特殊场景下可以节省30%的空间。主从库有独立的复制线程。我们自定义了一些数据结构,比如:LongSet数据结构,它是一个“定长开放寻址的Hash数组”,为Redisdict减少了很多额外的指针开销。在主从复制方面,独立复制线程+全增量复制,这样如果网络主从暂时断开,只从当前Pos点开始同步数据。在持久化方面,我们采用全量RDB加增量AOF复制。AOF写入/刷新,主线程->BIO,避免写入造成的阻塞。登陆时间不可控—>cronsave可控。增加aofnumber,设置AOF的个数,避免写得太快导致磁盘满。对于高可用性,我们不使用Redis的官方或社区开源HA。我们使用自己开发的一套RedisHA,保证故障时快速切换。微博有大量的技术场景,比如转发、点赞、阅读等,对于一些用户来说,他们非常关心这些指标。如果我们使用原生的Redis,会浪费很多存储空间,因为它的产品很特殊,它的Key是一个用户ID,它的Value是一个数字。我们先在内部改了一个叫做RedisCounter的版本,相当于只维护了一个哈希表,节省了大量的Redis内存空间。当然,它有一个缺点,就是上线时间短,速度快,所以只支持单列单表。如果要收藏、转发、评论、点赞3个计数,需要部署3套资源。这样大家访问微博获取这3个号就会慢一些。而我们需要维护3套资源。为了应对这种场景,我们支持多列多表的方式。如果一个表写满了,我们可以继续写下一个表。在写入最后一个表的时候,我们可以将之前的表roll到磁盘,但是此时是不可读的。为了解决不可读的问题,我们想到了一个办法,把所有的表放到磁盘中,维护ddb的数据结构。历史数据存储在磁盘上。根据之前的统计,在90%以上的线上案例中,用户只访问了几个月的数据,所以一些长尾数据可以通过从磁盘读取数据来解决,而不会影响用户体验。微博还有一些存在判断行为,比如是否被点赞,是否被阅读。数据总量非常大。如果使用Redis,内存开销会特别高。所以我们修改了一个服务版本,兼容Redis协议,基于BloomFilter,开发了一个Phantom版本,高性能,单线程网络处理机制,媲美Redis性能,低存储空间,每条记录占用1.2*Nwordssection(1%误判率,每增加0.6*N字节,误判率下降到原来的1/10,N为单slot占用的比特数)。当然还有其他类型的数据库像我们最近用的队列,MySQL等等,这里就不展开了。简单总结一下Redis优化的第一阶段:非阻塞落地。增量复制->RDB+AOF。在线热升级。定制RelationalGraph,内存降低1/10,性能相当。自定义计数,内存减少1/4,性能提升3-5倍。布隆过滤器。但是我们做了那么多优化,还是跟不上业务的需求。微博对Redis的优化首先要明白为什么要优化。我们一般从三个方面来考虑:第一是业务端。目前线上业务方需要关心资源分配、容量规划等方面,比如内存是否满,磁盘是否满,是否使用MySQL,是否提前分库分表,是否QPS可以处理它。我们希望对业务方屏蔽这些问题,他们只是工作,不必太在意资源细节。第二个是DBA。微博虽然不再处于快速增长的状态,但实际上还在以一定的速度增长,所以对DBA的需求还是很大的。另外,我们部门承担了微博所有的数据库服务,微博服务器也是最多的,所以对我们来说,需求多,变化多,挑战也大。从设计的角度,我们需要考虑如何更合理的设计Redis。概括起来就是三个方面:高性能、快速读写、快速访问、快速响应时间。能够支持大容量需求。可扩展性,因为接触的业务方很多,你会发现一个问题。基本上很少有业务方能把自己的需求描述的很清楚。上线后经常发现内存不够用,或者写不上去,所以这个时候我们就需要在扩展性方面提供强有力的支持。我们可以把这三个方面理解为三座山。CacheService服务化要解决三座大山,首先要把CacheService服务化。是一种多级缓存服务,可以解决高访问和高并发的问题,实现高可用。基于该系统,还设计了一套后台程序,自动监控微博流量,支持自动扩缩容,实现快速攻克高峰,过峰后回收机器,实现流量的充分利用。资源。.当然这个系统还在不断完善中,希望以后会更加智能。ConfigService就是我们把配置放在配置中心,客户端从配置中心拉取配置。接入方式有两种,第一种是SDK,第二种支持多语言,请求通过Proxy路由到后端Cache。DBA只要使用管理平台,就可以快速扩缩资源。现在让我们谈谈多级缓存。其实里面有四个角色:MasterMaster-l1SlaveSlave-l1Master和Slave没有同步关系,而是根据角色的作用来命名的。Master-l1有多组数据处理热点,Master是基准数据保存全量数据。Slave一般用于多个机房的容灾,Slave-l1用于多个机房的数据同步。这种同步只是保证最终数据的一致性。下面以阅读为例来说明这个过程。阅读是先访问Master-l1。如果没有命中,它将访问Master。如果没有命中,它将访问从站。通过这三层,在大多数情况下,99%的热点Hold住了,然后1%的流量就会落到MySQL里面。如果有100万次读取,只会有10000个QPS传给MySQL。如果100万次读取全部发给MySQL,MySQL的成本会特别高。而且大家都知道MySQL在高并发读写的情况下很容易被kill掉,短时间内无法恢复。CacheService目前支持MC和Redis两种协议。上图是我们DBA操作的扩缩容界面。这个业务一共有20个组,每个组有5个IP,5×20=100个实例。其实百个实例在里面提供服务,在线有很多单集群服务,可以支持百万级甚至千万级QPS的高并发访问,可以支持快速扩缩容。让我分享一下我们以前的成功案例。我们连续几年实现春晚1000+阿里云ECS的弹性扩缩容,多次实现平滑过渡不降级,支撑微博春晚50%的核心流量。上图是我们内部系统集成来支撑系统,这里就不展开了。MCQ服务是基于之前的Cache服务。我们在2018年上半年就和业务方合作做了排队服务。为什么要单独提出队列?因为内部或外部经常有人问,发微博的流程是什么?发表评论的流程是什么?数据如何解决?这些问题是一个关键部分它在队列中。发微博的时候其实是先写入队列,然后队列再写入后端MySQL。如果这时候MySQL宕机了,我们会有一个修复队列,有专门的Key来存放这部分数据,等MySQL恢复后,再把这部分数据写入MySQL。上面还有一个BCP,因为我们在做这套的时候,其实是想在整个微博上宣传一下。去年,比特币非常受欢迎。我们也想购买比特币,在内部使用机器资源或者一些内部开源的东西,转换等价的材料,然后应用这个服务,但是最后这个计划没有实现。上图是一键报警和操作的监控画面。上面说了,我们把Cache变成了服务,但实际上并没有解决容量过大的问题。虽然内存的价格一直在下降,但是相对于硬盘来说,价格还是太高了。如果我们经常有5T或者10T这样的业务,都放在内存里面,其实我们的成本压力会特别大。此外,我们需要向特别成本委员会申请资源。只有通过成本委员会的批准,我们才能得到这些机器,整个周期需要很长时间。Redis容量过大如何解决?为了解决容量过剩的问题,我们要把容量从内存放到磁盘上。我们当时考虑了一些特性,比如支持冷热数据分离,比如把所有的历史数据或者全量数据存盘,然后支持持久化,数据主从复制,在线热升级。需要与Redis数据类型兼容。还与Redis复制兼容。基于之前的场景,这种方式特别适用于微博等属性。即使数据冷热不明显,比如T的情况,每秒访问数K,这种方式也特别适用。先说处理模块,包含主线程和后台线程。主线程主要处理连接请求、协议解析、命令请求等。后台线程主要是复制线程,还有BIO线程。我们把烧写、写AOF等操作放在这个线程中,尽可能的减少写对Redis造成的阻塞。还有一个BloomFilter,是基于布谷鸟算法优化的。初始化时,指定Filter的容量,增加一个新的双链表来管理Hash冲突。从名字就可以猜到它是Redis+RocksDB的组合。为什么我们这次没有像前面提到的CounterserviceSSD那样自己设计,主要是因为我们设计的时候RocksDB并没有很多大规模的应用。现在RocksDB已经很成熟了,也有很多成功的案例。我们还有一个不自己开发的原因,就是如果自己开发的话,适用性、性能、代码健壮性可能都不会那么好,所以为了节省时间,我们使用RocksDB来存储,避免重新发明车轮。LRU是为了加快访问速度。如果第一次访问时没有在内存中读取,就会从磁盘中读取。它实际上将存储在内存中。下次读取时,会从LRU读取。读出来。这还涉及将数据从内存换入和换出磁盘。如果Key或Value特别大,性能会受到影响。这就是为什么我们不建议如前所述对特别大的Key或Value使用RedRocks。将前面的处理模块和后端整合形成如下架构图:简单总结:简单易用:完全兼容Redis,业务方无需做任何改动即可迁移。成本优势:将热点数据或经常访问的数据放在内存中,将所有数据放在磁盘中。这是一个特别大的优势,可以突破内存容量的限制。高性能:热点数据存储在内存中,热点数据访问性能媲美Redis。下图是性能压力测试报告。我们对比了Set的随机配对:还是不能满足新要求?之前我们解决了大容量的问题,但是还有很多困难没有得到很好的解决。所以我们借鉴了开源的经验,也考察了Twemproxy、Codis、Corvus、Redis-Cluser的功能:其实我们在2015年的时候就已经有基于Twemproxy的业务了。线上,比如微博音乐,微博健康、护照这3个业务已经上线。但是我们内部并没有大规模推广,这主要有两个原因:一是迁移还是比较耗时的。二是无法更完美的动态添加节点,还有其他内部约束。以上是我们的设计思路:第一,可以支持在线扩容和缩容。二是支持多语言访问,因为我们要宣传的是整个公司,而不是一个部门,所以必须要有这个功能,方便宣传。三是服务特色需求。下面简单说一下Proxy中各个模块的功能:端口自动增删改查:根据Vintage对Proxy节点的配置,自动添加监控端口或删除删除的端口,以及客户端被监控。Redis协议分析:分析Redis协议,判断需要路由的请求,对于非法和不支持的请求直接返回错误。路由:需要获取监听端口对应的Backend及其Slot,根据端口、Key和Redis命令选择一个Backend,将请求路由到对应的Backend,并将结果返回给客户端。配置监控:在Vintage中监控这个Proxy的配置,包括端口变化、端口和Backend变化、slot变化等,并通知端口监控模块和路由模块。指标监控:指标需要发送给Graphite进行监控。日志记录:生成用于跟踪目的的日志文件。Redis存储:我们仍然使用我们内部修改的Redis版本。与之前的在线版本相比,这次我们增加了Mememory、内存编码优化等官方功能,以及一些新的内部功能。关于集群管理,不管是Redis还是MySQL,任何对资源的管理都可以用这个来概括,包括五个部分:资源申请、资源分配、业务在线资源查询、资源变更、业务申请。业务、QPS、数据类型的唯一标识是什么?基于这些考虑,我们将对其进行分配、配置和部署。基于我们之前做过的这么多的优化和平台服务,用下图来概括比较合适,相当于服务的高可用、高性能和可扩展性。我们基本上都是用之前的方案来解决的。.未来展望不管是一开始的MySQL,还是后来的Oracle,这些都离不开SQL。如果能很好的解决数据一致性问题,Redis的应用场景会更广。现在很多公司都对Raft做了二次开发,我们以后也会在这方面进行投入。借用两句话来结束今天的演讲:“数据库其实就是要求你以最快的速度存储数据,然后以最方便的方式调出数据。”作者:兰江洲简介:新浪微博核心信息流与广告数据库业务线负责人,主要负责MySQL、NoSQL、TiDB的自动化开发与运维,参与相关代码开发Redis、counteservice_ssd和Memcacheq。目前,他专注于分布式系统。
