-
安装和配置Redis前言:windows没有32位的Redis,所以32位机器的朋友考虑换一下机器。1、windows环境下的redis需要去github上获取:https://github.com/MicrosoftArchive/redis/releases2、直接下载最新版本,选择.msi格式的安装版本(另外一种.zip通过命令安装)3、直接运行.msi的安装包,一直next,直到下面界面,勾选上再next4、这一步选择端口,然后next(后面可以通过配置文件修改的)5、选择最大缓存容量,点击next(后面可以通过配置文件修改的)6、接下来可以点击install进行安装了,安装完毕后,进入redis安装目录找到配置文件,注意是redis.windows-service.conf,不是redis.windows.conf,后者是以非系统服务方式启动程序使用的配置文件.7、在配置文件中,找到requirepass foobared字样,在其后面追加一行,输入requirepass 123456。这是访问Redis时所需的密码,一般测试情况下可以不用设定密码。不过,即使是作为本地访问,也建议设定一个密码。此处以简单的123456来演示,然后保存退出8、进入计算机服务中(右键计算机-->管理-->服务和应用程序-->服务),再在右侧找到Redis名称的服务,查看启动情况。如未启动,则手动启动之。正常情况下,服务应该正常启动并运行了,但是因为前面修改过配置文件,需要重启服务,切记9、测试一下redis能否正常工作。用命令进入redis安装路径,输入redis-cli并回车(redis-cli是客户端程序)如图正常提示进入,并显示正确端口号,则表示服务已经启动。10、使用服务前需要先通过密码验证。输入“auth 123456”并回车(123456是之前设定的密码)。返回提示OK表示验证通过。然后再验证set和get。一切正常,说明可以开始随便盘它了。可视化工具redis常用的可视化工具 Redis Desktop Manager。0.9.4以上要给钱的。下载地址:https://github.com/uglide/RedisDesktopManager/releases/download/0.9.3/redis-desktop-manager-0.9.3.817.exe这位朋友的百度云盘里也有(感谢):https://blog.csdn.net/u012688704/article/details/82251338下载完成之后直接点击安装包安装即可,无需任何配置。然后直接连接Redis就可以盘了————————————————原文链接:https://blog.csdn.net/weixin_41381863/article/details/88231397
-
【摘要】 用户画像存储是推荐业务核心,但开源Redis无法胜任。华为云高斯Redis是最佳存储选型,轻松降本60%,同时获得企业级高稳定性。本文分享自华为云社区《华为云GaussDB(for Redis)揭秘第23期:用GaussDB(for Redis)存画像,推荐业务轻松降本60%》,作者: 高斯Redis官方博客 。一、什么是推荐系统 不知道大家有没有过这样的经历,当你前脚刚在某电商网站买了一个手机,过两天之后你再打开该电商网站,显示在首页的上必定有耳机、手机壳和蓝牙音箱等手机配件。如果你买的不是手机,而是一件衣服的话,下次打开显示的必定是和该衣服非常搭配的裤子和鞋子等。聪明的你不禁要问,为何电商网站如此强大,竟然能提前预知你的偏好,并且给你推荐你可能喜欢的商品?其实在这背后,都离不开那强大的推荐系统。 什么是推荐系统呢?首先我们来看看维基百科对推荐系统的定义:推荐系统是一种信息过滤系统,可以根据用户历史行为用于预测用户对物品的“评分”或“偏好”。简单来说,如果你是一个电子发烧友,那么系统肯定会给你推荐各种新鲜出炉的3C产品,如果你是一个coder,那么你的页面肯定充斥各种编程大全的书籍。推荐系统近年来非常流行,应用于各行各业,它推荐的对象包括:电影、音乐、新闻、书籍、学术论文、搜索查询、分众分类、电商购物和游戏业务等。二、推荐系统的架构 了解了什么是推荐系统之后,接下来我们继续介绍下推荐系统的架构,以游戏行业为例,一个典型的游戏业务中的推荐系统架构设计如下: 推荐系统主要由3部分组成,分别是:行为日志收集、特征生产和特征消费。(1)行为日志收集 大数据业务收集用户的行为日志,分析获得用户画像,并且将这些用户画像保存在分布式文件系统HDFS中。(2)特征生产 工程业务负责为大数据业务提供一套接口调用,主要是“灌库”,即定时或按一定逻辑把HDFS中的用户画像加工成特征,灌入工程业务负责的“KV存储”。(3)特征消费 工程业务团队还负责将算法团队的推荐模型进行工程落地,他们开发线上推理组件,从KV存储中提取特征数据,分析计算,最终得出推荐结论,展示给用户。三、推荐系统的存储痛点 上一节中我们介绍了游戏行业中推荐系统的架构,这也是推荐系统的一个典型架构。从架构图可以看出,KV存储在整套链路中,承载着重要的承上启下作用。然而,推荐系统中的KV存储却存在着两个大的痛点,第一个是成本高,第二个是扩容慢。(1)成本高 首先第一个是成本高的问题。通常我们搭建KV存储的话首选都是选择自建一个开源Redis集群作为KV存储系统。一方面,开源Redis的数据全部放在内存中,众所周知内存的存储成本是非常昂贵的,内存只适合用于存储少量数据,如果数据量大的话,数据的存储成本将成为企业的负担;另一方面由于开源Redis在进行AOF文件重写的过程中,存在fork机制,导致开源Redis在AOF文件重写时,其内存利用率只有50%,这就进一步使增加了开源Redis的内存使用成本。(2)扩容慢 除了成本比较高,开源Redis还存在扩容慢的问题。在自建的开源Redis集群中,随着业务增长,KV存储的容量不得不进行扩容。但是由于原生Redis本身的架构特点,在扩容过程中难免要发生key的跨slot迁移,如下图所示: 如果发生slot迁移,需要耗时很久并且业务受影响时间长。四、为什么推荐高斯Redis 上一节中我们介绍了推荐系统的存储痛点,如何解决呢?要解决这两个痛点,无非是需要进行降本增效,而高斯Redis似乎就是为了解决这些问题而生。(1)降本 就降本而言,高斯Redis从两个方面降低KV数据的存储成本: 第一个方面,高斯Redis的所有数据全部落在磁盘,相比开源Redis存在内存中而言,其成本降低了75%~90%,拥有极大的价格优势。举个例子来说,一个512GB的开源Redis集群,其成本几乎要5w/月,而相同规格的实例,如果替换成高斯Redis,其成本节约40%以上,几乎节省了一个人力成本在里面。下面这张表格是不同层级的存储器之间的成本对比图。 另一方面,在推荐系统中,KV数据库主要存储的是用户画像的信息,而这些信息基本上都是经过Protobuf序列化后的信息,而高斯Redis自带的数据压缩功能,对序列化后的信息可以进行高压缩比的压缩,相对于开源Redis而言,存储空间仅仅为开源Redis的70%到85%,这又进一步降低了KV数据的存储成本。(2)增效 除了降本,另一方面就是增效了。众所周知,开源Redis的架构中,如下图所示,其存储和计算是不分离的,这就导致节点进行扩容的时候,会发生分片的迁移,从而导致业务会受到影响。 高斯Redis为了解决这个问题,采用了存算分离的架构,如下图所示: 在采用存算分离的架构下,底层数据可以被任意上层计算节点访问,扩容过程不发生数据拷贝搬迁,速度极快。节点扩容时,分钟级完成,业务只有秒级感知。容量扩容时,业务0感知。无论是扩节点还是扩磁盘容量,对业务造成的影响几乎为0。五、总结 本文简单介绍了推荐系统是什么,并且以游戏行业中的推荐系统为例,介绍了推荐系统的架构和其存在的存储痛点,并且介绍了高斯Redis如何解决该存储痛点。在这个大数据时代,推荐系统的应用会越来越多,作为推荐系统的数据存储,高斯Redis解决了开源Redis所存在的缺点,为推荐系统提供了有力的技术支撑。六、附录本文作者:华为云数据库GaussDB(for Redis)团队杭州/西安/深圳简历投递:yuwenlong4@huawei.com更多产品信息,欢迎访问官方博客:bbs.huaweicloud.com/blogs/248875
-
一、什么是推荐系统不知道大家有没有过这样的经历,当你前脚刚在某电商网站买了一个手机,过两天再打开该电商网站,首页推荐显示的必定有耳机、手机壳、蓝牙音箱等手机配件。如果你买的不是手机,而是一件衣服,那么下次打开电商网站显示的,必定是和衣服搭配的裤子和鞋子等。聪明的你不禁要问,为何电商网站如此强大,竟能提前预知你的偏好,并且给你推荐你可能喜欢的商品?其实在这背后,都离不开那强大的推荐系统。什么是推荐系统?首先我们来看看维基百科上的定义:推荐系统是一种信息过滤系统,可以根据用户历史行为预测用户对物品的“评分”或“偏好”。简单来说,如果你是一个电子发烧友,那么系统肯定会给你推荐各种新鲜出炉的3C产品,如果你是一个coder,那么你的页面肯定充满各种编程大全的书籍。推荐系统近年来非常流行,应用于各行各业,推荐的对象包括:电影、音乐、新闻、书籍、学术论文、搜索查询、分众分类、电商购物和游戏业务等。二、推荐系统的架构了解了什么是推荐系统之后,接下来我们继续介绍下推荐系统的架构,以游戏行业为例,一个典型的游戏业务的推荐系统架构设计如下: 推荐系统主要由3部分组成,分别是:行为日志收集、特征生产和特征消费。 01.行为日志大数据业务通过收集用户的行为日志,分析获得用户画像,并且将这些用户画像保存在分布式文件系统HDFS中。02.特征生产工程业务负责为大数据业务提供一套接口调用,主要是“灌库”,即定时或按一定逻辑把HDFS中的用户画像加工成特征,灌入工程业务负责的“KV存储”。 03.特征消费工程业务团队还负责将算法团队的推荐模型进行工程落地,他们开发线上推理组件,从KV存储中提取特征数据、分析计算,最终得出推荐结论,展示给用户。 三、推荐系统的存储痛点上一节中我们介绍了游戏行业中推荐系统的架构,这也是推荐系统的一个典型架构。从架构图可以看出,KV存储在整套链路中,承载着重要的承上启下作用。然而,推荐系统中的KV存储却存在着两个大的痛点,第一个是成本高,第二个是扩容慢。 01.成本高首先第一个是成本高的问题。通常我们搭建KV存储的首选是选择自建一个开源Redis集群作为KV存储系统。一方面,开源Redis的数据全部放在内存中,众所周知内存的存储成本非常昂贵,只适用于存储少量数据,如果数据量大,存储成本将成为企业的负担;另一方面,开源Redis在进行AOF文件重写的过程中存在fork机制,导致开源Redis在AOF文件重写时,其内存利用率只有50%,这就进一步使增加了开源Redis的内存使用成本。02.扩容慢除了成本比较高,开源Redis还存在扩容慢的问题,在自建的开源Redis集群中,随着业务增长,KV存储的容量不得不进行扩容。但由于原生Redis本身的架构特点,在扩容过程中难免要发生key的跨slot迁移,如下图所示,跨slot迁移需要耗时很久并且业务受影响时间长。 四、为什么推荐GaussDB(for Redis)知道了推荐系统的痛点所在,该如何解决呢?究其根本是降本增效,而GaussDB(for Redis)可以说是为解决这些问题而生。01.降本GaussDB(for Redis)从两个方面降低KV数据的存储成本: 第一个方面,GaussDB(for Redis)的所有数据全部落在存储,相比开源Redis数据存放在内存中,其成本降低了75%~90%,形成极大的价格优势。举个例子,一个512GB的开源Redis集群,其成本几乎要5w/月,而相同规格的实例,如果替换成GaussDB(for Redis),其成本节约40%以上,几乎节省了一个人力成本在里面。下面这张表格是不同层级存储器之间的成本对比图。 另一方面,在推荐系统中,KV数据主要存储的是用户画像的信息,这些信息基本上都是经过Protobuf序列化后的信息,而GaussDB(for Redis)自带的数据压缩功能,可以对序列化后的信息进行高压缩比的压缩,实际占用空间仅为开源Redis的50%左右,这又进一步降低了KV数据的存储成本。 02.增效除了降本,另一方面就是增效了。众所周知,开源Redis的架构中,如下图所示,其存储和计算是不分离的,这就导致节点进行扩容的时候,会发生分片的迁移,从而导致业务会受到影响。GaussDB(for Redis)为了解决这个问题,采用了存算分离的架构,如下图所示:在存算分离的架构下,底层数据可以被任意上层计算节点访问,扩容过程不发生数据拷贝搬迁,速度极快;同时还做到分钟级节点扩容,业务秒级感知;存储扩容业务0感知。无论是扩节点还是扩存储容量,对业务的影响几乎为0。五、总结本文简单介绍了推荐系统是什么,并以游戏业务为例,阐明了推荐系统的架构和存在的存储痛点,同时GaussDB(for Redis)是如何解决这些存储痛点的。在大数据时代,推荐系统的应用场景将会越来越多,作为推荐系统的数据存储,GaussDB(for Redis)完美弥补了开源Redis的短板,能够为推荐系统提供强有力的技术支撑。
-
【摘要】 2018年,王思聪的冲顶大会,西瓜视频的百万英雄,再到映客的芝士超人,直播答题火爆全网。我服务的一家电商公司也加入了这次热潮,技术团队研发了直播答题功能。答题结束之后,红包会以红包雨的形式落下,用户点击屏幕上落下的红包,若抢到红包,红包会以现金的形式进入用户账户。 红包雨是一个典型的高并发场景,短时间内有海量请求访问服务端,技术团队为了让系统运行顺畅,抢红包采用了基于 Redis + Lua...本文分享自华为云社区《红包雨中:Redis 和 Lua 的邂逅》,作者:勇哥java实战分享。2018年,王思聪的冲顶大会,西瓜视频的百万英雄,再到映客的芝士超人,直播答题火爆全网。我服务的一家电商公司也加入了这次热潮,技术团队研发了直播答题功能。答题结束之后,红包会以红包雨的形式落下,用户点击屏幕上落下的红包,若抢到红包,红包会以现金的形式进入用户账户。红包雨是一个典型的高并发场景,短时间内有海量请求访问服务端,技术团队为了让系统运行顺畅,抢红包采用了基于 Redis + Lua 脚本的设计方案。1 整体流程我们分析下抢红包的整体流程 :运营系统配置红包雨活动总金额以及红包个数,提前计算出各个红包的金额并存储到 Redis 中;抢红包雨界面,用户点击屏幕上落下的红包,发起抢红包请求;TCP 网关接收抢红包请求后,调用答题系统抢红包 dubbo 服务,抢红包服务本质上就是执行 Lua 脚本,将结果通过 TCP 网关返回给前端;用户若抢到红包,异步任务会从 Redis 中 获取抢得的红包信息,调用余额系统,将金额返回到用户账户。2 红包 Redis 设计抢红包有如下规则:同一活动,用户只能抢红包一次 ;红包数量有限,一个红包只能被一个用户抢到。如下图,我们设计三种数据类型:运营预分配红包列表 ;队列元素 json 数据格式 :1{2 //红包编号3 redPacketId : '365628617880842241' 4 //红包金额5 amount : '12.21' 6}用户红包领取记录列表;队列元素 json 数据格式:1{2 //红包编号3 redPacketId : '365628617880842241'4 //红包金额5 amount : '12.21',6 //用户编号7 userId : '265628617882842248'8}用户红包防重 Hash 表;抢红包 Redis 操作流程 :通过 hexist 命令判断红包领取记录防重 Hash 表中用户是否领取过红包 ,若用户未领取过红包,流程继续;从运营预分配红包列表 rpop 出一条红包数据 ;操作红包领取记录防重 Hash 表 ,调用 HSET 命令存储用户领取记录;将红包领取信息 lpush 进入用户红包领取记录列表。抢红包的过程 ,需要重点关注如下几点 :执行多个命令,是否可以保证原子性 , 若一个命令执行失败,是否可以回滚;在执行过程中,高并发场景下,是否可以保持隔离性;后面的步骤依赖前面步骤的结果。Redis 支持两种模式 : 事务模式 和 Lua 脚本,接下来,我们一一展开。3 事务原理Redis 的事务包含如下命令:序号命令及描述1MULTI 标记一个事务块的开始。2EXEC 执行所有事务块内的命令。3DISCARD 取消事务,放弃执行事务块内的所有命令。4WATCH key [key ...] 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。5UNWATCH 取消 WATCH 命令对所有 key 的监视。事务包含三个阶段:事务开启,使用 MULTI , 该命令标志着执行该命令的客户端从非事务状态切换至事务状态 ;命令入队,MULTI 开启事务之后,客户端的命令并不会被立即执行,而是放入一个事务队列 ;执行事务或者丢弃。如果收到 EXEC 的命令,事务队列里的命令将会被执行 ,如果是 DISCARD 则事务被丢弃。下面展示一个事务的例子。1redis> MULTI 2OK3redis> SET msg "hello world"4QUEUED5redis> GET msg6QUEUED7redis> EXEC81) OK91) hello world这里有一个疑问?在开启事务的时候,Redis key 可以被修改吗?在事务执行 EXEC 命令之前 ,Redis key 依然可以被修改。在事务开启之前,我们可以 watch 命令监听 Redis key 。在事务执行之前,我们修改 key 值 ,事务执行失败,返回 nil 。通过上面的例子,watch 命令可以实现类似乐观锁的效果 。4 事务的ACID4.1 原子性原子性是指:一个事务中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。第一个例子:在执行 EXEC 命令前,客户端发送的操作命令错误,比如:语法错误或者使用了不存在的命令。1redis> MULTI2OK3redis> SET msg "other msg"4QUEUED5redis> wrongcommand ### 故意写错误的命令6(error) ERR unknown command 'wrongcommand' 7redis> EXEC8(error) EXECABORT Transaction discarded because of previous errors.9redis> GET msg10"hello world"在这个例子中,我们使用了不存在的命令,导致入队失败,整个事务都将无法执行 。第二个例子:事务操作入队时,命令和操作的数据类型不匹配 ,入队列正常,但执行 EXEC 命令异常 。1redis> MULTI 2OK3redis> SET msg "other msg"4QUEUED5redis> SET mystring "I am a string"6QUEUED7redis> HMSET mystring name "test"8QUEUED9redis> SET msg "after"10QUEUED11redis> EXEC121) OK132) OK143) (error) WRONGTYPE Operation against a key holding the wrong kind of value154) OK16redis> GET msg17"after"这个例子里,Redis 在执行 EXEC 命令时,如果出现了错误,Redis 不会终止其它命令的执行,事务也不会因为某个命令执行失败而回滚 。综上,我对 Redis 事务原子性的理解如下:命令入队时报错, 会放弃事务执行,保证原子性;命令入队时正常,执行 EXEC 命令后报错,不保证原子性;也就是:Redis 事务在特定条件下,才具备一定的原子性 。4.2 隔离性数据库的隔离性是指:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别 ,分别是:未提交读(read uncommitted)提交读(read committed)可重复读(repeatable read)串行化(serializable)首先,需要明确一点:Redis 并没有事务隔离级别的概念。这里我们讨论 Redis 的隔离性是指:并发场景下,事务之间是否可以做到互不干扰。我们可以将事务执行可以分为 EXEC 命令执行前和 EXEC 命令执行后两个阶段,分开讨论。EXEC 命令执行前在事务原理这一小节,我们发现在事务执行之前 ,Redis key 依然可以被修改。此时,可以使用 WATCH 机制来实现乐观锁的效果。EXEC 命令执行后因为 Redis 是单线程执行操作命令, EXEC 命令执行后,Redis 会保证命令队列中的所有命令执行完 。 这样就可以保证事务的隔离性。4.3 持久性数据库的持久性是指 :事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。Redis 的数据是否持久化取决于 Redis 的持久化配置模式 。没有配置 RDB 或者 AOF ,事务的持久性无法保证;使用了 RDB模式,在一个事务执行后,下一次的 RDB 快照还未执行前,如果发生了实例宕机,事务的持久性同样无法保证;使用了 AOF 模式;AOF 模式的三种配置选项 no 、everysec 都会存在数据丢失的情况 。always 可以保证事务的持久性,但因为性能太差,在生产环境一般不推荐使用。综上,redis 事务的持久性是无法保证的 。4.4 一致性一致性的概念一直很让人困惑,在我搜寻的资料里,有两类不同的定义。维基百科我们先看下维基百科上一致性的定义:Consistency ensures that a transaction can only bring the database from one valid state to another, maintaining database invariants: any data written to the database must be valid according to all defined rules, including constraints, cascades, triggers, and any combination thereof. This prevents database corruption by an illegal transaction, but does not guarantee that a transaction is correct. Referential integrity guarantees the primary key – foreign key relationship.在这段文字里,一致性的核心是“约束”,“any data written to the database must be valid according to all defined rules ”。如何理解约束?这里引用知乎问题 如何理解数据库的内部一致性和外部一致性,蚂蚁金服 OceanBase 研发专家韩富晟回答的一段话:“约束”由数据库的使用者告诉数据库,使用者要求数据一定符合这样或者那样的约束。当数据发生修改时,数据库会检查数据是否还符合约束条件,如果约束条件不再被满足,那么修改操作不会发生。关系数据库最常见的两类约束是“唯一性约束”和“完整性约束”,表格中定义的主键和唯一键都保证了指定的数据项绝不会出现重复,表格之间定义的参照完整性也保证了同一个属性在不同表格中的一致性。“ Consistency in ACID ”是如此的好用,以至于已经融化在大部分使用者的血液里了,使用者会在表格设计的时候自觉的加上需要的约束条件,数据库也会严格的执行这个约束条件。所以事务的一致性和预先定义的约束有关,保证了约束即保证了一致性。我们细细品一品这句话: This prevents database corruption by an illegal transaction, but does not guarantee that a transaction is correct。写到这里可能大家还是有点模糊,我们举经典转账的案例。我们开启一个事务,张三和李四账号上的初始余额都是1000元,并且余额字段没有任何约束。张三给李四转账1200元。张三的余额更新为 -200 , 李四的余额更新为2200。从应用层面来看,这个事务明显不合法,因为现实场景中,用户余额不可能小于 0 , 但是它完全遵循数据库的约束,所以从数据库层面来看,这个事务依然保证了一致性。Redis 的事务一致性是指:Redis 事务在执行过程中符合数据库的约束,没有包含非法或者无效的错误数据。我们分三种异常场景分别讨论:执行 EXEC 命令前,客户端发送的操作命令错误,事务终止,数据保持一致性;执行 EXEC 命令后,命令和操作的数据类型不匹配,错误的命令会报错,但事务不会因为错误的命令而终止,而是会继续执行。正确的命令正常执行,错误的命令报错,从这个角度来看,数据也可以保持一致性;执行事务的过程中,Redis 服务宕机。这里需要考虑服务配置的持久化模式。无持久化的内存模式:服务重启之后,数据库没有保持数据,因此数据都是保持一致性的;RDB / AOF 模式: 服务重启后,Redis 通过 RDB / AOF 文件恢复数据,数据库会还原到一致的状态。综上所述,在一致性的核心是约束的语意下,Redis 的事务可以保证一致性。《设计数据密集型应用》这本书是分布式系统入门的神书。在事务这一章节有一段关于 ACID 的解释:Atomicity, isolation, and durability are properties of the database,whereas consistency (in the ACID sense) is a property of the application. The application may rely on the database’s atomicity and isolation properties in order to achieve consistency, but it’s not up to the database alone. Thus, the letter C doesn’t really belong in ACID.原子性,隔离性和持久性是数据库的属性,而一致性(在 ACID 意义上)是应用程序的属性。应用可能依赖数据库的原子性和隔离属性来实现一致性,但这并不仅取决于数据库。因此,字母 C 不属于 ACID 。很多时候,我们一直在纠结的一致性,其实就是指符合现实世界的一致性,现实世界的一致性才是事务追求的最终目标。为了实现现实世界的一致性,需要满足如下几点:保证原子性,持久性和隔离性,如果这些特征都无法保证,那么事务的一致性也无法保证;数据库本身的约束,比如字符串长度不能超过列的限制或者唯一性约束;业务层面同样需要进行保障 。4.5 总结我们通常称 Redis 为内存数据库 , 不同于传统的关系数据库,为了提供了更高的性能,更快的写入速度,在设计和实现层面做了一些平衡,并不能完全支持事务的 ACID。Redis 的事务具备如下特点:保证隔离性;无法保证持久性;具备了一定的原子性,但不支持回滚;一致性的概念有分歧,假设在一致性的核心是约束的语意下,Redis 的事务可以保证一致性。另外,在抢红包的场景下, 因为每个步骤需要依赖上一个步骤返回的结果,需要通过 watch 来实现乐观锁 ,从工程角度来看, Redis 事务并不适合该业务场景。5 Lua 脚本5.1 简介“ Lua ” 在葡萄牙语中是“月亮”的意思,1993年由巴西的 Pontifical Catholic University 开发。该语言的设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。Lua 脚本可以很容易的被 C/C ++ 代码调用,也可以反过来调用 C/C++ 的函数,这使得 Lua 在应用程序中可以被广泛应用。不仅仅作为扩展脚本,也可以作为普通的配置文件,代替 XML, Ini 等文件格式,并且更容易理解和维护。Lua 由标准 C 编写而成,代码简洁优美,几乎在所有操作系统和平台上都可以编译,运行。一个完整的 Lua 解释器不过 200 k,在目前所有脚本引擎中,Lua 的速度是最快的。这一切都决定了 Lua 是作为嵌入式脚本的最佳选择。Lua 脚本在游戏领域大放异彩,大家耳熟能详的《大话西游II》,《魔兽世界》都大量使用 Lua 脚本。Java 后端工程师接触过的 api 网关,比如 Openresty ,Kong 都可以看到 Lua 脚本的身影。从 Redis 2.6.0 版本开始, Redis内置的 Lua 解释器,可以实现在 Redis 中运行 Lua 脚本。使用 Lua 脚本的好处 :减少网络开销。将多个请求通过脚本的形式一次发送,减少网络时延。原子操作。Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。复用。客户端发送的脚本会永久存在 Redis 中,其他客户端可以复用这一脚本而不需要使用代码完成相同的逻辑。Redis Lua 脚本常用命令:序号命令及描述1EVAL script numkeys key [key ...] arg [arg ...] 执行 Lua 脚本。2EVALSHA sha1 numkeys key [key ...] arg [arg ...] 执行 Lua 脚本。3SCRIPT EXISTS script [script ...] 查看指定的脚本是否已经被保存在缓存当中。4SCRIPT FLUSH 从脚本缓存中移除所有脚本。5SCRIPT KILL 杀死当前正在运行的 Lua 脚本。6SCRIPT LOAD script 将脚本 script 添加到脚本缓存中,但并不立即执行这个脚本。5.2 EVAL 命令命令格式:1EVAL script numkeys key [key ...] arg [arg ...]说明:script是第一个参数,为 Lua 5.1脚本;第二个参数numkeys指定后续参数有几个 key;key [key ...],是要操作的键,可以指定多个,在 Lua 脚本中通过KEYS[1], KEYS[2]获取;arg [arg ...],参数,在 Lua 脚本中通过ARGV[1], ARGV[2]获取。简单实例:1redis> eval "return ARGV[1]" 0 100 2"100"3redis> eval "return {ARGV[1],ARGV[2]}" 0 100 10141) "100"52) "101"6redis> eval "return {KEYS[1],KEYS[2],ARGV[1]}" 2 key1 key2 first second71) "key1"82) "key2"93) "first"104) "second"下面演示下 Lua 如何调用 Redis 命令 ,通过redis.call()来执行了 Redis 命令 。1redis> set mystring 'hello world'2OK3redis> get mystring4"hello world"5redis> EVAL "return redis.call('GET',KEYS[1])" 1 mystring6"hello world"7redis> EVAL "return redis.call('GET','mystring')" 08"hello world"5.3 EVALSHA 命令使用 EVAL 命令每次请求都需要传输 Lua 脚本 ,若 Lua 脚本过长,不仅会消耗网络带宽,而且也会对 Redis 的性能造成一定的影响。思路是先将 Lua 脚本先缓存起来 , 返回给客户端 Lua 脚本的 sha1 摘要。 客户端存储脚本的 sha1 摘要 ,每次请求执行 EVALSHA 命令即可。EVALSHA 命令基本语法如下:redis> EVALSHA sha1 numkeys key [key ...] arg [arg ...] 实例如下:redis> SCRIPT LOAD "return 'hello world'" "5332031c6b470dc5a0dd9b4bf2030dea6d65de91" redis> EVALSHA 5332031c6b470dc5a0dd9b4bf2030dea6d65de91 0 "hello world"5.4 事务 VS Lua 脚本从定义上来说, Redis 中的脚本本身就是一种事务, 所以任何在事务里可以完成的事, 在脚本里面也能完成。 并且一般来说, 使用脚本要来得更简单,并且速度更快。因为脚本功能是 Redis 2.6 才引入的, 而事务功能则更早之前就存在了, 所以 Redis 才会同时存在两种处理事务的方法。不过我们并不打算在短时间内就移除事务功能, 因为事务提供了一种即使不使用脚本, 也可以避免竞争条件的方法, 而且事务本身的实现并不复杂。-- https://redis.io/Lua 脚本是另一种形式的事务,他具备一定的原子性,但脚本报错的情况下,事务并不会回滚。Lua 脚本可以保证隔离性,而且可以完美的支持后面的步骤依赖前面步骤的结果。综上,Lua 脚本是抢红包场景最优的解决方案。但在编写 Lua 脚本时,要注意如下两点:为了避免 Redis 阻塞,Lua 脚本业务逻辑不能过于复杂和耗时;仔细检查和测试 Lua 脚本 ,因为执行 Lua 脚本具备一定的原子性,不支持回滚。6 实战准备我选择 Redisson 3.12.0 版本作为 Redis 的客户端,在 Redisson 源码基础上做一层薄薄的封装。创建一个 PlatformScriptCommand 类, 用来执行 Lua 脚本。// 加载 Lua 脚本 String scriptLoad(String luaScript); // 执行 Lua 脚本 Object eval(String shardingkey, String luaScript, ReturnType returnType, List<Object> keys, Object... values); // 通过 sha1 摘要执行Lua脚本 Object evalSha(String shardingkey, String shaDigest, List<Object> keys, Object... values);这里为什么我们需要添加一个 shardingkey 参数呢 ?因为 Redis 集群模式下,我们需要定位哪一个节点执行 Lua 脚本。public int calcSlot(String key) { if (key == null) { return 0; } int start = key.indexOf('{'); if (start != -1) { int end = key.indexOf('}'); key = key.substring(start+1, end); } int result = CRC16.crc16(key.getBytes()) % MAX_SLOT; log.debug("slot {} for {}", result, key); return result; }7 抢红包脚本客户端执行 Lua 脚本后返回 json 字符串。用户抢红包成功{ "code":"0", //红包金额 "amount":"7.1", //红包编号 "redPacketId":"162339217730846210" }用户已领取过{ "code":"1" }用户抢红包失败{ "code":"-1" }Redis Lua 中内置了 cjson 函数,用于 json 的编解码。-- KEY[1]: 用户防重领取记录 local userHashKey = KEYS[1]; -- KEY[2]: 运营预分配红包列表 local redPacketOperatingKey = KEYS[2]; -- KEY[3]: 用户红包领取记录 local userAmountKey = KEYS[3]; -- KEY[4]: 用户编号 local userId = KEYS[4]; local result = {}; -- 判断用户是否领取过 if redis.call('hexists', userHashKey, userId) == 1 then result['code'] = '1'; return cjson.encode(result); else -- 从预分配红包中获取红包数据 local redPacket = redis.call('rpop', redPacketOperatingKey); if redPacket then local data = cjson.decode(redPacket); -- 加入用户ID信息 data['userId'] = userId; -- 把用户编号放到去重的哈希,value设置为红包编号 redis.call('hset', userHashKey, userId, data['redPacketId']); -- 用户和红包放到已消费队列里 redis.call('lpush', userAmountKey, cjson.encode(data)); -- 组装成功返回值 result['redPacketId'] = data['redPacketId']; result['code'] = '0'; result['amount'] = data['amount']; return cjson.encode(result); else -- 抢红包失败 result['code'] = '-1'; return cjson.encode(result); end end脚本编写过程中,难免会有疏漏,如何进行调试?个人建议两种方式结合进行。编写 junit 测试用例 ;从 Redis 3.2 开始,内置了 Lua debugger(简称LDB), 可以使用 Lua debugger 对 Lua 脚本进行调试。8 异步任务在 Redisson 基础上封装了两个类 ,简化开发者的使用成本。RedisMessageConsumer : 消费者类,配置监听队列名,以及对应的消费监听器String groupName = "userGroup"; String queueName = "userAmountQueue"; RedisMessageQueueBuilder buidler = redisClient.getRedisMessageQueueBuilder(); RedisMessageConsumer consumer = new RedisMessageConsumer(groupName, buidler); consumer.subscribe(queueName, userAmountMessageListener); consumer.start();RedisMessageListener : 消费监听器,编写业务消费代码public class UserAmountMessageListener implements RedisMessageListener { @Override public RedisConsumeAction onMessage(RedisMessage redisMessage) { try { String message = (String) redisMessage.getData(); // TODO 调用用户余额系统 // 返回消费成功 return RedisConsumeAction.CommitMessage; }catch (Exception e) { logger.error("userAmountService invoke error:", e); // 消费失败,执行重试操作 return RedisConsumeAction.ReconsumeLater; } } }9 写到最后"纸上得来终觉浅, 绝知此事要躬行" 。学习 Redis Lua 过程中,查询了很多资料,一个例子一个例子的实践,收获良多。非常坦诚的讲 , 写这篇文章之前,我对 Redis Lua 有很多想当然的理解,比如 Redis 的事务不能回滚就让我惊讶不已。所以当面对自己不熟悉的知识点时,不要轻易下结论,以谦卑的心态去学习,才是一个工程师需要的心态。同时,没有任何一项技术是完美的,在设计和编码之间,有这样或者那样的平衡,这才是真实的世界。
-
【摘要】 GaussDB(for Redis)推出双活方案,助力全球化业务部署,为您的数据资产保驾护航!本文分享自华为云社区《华为云GaussDB(for Redis)揭秘第22期:拔掉电源会怎样?GaussDB(for Redis)双活让你有备无患》,作者: 高斯Redis官方博客。一、GaussDB(for Redis)双活方案介绍 数据库系统是业务稳定运行的基石,其重要性不言而喻。然而,现实世界存在着的如断电、火灾,甚至是更小概率的地震等突发灾害,这些不稳定因素都会威胁到公司核心业务的连续性。 华为云GaussDB(for Redis)是采用存算分离架构的企业级KV数据库,使用方式上完全兼容开源Redis,同时稳定性全面超越开源Redis,此外还提供数据高可靠存储、秒级在线扩容等企业级能力。 在上半年的故障演练中,作为需求方的某内部重要业务部门对华为云KV数据库GaussDB(for Redis)进行了一系列严苛的可靠性测试。其中就包括模拟实例级断电,评测GaussDB(for Redis)的双活容灾能力。在故障演练中,GaussDB(for Redis)顺利通过测试,满足了业务部门对RTO和RPO等指标的严格要求。GaussDB(for Redis)支持双活容灾,即在两个实例之间建立数据同步链路,其中主实例支持读写,备实例只读。适用于以下两类业务场景: 1. 灾备系统:核心业务生产环境可靠性要求高,例如金融服务等。 2. 实例级读写分离:有超高并发访问诉求,例如广告竞价、推荐系统等。 在后续规划中,GaussDB(for Redis)还将进一步支持双主实例模式,实现双主双写,敬请期待。下面让我们来深入了解GaussDB(for Redis)的双活能力。二、GaussDB(for Redis)双活原理及评估指标1) 架构解析 常见的容灾方案有以下2种:同城容灾:由于距离近,通信质量好,比较容易实现数据零丢失。一般用于防范火灾、供电故障等人为破坏引起的灾难。异地容灾:由于主备数据中心之间距离远,通常会有少量的数据丢失,而异地灾备可以防范火灾、水灾、建筑物破坏、地震、战争等可能遇到的风险隐患,保障业务的连续性。 GaussDB(for Redis)双活方案支持同城容灾和异地容灾,下图展示了其技术原理: 可以看出,与开源Redis那种简单的命令转发不同,GaussDB(for Redis)的双活方案是基于WAL日志的数据同步,原理上更类似于MySQL数据库。在双活架构中,RsyncServer进程负责数据的全量和增量同步。数据同步链路采用华为云内部高速网络,同Region内仅毫秒级延迟。 GaussDB(for Redis)的双活功能有以下企业级特性:全量同步:支持秒级快照,且不影响原有集群的正常读写,发送速度快增量同步:监听日志写入变更,实时同步最新数据,毫秒级延迟key保序:主备实例采用多线程异步并发模式发送数据,按Key保序可靠传输:利用wal日志序号的单调递增特性,实现滑窗机制确保可靠传输高效传输:日志迭代、网络发送、DB增删监听等任务采用Reactor事件管理,全流程pipeline异常重传:解决网络抖动导致的丢包或延迟现象断点续传:每个DB持久化保存其应答过的日志序号,如发生宕机、网络隔离、进程重启等情况,可从该序号位置进行续传防止回流:同步到对端流量做特殊标记,防止回流到源端2) 技术指标 从技术上看,业界衡量容灾系统有两个主要指标:RPO:最多可能丢失数据的时长。RTO:从灾难发生到整个系统恢复正常所需要的最大时长。 一般而言,容灾系统能够提供较好的RTO和RPO指标。国际通用的容灾系统的评审标准SHARE 78(7个层次、8个原则),可以作为广大用户衡量和选择容灾解决方案的指标。 目前,在大部分场景下,GaussDB(for Redis)灾备方案位于6级,即只有在极端场景下才会丢失少量数据。3) 优势总结 根据前期市场调研,有些客户的业务场景基于开源Redis搭建集群级的灾备系统,从技术原理上评估可靠性很低,违背了灾备的初衷。与之相比,GaussDB(for Redis)提供企业级可靠的双活方案,优势如下:三、GaussDB(for Redis)双活搭建步骤 登录GaussDB NoSQL控制台,您可以很方便地搭建GaussDB(for Redis)双活系统: 1. 点击“购买数据库实例”,创建2个GaussDB(for Redis)实例(主、备),建议规格保持相同。 2. 进行网络配置,详细指导:https://support.huaweicloud.com/redisug-nosql/nosql_10_0502.html 3. 选中期望的主实例,如图操作,开始搭建双活关系: 4. 在“搭建双活关系”页面,选中期望的备实例,点击确定,即开始双活系统搭建。 注意:一旦双活系统开始搭建,备实例数据将被清空,请谨慎操作。 双活关系搭建完成后,除了用于容灾之外,其备实例可提供读服务,可以有效分担主实例的读压力。四、GaussDB(for Redis)双活技术演进 当前双活方案中,备实例在运行期间仅提供读服务,如果两个数据中心能同时提供写服务,那么将极大地提高资源利用率。当灾难发生时,双活系统提供极致的RPO和RTO,从而最大程度保障业务连续性。它具有资源利用率高的特点,支持数据冲突解决等功能。目前,GaussDB(for Redis)的双活多写功能已经在开发阶段,不久之后即将上线,请大家拭目以待。五、附录本文作者:华为云数据库GaussDB(for Redis)团队杭州/西安/深圳简历投递:yuwenlong4@huawei.com更多产品信息,欢迎访问官方博客:bbs.huaweicloud.com/blogs/248875
-
【摘要】 高斯Redis 搭建业务二级索引,低成本,高性能,实现性能与成本的双赢本文分享自华为云社区《华为云GaussDB(for Redis)揭秘第21期:使用高斯Redis实现二级索引》,作者:高斯Redis官方博客。一、背景提起索引,第一印象就是数据库的名词,但是,高斯Redis也可以实现二级索引!!!高斯Redis中的二级索引一般利用zset来实现。高斯Redis相比开源Redis有着更高的稳定性、以及成本优势,使用高斯Redis zset实现业务二级索引,可以获得性能与成本的双赢。索引的本质就是利用有序结构来加速查询,因而通过Zset结构高斯Redis可以轻松实现数值类型以及字符类型索引。• 数值类型索引(zset按分数排序):• 字符类型索引(分数相同时zset按字典序排序): 下面让我们切入两类经典业务场景,看看如何使用高斯Redis来构建稳定可靠的二级索引系统。 二、场景一:词典补全当在浏览器中键入查询时,浏览器通常会按照可能性推荐相同前缀的搜索,这种场景可以用高斯Redis二级索引功能实现。2.1 基本方案最简单的方法是将用户的每个查询添加到索引中。当需要进行用户输入补全推荐时,使用ZRANGEBYLEX执行范围查询即可。如果不希望返回太多条目,高斯Redis还支持使用LIMIT选项来减少结果数量。• 将用户搜索banana添加进索引:ZADD myindex 0 banana:1• 假设用户在搜索表单中输入“bit”,并且我们想提供可能以“bit”开头的搜索关键字。ZRANGEBYLEX myindex "[bit" "[bit\xff"即使用ZRANGEBYLEX进行范围查询,查询的区间为用户现在输入的字符串,以及相同的字符串加上一个尾随字节255(\xff)。通过这种方式,我们可以获得以用户键入字符串为前缀的所有字符串。2.2 与频率相关的词典补全实际应用中通常希望按照出现频率自动排序补全词条,同时可以清除不再流行的词条,并自动适应未来的输入。我们依然可以使用高斯Redis的ZSet结构实现这一目标,只是在索引结构中,不仅需要存储搜索词,还需要存储与之关联的频率。• 将用户搜索banana添加进索引 • 判断banana是否存在ZRANGEBYLEX myindex "[banana:" + LIMIT 0 1 • 假设banana不存在,添加banana:1,其中1是频率ZADD myindex 0 banana:1 • 假设banana存在,需要递增频率 若ZRANGEBYLEX myindex "[banana:" + LIMIT 0 1 中返回的频率为1 1)删除旧条目:ZREM myindex 0 banana:1 2)频率加一重新加入:ZADD myindex 0 banana:2 请注意,由于可能存在并发更新,因此应通过Lua脚本发送上述三个命令,用Lua script自动获得旧计数并增加分数后重新添加条目。• 假设用户在搜索表单中输入“banana”,并且我们想提供相似的搜索关键字。通过ZRANGEBYLEX获得结果后按频率排序。ZRANGEBYLEX myindex "[banana:" + LIMIT 0 10 1) "banana:123" 2) "banaooo:1" 3) "banned user:49" 4) "banning:89"• 使用流算法清除不常用输入。从返回的条目中随机选择一个条目,将其分数减1,然后将其与新分数重新添加。但是,如果新分数为0,我们需从列表中删除该条目。 • 若随机挑选的条目频率是1,如banaooo:1ZREM myindex 0 banaooo:1 • 若随机挑选的条目频率大于1,如banana:123ZREM myindex 0 banana:123 ZADD myindex 0 banana:122 从长远来看,该索引会包含热门搜索,如果热门搜索随时间变化,它还会自动适应。三、场景二:多维索引除了单一维度上的查询,高斯Redis同样支持在多维数据中的检索。例如,检索所有年龄在50至55岁之间,同时薪水在70000至85000之间的人。实现多维二级索引的关键是通过编码将二维的数据转化为一维数据,再基于高斯Redis zset存储。从可视化视角表示二维索引。下图空间中有一些点,它们代表我们的数据样本,其中x和y是两个变量,其最大值均为400。图片中的蓝色框代表我们的查询。我们希望查询x介于50和100之间,y介于100和300之间的所有点。3.1 数据编码 若插入数据点为x = 75和y = 2001) 填充0(数据最大为400,故填充3位) x = 075 y = 2002) 交织数字,以x表示最左边的数字,以y表示最左边的数字,依此类推,以便创建一个编码 027050若使用00和99替换最后两位,即027000 to 027099,map回x和y,即: x = 70-79 y = 200-209因此,针对x=70-79和y = 200-209的二维查询,可以通过编码map成027000 to 027099的一维查询,这可以通过高斯Redis的Zset结构轻松实现。同理,我们可以针对后四/六/etc位数字进行相同操作,从而获得更大范围。3) 使用二进制为获得更细的粒度,可以将数据用二进制表示,这样在替换数字时,每次会得到比原来大二倍的搜索范围。假设我们每个变量仅需要9位(以表示最多400个值的数字),我们采用二进制形式的数字将是: x = 75 -> 001001011 y = 200 -> 011001000交织后,000111000011001010让我们看看在交错表示中用0s ad 1s替换最后的2、4、6、8,...位时我们的范围是什么:3.2 添加新元素 若插入数据点为x = 75和y = 200 x = 75和y = 200二进制交织编码后为000111000011001010,ZADD myindex 0 0001110000110010103.3 查询 查询:x介于50和100之间,y介于100和300之间的所有点从索引中替换N位会给我们边长为2^(N/2)的搜索框。因此,我们要做的是检查搜索框较小的尺寸,并检查与该数字最接近的2的幂,并不断切分剩余空间,随后用ZRANGEBYLEX进行搜索。下面是示例代码:def spacequery(x0,y0,x1,y1,exp) bits=exp*2 x_start = x0/(2**exp) x_end = x1/(2**exp) y_start = y0/(2**exp) y_end = y1/(2**exp) (x_start..x_end).each{|x| (y_start..y_end).each{|y| x_range_start = x*(2**exp) x_range_end = x_range_start | ((2**exp)-1) y_range_start = y*(2**exp) y_range_end = y_range_start | ((2**exp)-1) puts "#{x},#{y} x from #{x_range_start} to #{x_range_end}, y from #{y_range_start} to #{y_range_end}" # Turn it into interleaved form for ZRANGEBYLEX query. # We assume we need 9 bits for each integer, so the final # interleaved representation will be 18 bits. xbin = x_range_start.to_s(2).rjust(9,'0') ybin = y_range_start.to_s(2).rjust(9,'0') s = xbin.split("").zip(ybin.split("")).flatten.compact.join("") # Now that we have the start of the range, calculate the end # by replacing the specified number of bits from 0 to 1. e = s[0..-(bits+1)]+("1"*bits) puts "ZRANGEBYLEX myindex [#{s} [#{e}" } } end spacequery(50,100,100,300,6) 四、总结本文介绍了如何通过高斯Redis搭建二级索引,二级索引在电商、图(hexastore)、游戏等领域具有广泛的应用场景,高斯redis现网亦有很多类似应用。高斯Redis基于存算分离架构,依托分布式存储池确保数据强一致,可方便的支持二级索引功能,为企业客户提供稳定可靠、超高并发,且能够极速弹性扩容的核心数据存储服务。附录本文作者:华为云数据库GaussDB(for Redis)团队杭州/西安/深圳简历投递:yuwenlong4@huawei.com更多产品信息,欢迎访问官方博客:bbs.huaweicloud.com/blogs/248875
-
文末附件可下载《GaussDB(for Redis)技术与应用解读》电子书你有没有想过这样一个问题:当下的互联网时代,网络覆盖了10.32亿人的生活,从衣食住行到日常社交,究竟是什么支撑着我们多样化的需求,它背后的底层系统到底是什么?是数据库。第一次听到这个答案可能会有点迟疑,细思一下,不无道理!随着短视频的兴起和支付系统的完善,越来越多人的物质消费、精神需求、情感导向等被算法一一俘获,“TA经济”在年轻人中迅速蔓延,人们也与数据库无数次不期而遇,比如:周末宅在家里,不想出门也不想做饭,饿了点外卖,查询附近的餐厅、附近的外卖商家,这些所有地理位置的查找,都需要数据库的存储;吃完饭躺在沙发上玩手机,微信朋友圈、微博、抖音以及头条等社交软件,系统及时且准确地推送着关注的好友或感兴趣的内容,这些Feed流信息的推送,也是数据库的功劳;到了晚上,各个直播间里“宝子们福利来喽,今天xx返场,全年最低价……”,这高亢的呼唤、迷人的价格让穿梭在直播间的看客们果断砍手,于是,账单来袭,不经意间又一次与数据库撞个满怀。数据库是存放数据的地方,它神秘但又无处不在,而类似于以上发生的场景,都有Redis忙碌的身影。提起Redis,互联网从业者里无人不知无人不晓。开源Redis作为一款经典的“缓存”产品,有着丰富的数据类型,不仅好用还支撑着众多业务的架构搭建,在游戏、电商、社交媒体及其他互联网领域有着重要地位。然而,在数据量和访问量指数级增长的今天,“容量有限,高并发写入容易OOM”、“内存昂贵、成本降不下来”、“可靠性有限,容易丢关键数据”的问题,也成了开源Redis绕不过的痛。不过,GaussDB(for Redis)完美地解决了开源Redis的难题。GaussDB(for Redis) 是华为云推出的一款,基于计算存储分离架构的云原生分布式数据库,兼容Redis生态的云原生NoSQL数据库,并提供强一致、三副本存储,高度保证数据的安全可靠。GaussDB(for Redis)突破了开源Redis的内存限制,通过将数据进行冷热分离,在保证热数据驻留计算节点内存满足业务低时延要求的同时,将冷数据置换入分布式存储池进行持久化存储,最大程度的降低使用成本,具有高兼容、高性价比、高可靠、弹性伸缩、高可用、冷热分离等特点。为了让开发者更加系统地了解GaussDB(for Redis),华为云数据库专家集经验与心血之所成,联合推出《GaussDB(for Redis)企业与技术解读》经典案例锦集,从入门篇、架构篇、测试篇、应用篇四个章节,全面解析GaussDB(for Redis)技术架构与实践,帮助开发者充分了解GaussDB(for Redis)的能力。附件可下载《GaussDB(for Redis)技术与应用解读》电子书
-
>摘要:盘点高斯Redis的秒级能力,包括扩容、备份、删除、启动等。 本文分享自华为云社区《华为云GaussDB(for Redis)揭秘第20期:六大秒级能力盘点》,作者: 高斯Redis官方博客。 # 引言 大家经常用时间类指标来评估数据库能力,诸如RTO/RPO、扩容耗时、变更操作对业务影响的时长等等,在高可用需求日益迫切的当下,这些都是评价数据库能力的重要参考。在KV数据库领域,华为云GaussDB(for Redis)基于存算分离的基础架构,在很多关键评估维度都具有“秒级”能力,今天便带大家了解一下。 # GaussDB(for Redis)秒级能力盘点 ## 秒启动 社区版Redis在宕机恢复的场景下,需要依赖AOF或RDB文件将数据全量加载到内存中,随着数据量增加,这个耗时经常需要数分钟甚至更久。 GaussDB(for Redis)节点拉起时无需加载全量数据,即可快速提供服务,启动时间受数据量影响极小,**大数据规模下亦可以达到秒级启动**,体验丝滑。 ## 秒删除 社区版Redis在删除/过期数据,尤其是大key的情况下会导致访问严重阻塞。 而GaussDB(for Redis)从根本上解决了大key删除/过期的操作隐患,在底层事先采用了“标记删除,异步回收”的逻辑,对任何数据执行删除/过期,都是立刻执行成功且0阻塞,因此完全不影响业务访问。在实测删除/过期一个大hash key(包含1000w个元素)时,GaussDB(for Redis)仅耗时毫秒。  图:GaussDB(for Redis)大key秒删 ## 秒备份 社区版Redis打快照时需要将全量数据dump到磁盘上,效率受到磁盘IO性能影响,耗时久。而且有着“fork问题”,造成性能抖动,导致容量利用率只有50%。 GaussDB(for Redis)备份基于底层文件系统的快照技术,记录某一时间点的数据状态,无需进行耗时的数据拷贝操作,真正做到秒级打快照。客户的数据快照文件自动上传到华为云OBS桶内,后续可用来一键恢复到新实例,相当便捷。  图:GaussDB(for Redis)时间点快照一键恢复 ## 秒扩容 社区版Redis计算、存储强耦合。比如64G规格装满了,当需要升128G规格时,升级过程不仅耗时久,严重影响业务;而且价格翻倍,存在大量算力成本浪费。 GaussDB(for Redis)提供以下3种灵活变更的“扩容”方式: - **1)数据快装满了?一键扩容量,秒级完成,不影响业务** GaussDB(for Redis)底层存储资源按配额形式提供使用,数据快写满场景,只需一键点击扩容,秒级获得更多容量。比如64G扩容到128G,秒级完成,不影响业务。同时无需为不必要的算力买单,扩容价格涨幅小,成本有极大优势。  图:GaussDB(for Redis)存储扩容 - **2)流量突发高峰?一键加节点,业务只受到秒级影响** GaussDB(for Redis)扩算力的最便捷方式即增加节点,秒级即可完成负载重新均衡,业务短暂受影响。  图:GaussDB(for Redis)算力扩容-添加节点 - **3)周期性做活动?节点规格按需伸缩,避免算力浪费** 在一些周期性的活动抢券场景,客户往往需要按周期进行规格升降,承载计划内的流量高峰,同时避免平时的浪费。GaussDB(for Redis)支持提升节点规格,比如16U升32U,客户可灵活按需扩容。  图:GaussDB(for Redis)算力扩容-节点规格变更 ## 故障秒恢复 GaussDB(for Redis)内部有全局的HA管控能力,计算节点故障秒级探测,发现故障后同样采用秒级接管的方式处理,秒级恢复业务侧正常访问。 ## 双活秒级RPO GaussDB(for Redis)支持region内/跨region容灾,即搭建稳定主从实例,提供企业级的高可用。通过高性能数据同步模块实现高性能主从实例同步,在高压力写入的业务压力下测试,有着RPO=10s的超高可靠性表现。 # 总结 经过上面的简单盘点可以看出,GaussDB(for Redis)在启动、大key删除、扩容变更等多种应用场景下均具有秒级能力,最小化业务影响,给用户提供企业级安全、稳定、可靠的使用体验。
-
>摘要:推荐使用GaussDB(for Redis)搞定"大key"存储,从根本上解决社区版Redis使用风险。 本文分享自华为云社区《[华为云GaussDB(for Redis)揭秘第18期:谁说Redis不能存大key](https://bbs.huaweicloud.com/blogs/358513?utm_source=csdn&utm_medium=bbs-ex&utm_campaign=database&utm_content=content)》,作者: 高斯Redis官方博客 。 # 一、社区版Redis的大key痛点 GaussDB(for Redis)专家小强最近有点忙,因为很多客户经理都来找他咨询社区版Redis的大key问题,且一个个都求知欲爆表:  小强一拍大腿:你们还真问对人了! 根据现网经验,生产环境因为大key导致的Redis事故屡见不鲜,其中典型的有扩容失败、请求阻塞、OOM宕机等等。早期业务规划不充分、消息队列消费不及时、未及时清理无效数据等原因都可能引入大key隐患。 **社区版Redis大key隐患常见于以下三大场景:** 1. 内存消耗不均衡,大key所在分片有OOM风险 2. 扩容时需要搬迁部分数据,大key耗时久,会导致访问阻塞甚至数据丢失 3. 删除或过期大key时,业务访问被长时间阻塞,甚至导致主从同步中断 社区版Redis架构并不适合可靠存储大key,业界也只能建议预防、拆分或及时清理大key。从客户视角出发,其实有些场景是需要大key的,例如企业ERP系统,海量货币汇率存储等。这时即使适当拆分,也避免不了较大HASH key存在。 # 二、华为云GaussDB(for Redis)的大Key解决方案 小强始终认为,好的产品应当尽量把复杂留给自己,把简单留给用户。因此,小强给每一位来咨询大key问题的客户经理都安利了better解决方案——**使用华为云企业级KV数据库GaussDB(for Redis)**。 大key场景下,GaussDB(for Redis)究竟比社区版Redis优秀在哪?**下面从3个角度深度分析**: ## 1. 高斯Redis支持大key存储,不用担心分片OOM - 社区版Redis存储大key会导致分片内存消耗不均,随着集群整体数据量水位提升,大key所在分片随时有OOM风险。  - 高斯Redis支持大key可靠存储,且不会导致分片OOM。需要注意的是,虽然高斯Redis适合用来可靠存储大key,但从网络链路角度考虑,业务应避免对大key执行诸如hgetall等风险命令。  ## 2. 高斯Redis在大key场景中也支持秒级无损扩容 - 社区版Redis在扩容时,由于要搬迁数据,此时画风是这样的:  图:社区版Redis在大key场景扩容,风险高 可以总结为:**有大key,开源Redis请谨慎扩容!** - 高斯Redis支持秒级扩容,并支持升规格、加节点、加存储容量3种手段灵活扩容,运维体验极佳。  图:高斯Redis在大key场景可安全扩容 从上图可以看出,高斯Redis采用计算、存储分离架构,扩容时不必搬迁任何数据,因此速度、稳定性都远超社区版Redis。**有大key,高斯Redis可以放心扩容!** ## 3. 高斯Redis删除/过期大key时,业务0阻塞 - 社区版Redis大key的删除/过期都会导致访问严重阻塞。实测删除/过期一个大hash key(包含1000w个元素),社区版Redis访问阻塞长达整整14秒。  图:社区版Redis在大key删除场景阻塞业务访问 虽然社区版Redis提供了“异步”的unlink命令能够一定程度上缓解大key阻塞问题,但unlink并非严格异步,例如对于zset类型(skiplist编码)以及全部string key都只能阻塞删除,风险不可控。 - 高斯Redis从根本上解决了大key删除/过期操作隐患。在高斯Redis中,对任何数据执行删除/过期,都是立刻执行成功且0阻塞。这是由于底层采用了真正的“标记删除”,因此完全不影响业务访问。实测删除/过期一个大hash key(包含1000w个元素),高斯Redis仅毫秒级。  # 三、总结 根据上述对比评测,可看出相比社区版Redis的实际表现,高斯Redis更适用于大key的可靠存储场景。除了能解决业务大key痛点外,高斯Redis在稳定性、可靠性、安全性等方面也有全面的提升。
-
原始数据存储在 DB 中(如 MySQL、Hbase 等),但 DB 的读写性能低、延迟高。比如 MySQL 在 4 核 8G 上的 TPS = 5000,QPS = 10000 左右,读写平均耗时 10~100 ms。用 Redis 作为缓存系统正好可以弥补 DB 的不足,「码哥」在自己的 MacBook Pro 2019 上执行 Redis 性能测试如下:$ redis-benchmark -t set,get -n 100000 -qSET: 107758.62 requests per second, p50=0.239 msecGET: 108813.92 requests per second, p50=0.239 msecTPS 和 QPS 达到 10 万,于是乎我们就引入缓存架构,在数据库中存储原始数据,同时在缓存总存储一份。当请求进来的时候,先从缓存中取数据,如果有则直接返回缓存中的数据。如果缓存中没数据,就去数据库中读取数据并写到缓存中,再返回结果。这样就天衣无缝了么?缓存的设计不当,将会导致严重后果,本文将介绍缓存使用中常见的三个问题和解决方案:缓存击穿(失效);缓存穿透;缓存雪崩。缓存击穿(失效)高并发流量,访问的这个数据是热点数据,请求的数据在 DB 中存在,但是 Redis 存的那一份已经过期,后端需要从 DB 从加载数据并写到 Redis。关键字:单一热点数据、高并发、数据失效但是由于高并发,可能会把 DB 压垮,导致服务不可用。如下图所示:解决方案过期时间 + 随机值对于热点数据,我们不设置过期时间,这样就可以把请求都放在缓存中处理,充分把 Redis 高吞吐量性能利用起来。或者过期时间再加一个随机值。设计缓存的过期时间时,使用公式:过期时间=baes 时间+随机时间。即相同业务数据写缓存时,在基础过期时间之上,再加一个随机的过期时间,让数据在未来一段时间内慢慢过期,避免瞬时全部过期,对 DB 造成过大压力预热预先把热门数据提前存入 Redis 中,并设热门数据的过期时间超大值。使用锁当发现缓存失效的时候,不是立即从数据库加载数据。而是先获取分布式锁,获取锁成功才执行数据库查询和写数据到缓存的操作,获取锁失败,则说明当前有线程在执行数据库查询操作,当前线程睡眠一段时间在重试。这样只让一个请求去数据库读取数据。伪代码如下:public Object getData(String id) { String desc = redis.get(id); // 缓存为空,过期了 if (desc == null) { // 互斥锁,只有一个请求可以成功 if (redis(lockName)) { try // 从数据库取出数据 desc = getFromDB(id); // 写到 Redis redis.set(id, desc, 60 * 60 * 24); } catch (Exception ex) { LogHelper.error(ex); } finally { // 确保最后删除,释放锁 redis.del(lockName); return desc; } } else { // 否则睡眠200ms,接着获取锁 Thread.sleep(200); return getData(id); } }}缓存穿透缓存穿透:意味着有特殊请求在查询一个不存在的数据,即数据不存在 Redis 也不存在于数据库。导致每次请求都会穿透到数据库,缓存成了摆设,对数据库产生很大压力从而影响正常服务。如图所示:解决方案缓存空值:当请求的数据不存在 Redis 也不存在数据库的时候,设置一个缺省值(比如:None)。当后续再次进行查询则直接返回空值或者缺省值。布隆过滤器:在数据写入数据库的同时将这个 ID 同步到到布隆过滤器中,当请求的 id 不存在布隆过滤器中则说明该请求查询的数据一定没有在数据库中保存,就不要去数据库查询了。BloomFilter 要缓存全量的 key,这就要求全量的 key 数量不大,100 亿 条数据以内最佳,因为 100 亿条数据大概要占用 3.5GB 的内存。说下布隆过滤器的原理吧BloomFilter 的算法是,首先分配一块内存空间做 bit 数组,数组的 bit 位初始值全部设为 0。加入元素时,采用 k 个相互独立的 Hash 函数计算,然后将元素 Hash 映射的 K 个位置全部设置为 1。检测 key 是否存在,仍然用这 k 个 Hash 函数计算出 k 个位置,如果位置全部为 1,则表明 key 存在,否则不存在。如下图所示:哈希函数会出现碰撞,所以布隆过滤器会存在误判。这里的误判率是指,BloomFilter 判断某个 key 存在,但它实际不存在的概率,因为它存的是 key 的 Hash 值,而非 key 的值。所以有概率存在这样的 key,它们内容不同,但多次 Hash 后的 Hash 值都相同。对于 BloomFilter 判断不存在的 key ,则是 100% 不存在的,反证法,如果这个 key 存在,那它每次 Hash 后对应的 Hash 值位置肯定是 1,而不会是 0。布隆过滤器判断存在不一定真的存在。缓存雪崩缓存雪崩指的是大量的请求无法在 Redis 缓存系统中处理,请求全部打到数据库,导致数据库压力激增,甚至宕机。出现该原因主要有两种:大量热点数据同时过期,导致大量请求需要查询数据库并写到缓存;Redis 故障宕机,缓存系统异常。缓存大量数据同时过期数据保存在缓存系统并设置了过期时间,但是由于在同时一刻,大量数据同时过期。系统就把请求全部打到数据库获取数据,并发量大的话就会导致数据库压力激增。缓存雪崩是发生在大量数据同时失效的场景,而缓存击穿(失效)是在某个热点数据失效的场景,这是他们最大的区别。如下图:缓存雪崩-大量缓存同时失效解决方案过期时间添加随机值要避免给大量的数据设置一样的过期时间,过期时间 = baes 时间+ 随机时间(较小的随机数,比如随机增加 1~5 分钟)。这样一来,就不会导致同一时刻热点数据全部失效,同时过期时间差别也不会太大,既保证了相近时间失效,又能满足业务需求。接口限流当访问的不是核心数据的时候,在查询的方法上加上接口限流保护。比如设置 10000 req/s。如果访问的是核心数据接口,缓存不存在允许从数据库中查询并设置到缓存中。这样的话,只有部分请求会发送到数据库,减少了压力。限流,就是指,我们在业务系统的请求入口前端控制每秒进入系统的请求数,避免过多的请求被发送到数据库。如下图所示:缓存雪崩-限流Redis 故障宕机一个 Redis 实例能支撑 10 万的 QPS,而一个数据库实例只有 1000 QPS。一旦 Redis 宕机,会导致大量请求打到数据库,从而发生缓存雪崩。解决方案对于缓存系统故障导致的缓存雪崩的解决方案有两种:服务熔断和接口限流;构建高可用缓存集群系统。服务熔断和限流在业务系统中,针对高并发的使用服务熔断来有损提供服务从而保证系统的可用性。服务熔断就是当从缓存获取数据发现异常,则直接返回错误数据给前端,防止所有流量打到数据库导致宕机。服务熔断和限流属于在发生了缓存雪崩,如何降低雪崩对数据库造成的影响的方案。构建高可用的缓存集群所以,缓存系统一定要构建一套 Redis 高可用集群,比如 《Redis 哨兵集群》或者 《Redis Cluster 集群》,如果 Redis 的主节点故障宕机了,从节点还可以切换成为主节点,继续提供缓存服务,避免了由于缓存实例宕机而导致的缓存雪崩问题。总结缓存穿透指的是数据库本就没有这个数据,请求直奔数据库,缓存系统形同虚设。缓存击穿(失效)指的是数据库有数据,缓存本应该也有数据,但是缓存过期了,Redis 这层流量防护屏障被击穿了,请求直奔数据库。缓存雪崩指的是大量的热点数据无法在 Redis 缓存中处理(大面积热点数据缓存失效、Redis 宕机),流量全部打到数据库,导致数据库极大压力。
-
作者 | 丕天Redis是目前最受欢迎的kv类数据库,当然它的功能越来越多,早已不限定在kv场景,消息队列就是Redis中一个重要的功能。Redis从2010年发布1.0版本就具备一个消息队列的雏形,随着10多年的迭代,其消息队列的功能也越来越完善,作为一个全内存的消息队列,适合应用与要求高吞吐、低延时的场景。我们来盘一下Redis消息队列功能的发展历程,历史版本有哪些不足,后续版本是如何来解决这些问题的。一、Redis 1.0 list从广义上来讲消息队列就是一个队列的数据结构,生产者从队列一端放入消息,消费者从另一端读取消息,消息保证先入先出的顺序,一个本地的list数据结构就是一个进程维度的消息队列,它可以让模块A写入消息,模块B消费消息,做到模块A/B的解耦与异步化。但想要做到应用级别的解耦和异步还需要一个消息队列的服务。1.list的特性Redis 1.0发布时就具备了list数据结构,应用A可以通过lpush写入消息,应用B通过rpop从队列中读取消息,每个消息只会被读取一次,而且是按照lpush写入的顺序读到。同时Redis的接口是并发安全的,可以同时有多个生产者向一个list中生产消息,多个消费者从list中读取消息。这里还有个问题,消费者要如何知道list中有消息了,需要不断轮询去查询吗。轮询无法保证消息被及时的处理,会增加延时,而且当list为空时,大部分轮询的请求都是无效请求,这种方式大量浪费了系统资源。好在Redis有brpop接口,该接口有一个参数是超时时间,如果list为空,那么Redis服务端不会立刻返回结果,它会等待list中有新数据后在返回或是等待最多一个超时时间后返回空。通过brpop接口实现了长轮询,该效果等同于服务端推送,消费者能立刻感知到新的消息,而且通过设置合理的超时时间,使系统资源的消耗降到很低。#基于list完成消息的生产和消费 #生产者生产消息msg1 lpush listA msg1 (integer) 1 #消费者读取到消息msg1 rpop listA "msg1" #消费者阻塞式读取listA,如果有数据立刻返回,否则最多等待10秒 brpop listA 10 1) "listA" 2) "msg1"使用rpop或brpop这样接口消费消息会先从队列中删除消息,然后再由应用消费,如果应用应用在处理消息前异常宕机了,消息就丢失了。但如果使用lindex这样的只读命令先读取消息处理完毕后在删除,又需要额外的机制来保证一条消息不会被其他消费者重复读到。好在list有rpoplpush或brpoplpush这样的接口,可以原子性的从一个list中移除一个消息并加入另一个list。应用程序可以通过2个list组和来完成消息的消费和确认功能,使用rpoplpush从list A中消费消息并移入list B,等消息处理完毕后在从list B中删除消息,如果在处理消息过程中应用异常宕机,恢复后应用可以重新从list B中读取未处理的消息并处理。这种方式为消息的消费增加了ack机制。#基于2个list完成消息消费和确认 #从listA中读取消息并写入listB rpoplpush listA listB "msg1" #业务逻辑处理msg1完毕后,从listB中删除msg1,完成消息的确认 lrem listB 1 msg1 (integer) 12.list的不足之处通过Redis 1.0就引入的list结构我们就能实现一个分布式的消息队列,满足一些简单的业务需求。但list结构作为消息队列服务有一个很致命的问题,它没有广播功能,一个消息只能被消费一次。而在大型系统中,通常一个消息会被下游多个应用同时订阅和消费,例如当用户完成一个订单的支付操作时,需要通知商家发货,要更新物流状态,可能还会提高用户的积分和等级,这些都是不同的下游子系统,他们全部会订阅支付完成的操作,而list一个消息只能被消费一次在这样复杂的大型系统面前就捉襟见肘了。可能你会说那弄多个list,生产者向每个list中都投递消息,每个消费者处理自己的list不就行了吗。这样第一是性能不会太好,因为同一个消息需要被重复的投递,第二是这样的设计违反了生产者和消费者解耦的原则,这个设计下生产者需要知道下游有哪些消费者,如果业务发生变化,需要额外增加一个消费者,生产者的代码也需要修改。3.总结优势模型简单,和使用本地list基本相同,适配容易通过brpop做到消息处理的实时性通过rpoplpush来联动2个list,可以做到消息先消费后确认,避免消费者应用异常情况下消息丢失不足消息只能被消费一次,缺乏广播机制二、Redis 2.0 pubsublist作为消息队列应用场景受到限制很重要的原因在于没有广播,所以Redis 2.0中引入了一个新的数据结构pubsub。pubsub虽然不能算作是list的替代品,但它确实能解决一些list不能解决的问题。1.pubsub特性pubsub引入一个概念叫channel,生产者通过publish接口投递消息时会指定channel,消费者通过subscribe接口订阅它关心的channel,调用subscribe后这条连接会进入一个特殊的状态,通常不能在发送其他请求,当有消息投递到这个channel时Redis服务端会立刻通过该连接将消息推送到消费者。这里一个channel可以被多个应用订阅,消息会同时投递到每个订阅者,做到了消息的广播。另一方面,消费者可以会订阅一批channel,例如一个用户订阅了浙江的新闻的推送,但浙江新闻还会进行细分,例如“浙江杭州xx”、“浙江温州xx”,这里订阅者不需要获取浙江的所有子类在挨个订阅,只需要调用psubscribe“浙江*”就能订阅所有以浙江开头的新闻推送了,这里psubscribe传入一个通配符表达的channel,Redis服务端按照规则推送所有匹配channel的消息给对应的客户端。#基于pubsub完成channel的匹配和消息的广播 #消费者1订阅channel1 subscribe channel1 1) "subscribe" 2) "channel1" 3) (integer) 1 #收到消息推送 1) "message" 2) "channel1" 3) "msg1" #消费者2订阅channel* psubscribe channel* 1) "psubscribe" 2) "channel*" 3) (integer) 1 #收到消息推送 1) "pmessage" 2) "channel*" 3) "channel1" 4) "msg1" 1) "pmessage" 2) "channel*" 3) "channel2" 4) "msg2" #生产者发布消息msg1和msg2 publish channel1 msg1 (integer) 2 publish channel2 msg2 (integer) 1在Redfis 2.8时加入了keyspace notifications功能,此时pubsub除了通知用户自定义消息,也可以通知系统内部消息。keyspace notifications引入了2个特殊的channel分别是__keyevent@__:和__keyspace@__:,通过订阅__keyevent客户端可以收到某个具体命令调用的回调通知,通过订阅__keyspace客户端可以收到目标key的增删改操作以及过期事件。使用这个功能还需要开启配置notify-keyspace-events。#通过keyspace notifications功能获取系统事件 #写入请求 set testkey v EX 1 #订阅key级别的事件 psubscribe __keyspace@0__:testkey 1) "psubscribe" 2) "__keyspace@0__:testkey" 3) (integer) 1 #收到通知 1) "pmessage" 2) "__keyspace@0__:testkey" 3) "__keyspace@0__:testkey" 4) "set" 1) "pmessage" 2) "__keyspace@0__:testkey" 3) "__keyspace@0__:testkey" 4) "expire" 1) "pmessage" 2) "__keyspace@0__:testkey" 3) "__keyspace@0__:testkey" 4) "expired" #订阅所有的命令事件 psubscribe __keyevent@0__:* 1) "psubscribe" 2) "__keyevent@0__:*" 3) (integer) 1 #收到通知 1) "pmessage" 2) "__keyevent@0__:*" 3) "__keyevent@0__:set" 4) "testkey" 1) "pmessage" 2) "__keyevent@0__:*" 3) "__keyevent@0__:expire" 4) "testkey" 1) "pmessage" 2) "__keyevent@0__:*" 3) "__keyevent@0__:expired" 4) "testkey"2.pubsub的不足之处pubsub既能单播又能广播,还支持channel的简单正则匹配,功能上已经能满足大部分业务的需求,而且这个接口发布的时间很早,在2011年Redis 2.0发布时就已经具备,用户基础很广泛,所以现在很多业务都有用到这个功能。但你要深入了解pubsub的原理后,是肯定不敢把它作为一个一致性要求较高,数据量较大系统的消息服务的。首先,pubsub的消息数据是瞬时的,它在Redis服务端不做保存,publish发送到Redis的消息会立刻推送到所有当时subscribe连接的客户端,如果当时客户端因为网络问题断连,那么就会错过这条消息,当客户端重连后,它没法重新获取之前那条消息,甚至无法判断是否有消息丢失。其次,pubsub中消费者获取消息是一个推送模型,这意味着Redis会按消息生产的速度给所有的消费者推送消息,不管消费者处理能力如何,如果消费者应用处理能力不足,消息就会在Redis的client buf中堆积,当堆积数据超过一个阈值后会断开这条连接,这意味着这些消息全部丢失了,在也找不回来了。如果同时有多个消费者的client buf堆积数据但又还没达到断开连接的阈值,那么Redis服务端的内存会膨胀,进程可能因为oom而被杀掉,这导致了整个服务中断。3.总结优势消息具备广播能力psubscribe能按字符串通配符匹配,给予了业务逻辑的灵活性能订阅特定key或特定命令的系统消息不足Redis异常、客户端断连都会导致消息丢失消息缺乏堆积能力,不能削峰填谷。推送的方式缺乏背压机制,没有考虑消费者处理能力,推送的消息超过消费者处理能力后可能导致消息丢失或服务异常三、Redis 5.0 stream消息丢失、消息服务不稳定的问题严重限制了pubsub的应用场景,所以Redis需要重新设计一套机制,来解决这些问题,这就有了后来的stream结构。1.stream特性一个稳定的消息服务需要具备几个要点,要保证消息不会丢失,至少被消费一次,要具备削峰填谷的能力,来匹配生产者和消费者吞吐的差异。在2018年Redis 5.0加入了stream结构,这次考虑了list、pubsub在应用场景下的缺陷,对标kafka的模型重新设计全内存消息队列结构,从这时开始Redis消息队列功能算是能和主流消息队列产品pk一把了。stream的改进分为多个方面成本:存储message数据使用了listpack结构,这是一个紧凑型的数据结构,不同于list的双向链表每个节点都要额外占用2个指针的存储空间,这使得小msg情况下stream的空间利用率更高。功能:stream引入了消费者组的概念,一个消费者组内可以有多个消费者,同一个组内的消费者共享一个消息位点(last_delivered_id),这使得消费者能够水平的扩容,可以在一个组内加入多个消费者来线性的提升吞吐,对于一个消费者组,每条msg只会被其中一个消费者获取和处理,这是pubsub的广播模型不具备的。不同消费者组之前是相互隔离的,他们各自维护自己的位点,这使得一条msg能被多个不同的消费者组重复消费,做到了消息广播的能力。stream中消费者采用拉取的方式,并能设置timeout在没有消息时阻塞,通过这种长轮询机制保证了消息的实时性,而且消费速率是和消费者自身吞吐相匹配。消息不丢失:stream的数据会存储在aof和rdb文件中,这使Redis重启后能够恢复stream的数据。而pubsub的数据是瞬时的,Redis重启意味着消息全部丢失。stream中每个消费者组会存储一个last_delivered_id来标识已经读取到的位点,客户端连接断开后重连还是能从该位点继续读取,消息不会丢失。stream引入了ack机制保证消息至少被处理一次。考虑一种场景,如果消费者应用已经读取了消息,但还没来得及处理应用就宕机了,对于这种已经读取但没有ack的消息,stream会标示这条消息的状态为pending,等客户端重连后通过xpending命令可以重新读取到pengind状态的消息,继续处理。如果这个应用永久宕机了,那么该消费者组内的其他消费者应用也能读取到这条消息,并通过xclaim命令将它归属到自己下面继续处理。#基于stream完成消息的生产和消费,并确保异常状态下消息至少被消费一次 #创建mystream,并且创建一个consumergroup为mygroup XGROUP CREATE mystream mygroup $ MKSTREAM OK #写入一条消息,由redis自动生成消息id,消息的内容是一个kv数组,这里包含field1 value1 field2 value2 XADD mystream * field1 value1 field2 value2 "1645517760385-0" #消费者组mygroup中的消费者consumer1从mystream读取一条消息,>表示读取一条该消费者组从未读取过的消息 XREADGROUP GROUP mygroup consumer1 COUNT 1 STREAMS mystream > 1) 1) "mystream" 2) 1) 1) "1645517760385-0" 2) 1) "field1" 2) "value1" 3) "field2" 4) "value2" #消费完成后ack确认消息 xack mystream mygroup 1645517760385-0 (integer) 1 #如果消费者应用在ack前异常宕机,恢复后重新获取未处理的消息id。 XPENDING mystream mygroup - + 10 1) 1) "1645517760385-0" 2) "consumer1" 3) (integer) 305356 4) (integer) 1 #如果consumer1永远宕机,其他消费者可以把pending状态的消息移动到自己名下后继续消费 #将消息id 1645517760385-0移动到consumer2下 XCLAIM mystream mygroup consumer2 0 1645517760385-0 1) 1) "1645517760385-0" 2) 1) "field1" 2) "value1" 3) "field2" 4) "value2"Redis stream保证了消息至少被处理一次,但如果想做到每条消息仅被处理一次还需要应用逻辑的介入。消息被重复处理要么是生产者重复投递,要么是消费者重复消费。对于生产者重复投递问题,Redis stream为每个消息都设置了一个唯一递增的id,通过参数可以让Redis自动生成id或者应用自己指定id,应用可以根据业务逻辑为每个msg生成id,当xadd超时后应用并不能确定消息是否投递成功,可以通过xread查询该id的消息是否存在,存在就说明已经投递成功,不存在则重新投递,而且stream限制了id必须递增,这意味了已经存在的消息重复投递会被拒绝。这套机制保证了每个消息可以仅被投递一次。对于消费者重复消费的问题,考虑一个场景,消费者读取消息后业务处理完毕,但还没来得及ack就发生了异常,应用恢复后对于这条没有ack的消息进行了重复消费。这个问题因为ack和消费消息的业务逻辑发生在2个系统,没法做到事务性,需要业务来改造,保证消息处理的幂等性。2.stream的不足stream的模型做到了消息的高效分发,而且保证了消息至少被处理一次,通过应用逻辑的改造能做到消息仅被处理一次,它的能力对标kafka,但吞吐高于kafka,在高吞吐场景下成本比kafka低,那它又有哪些不足了。首先消息队列很重要的一个功能就是削峰填谷,来匹配生产者和消费者吞吐的差异,生产者和消费者吞吐差异越大,持续时间越长,就意味着steam中需要堆积更多的消息,而Redis作为一个全内存的产品,数据堆积的成本比磁盘高。其次stream通过ack机制保证了消息至少被消费一次,但这有个前提就是存储在Redis中的消息本身不会丢失。Redis数据的持久化依赖aof和rdb文件,aof落盘方式有几种,通过配置appendfsync决定,通常我们不会配置为always来让每条命令执行完后都做一次fsync,线上配置一般为everysec,每秒做一次fsync,而rdb是全量备份时生成,这意味了宕机恢复可能会丢掉最近一秒的数据。另一方面线上生产环境的Redis都是高可用架构,当主节点宕机后通常不会走恢复逻辑,而是直接切换到备节点继续提供服务,而Redis的同步方式是异步同步,这意味着主节点上新写入的数据可能还没同步到备节点,在切换后这部分数据就丢失了。所以在故障恢复中Redis中的数据可能会丢失一部分,在这样的背景下无论stream的接口设计的多么完善,都不能保证消息至少被消费一次。3.总结优势在成本、功能上做了很多改进,支持了紧凑的存储小消息、具备广播能力、消费者能水平扩容、具备背压机制通过ack机制保证了Redis服务端正常情况下消息至少被处理一次的能力不足内存型消息队列,数据堆积成本高Redis本身rpo>0,故障恢复可能会丢数据,所以stream在Redis发生故障恢复后也不能保证消息至少被消费一次。四、Tair持久内存版 streamRedis stream的不足也是内存型数据库特性带来的,它拥有高吞吐、低延时,但大容量下成本会比较高,而应用的场景也不完全是绝对的大容量低吞吐或小容量高吞吐,有时应用的场景会介于二者之间,需要平衡容量和吞吐的关系,所以需要一个产品它的存储成本低于Redis stream,但它的性能又高于磁盘型消息队列。另一方面Redis stream在Redis故障场景下不能保证消息的不丢失,这导致业务需要自己实现一些复杂的机制来回补这段数据,同时也限制了它应用在一些对一致性要求较高的场景。为了让业务逻辑更简单,stream应用范围更广,需要保证故障场景下的消息持久化。兼顾成本、性能、持久化,这就有了Tair持久内存版。1.Tair持久内存版特性更大空间,更低成本Tair持久内存版引入了Intel傲腾持久内存(下面称作AEP),它的性能略低于内存,但相同容量下成本低于内存。Tair持久内存版将主要数据存储在AEP上,使得相同容量下,成本更低,这使同样单价下stream能堆积更多的消息。兼容社区版Tair持久内存版兼容原生Redis绝大部分的数据结构和接口,对于stream相关接口做到了100%兼容,如果你之前使用了社区版stream,那么不需要修改任何代码,只需要换一个连接地址就能切换到持久内存版。并且通过工具完成社区版和持久内存版数据的双向迁移。数据的实时持久化Tair持久内存版并不是简单将Redis中的数据换了一个介质存储,因为这样仅能通过AEP降低成本,但没用到AEP断电数据不丢失的特性,对持久化能力没有任何提升。开源Redis通过在磁盘上记录AppendOnlyLog来持久化数据,AppendOnlyLog记录了所有的写操作,相当于redolog,在宕机恢复时通过回放这些log恢复数据。但受限于磁盘介质的高延时和Redis内存数据库使用场景下对低延时的要求,并不能在每次写操作后fsync持久化log,最新写入的数据可能并没有持久化到磁盘,这也是数据可能丢失的根因。Tair持久内存版的数据恢复没有使用AppendOnlyLog来完成, 而是将将redis数据结构存储在AEP上,这样宕机后这些数据结构并不会丢失,并且对这些数据结构增加了一些额外的描述信息,宕机后在recovery时能够读到这些额外的描述信息,让这些redis数据结构重新被识别和索引,将状态恢复到宕机前的样子。Tair通过将redis数据结构和描述信息实时写入AEP,保证了写入数据的实时持久化。HA数据不丢失Tair持久内存版保证了数据的持久化,但生产环境中都是高可用架构,多数情况下当主节点异常宕机后并不会等主节点重启恢复,而是切换到备节点继续提供服务,然后给新的主节点添加一个新的备节点。所以在故障发生时如果有数据还没从主节点同步到备节点,这部分数据就会丢失。Redis采用的异步同步,当客户端写入数据并返回成功时对Redis的修改可能还没同步到备节点,如果此时主节点宕机数据就会丢失。为了避免在HA过程中数据丢失,Tair持久内存版引入了半同步机制,确保写入请求返回成功前相关的修改已经同步到备节点。可以发现开启半同步功能后写入请求的RT会变高,多出主备同步的耗时,这部分耗时大概在几十微秒。但通过一些异步化的技术,虽然写请求的RT会变高,但对实例的最大写吞吐影响很小。当开启半同步后生成者通过xadd投递消息,如果返回成功,消息一定同步到备节点,此时发生HA,消费者也能在备节点上读到这条消息。如果xadd请求超时,此时消息可能同步到备节点也可能没有,生产者没法确定,此时通过再次投递消息,可以保证该消息至少被消费一次。如果要严格保证消息仅被消费一次,那么生产者可以通过xread接口查询消息是否存在,对于不存在的场景重新投递。2.总结优势引入了AEP作为存储介质,目前Tair持久内存版价格是社区版的70%。保证了数据的实时持久化,并且通过半同步技术保证了HA不丢数据,大多数情况下做到消息不丢失(备库故障或主备网络异常时会降级为异步同步,优先保障可用性),消息至少被消费一次或仅被消费一次。五、未来消息队列主要是为了解决3类问题,应用模块的解耦、消息的异步化、削峰填谷。目前主流的消息队列都能满足这些需求,所以在实际选型时还会考虑一些特殊的功能是否满足,产品的性能如何,具体业务场景下的成本怎么样,开发的复杂度等。Redis的消息队列功能并不是最全面的,它不希望做成一个大而全的产品,而是做一个小而美的产品,服务好一部分用户在某些场景下的需求。目前用户选型Redis作为消息队列服务的原因,主要有Redis在相同成本下吞吐更高、Redis的延时更低、应用需要一个消息服务但又不想额外引入一堆依赖等。未来Tair持久内存版会针对这些述求,把这些优势继续放大。吞吐通过优化持久内存版的持久化流程,让吞吐接近内存版甚至超过内存版吞吐。延时通过rdma在多副本间同步数据,降低半同步下写入数据的延时。
-
MySQL 是持久化存储,存放在磁盘里面,检索的话,会涉及到一定的 IO,为了解决这个瓶颈,于是出现了缓存,比如现在用的最多的 memcached(简称mc)。首先,用户访问mc,如果未命中,就去访问 MySQL,之后像内存和硬盘一样,把数据复制到mc一部分。 Redis 和mc都是缓存,并且都是驻留在内存中运行的,这大大提升了高数据量web访问的访问速度。然而mc只是提供了简单的数据结构,比如 string存储;Redis却提供了大量的数据结构,比如string、list、set、hashset、sorted set这些,这使得用户方便了好多,毕竟封装了一层实用的功能,同时实现了同样的效果,当然用Redis而慢慢舍弃mc。 内存和硬盘的关系,硬盘放置主体数据用于持久化存储,而内存则是当前运行的那部分数据,CPU访问内存而不是磁盘,这大大提升了运行的速度,当然这是基于程序的局部化访问原理。 推理到 Redis + MySQL,它是内存+磁盘关系的一个映射,MySQL 放在磁盘,Redis放在内存,这样的话,web应用每次只访问Redis,如果没有找到的数据,才去访问 MySQL。 然而 Redis + MySQL 和内存+磁盘的用法最好是不同的。前者是内存数据库,数据保存在内存中,当然速度快。后者是关系型数据库,功能强大,数据访问也就慢。像memcache,MongoDB,Redis,都属于No SQL系列。不是一个类型的东西,应用场景也不太一样,还是要看你的需求来决定。
-
MongoDB和Redis都是NoSQL,采用结构型数据存储。二者在使用场景中,存在一定的区别,这也主要由于二者在内存映射的处理过程,持久化的处理方法不同。MongoDB建议集群部署,更多的考虑到集群方案,Redis更偏重于进程顺序写入,虽然支持集群,也仅限于主-从模式。指标 MongoDB(v2.4.9) Redis(v2.4.17) 比较说明实现语言 C++C/C++-协议BSON、自定义二进制类Telnet-性能依赖内存,TPS较高依赖内存,TPS非常高Redis优于MongoDB可操作性丰富的数据表达、索引;最类似于关系数据库,支持丰富的查询语言数据丰富,较少的IOMongoDB优于Redis内存及存储适合大数据量存储,依赖系统虚拟内存管理,采用镜像文件存储;内存占有率比较高,官方建议独立部署在64位系统(32位有最大2.5G文件限制,64位没有改限制)Redis2.0后增加虚拟内存特性,突破物理内存限制;数据可以设置时效性,类似于memcache不同的应用角度看,各有优势可用性支持master-slave,replicaset(内部采用paxos选举算法,自动故障恢复),auto sharding机制,对客户端屏蔽了故障转移和切分机制依赖客户端来实现分布式读写;主从复制时,每次从节点重新连接主节点都要依赖整个快照,无增量复制;不支持自动sharding,需要依赖程序设定一致hash机制MongoDB优于Redis;单点问题上,MongoDB应用简单,相对用户透明,Redis比较复杂,需要客户端主动解决。(MongoDB 一般会使用replica sets和sharding功能结合,replica sets侧重高可用性及高可靠性,而sharding侧重于性能、易扩展)可靠性从1.8版本后,采用binlog方式(MySQL同样采用该方式)支持持久化,增加可靠性依赖快照进行持久化;AOF增强可靠性;增强可靠性的同时,影响访问性能MongoDB优于Redis一致性不支持事物,靠客户端自身保证支持事物,比较弱,仅能保证事物中的操作按顺序执行Redis优于MongoDB数据分析内置数据分析功能(mapreduce)不支持MongoDB优于Redis应用场景海量数据的访问效率提升较小数据量的性能及运算MongoDB优于Redis
-
主从复制:主节点负责写数据,从节点负责读数据,从而实现读写分离,提高redis的高可用性。让一个服务器去复制(replicate)另一个服务器,我们称呼被复制的服务器为主节点(master),而对主服务器进行复制的服务器则被称为从节点(slave)主从复制的特点:1、一个master可以有多个slave2、一个slave只能有一个master3、数据流向是单向的,master到slave 主从复制的作用:1、数据副本:多一份或多份数据拷贝,保证redis高可用2、扩展性能:单机redis的性能是有限的,主从复制能横向扩展 如容量、QPS等主从复制实现方式客户端命令:slaveof全量复制和增量复制全量复制过程:1. 向主节点发送psync,有两个参数,第一个参数是runId,第二个参数是偏移量,第一次发送不知道主节点的runId,也不知道偏移量,因此从节点发送 ? -12. 主节点收到消息,根据? -1 能判断出来是第一次复制,主节点把runId和offset 发送给Slave节点,3. 从节点保存主节点基本信息4-5-6. Master节点执行bgsave生成快照,在此期间会记录后续执行的数据更改命令所更改的数据,直到主节点将生成的RDB文件传输到从节点为止, 期间Master节点执行的写操作,主节点会将缓冲区中记录的新更改的数据发送给从节点7-8 从节点清空此前的所有数据,加载RDB文件恢复数据并存入新更改的数据说明: 全量复制的性能开销:1. bgsave生成RDB文件需要的时间,2. RDB文件在网络间的传输时间,3. 从节点的数据清空时间 , 4. 加载RDB文件的时间 5. 可能的 AOF 重写时间 数据更改命令缓冲区repl_back_buffer用于:当Redis通过Linux中的fork()函数开辟一个子进程处理其他事务(比如主进程执行bgsave生成一个RDB文件时,或者主进程执行bgrewriteaof生成一个AOF文件时), 而主进程(即处理客户端命令的进程)后续执行的一些数据更改命令会被暂时保存在该区域,而且该区域空间有限(配置文件中repl-backlog-size 1mb即可配置该处空间大小)部分复制部分复制解决的问题:在实际环境中,主节点与从节点之间可能会发生一些网络波动等情况,导致从节点与主节点之间的网络连接断开(主从节点的Redis均未关闭),如果重新连接上后,可以使用全量复制来重新进行一次主从节点数据同步,但是全量复制会带来一个性能开销的问题,而且从节点中可能有大量数据是主节点中没有更该过的,也就是不需要进行再次同步的数据,如果使用全量复制肯定是带来了一些不必要的浪费。所以,部分复制功能就是为了解决该问题的。过程:1. 主从节点直接连接断开,2. 此时主节点继续执行的数据更改命令会被记录在一个缓冲区 repl_back_buffer 中3. 当从节点重新连接主节点时,4. 自动发出一条命令(psync offset run_id),将从节点中存储的主节点的Redis运行时id和从节点中保存的偏移量发送给主节点5. 主节点接收从节点发送的偏移量和id,对比此时主节点的偏移量和接收的偏移量,如果两个偏移量之差大于repl_back_buffer中的数据,那么就表示在断开连接期间从节点已经丢失了超出规定数量的数据,此时就需要进行全量复制了,否则就进行部分复制6. 将主节点缓冲区中的数据同步更新到从节点中,这样就实现了部分数据的复制同步,降低了性能开销主从节点的故障处理故障发生时服务自动转移(自动故障转移):即当某个节点发生故障导致停止服务时,该节点提供的服务会有另一个节点自动代替提供,这样就实现了一个高可用的效果从节点故障:即如果某个从节点发生了故障,导致无法向在该节点上的客户端提供读服务,解决办法就是使该客户端转移到另一个可用从节点上,但是在转移时,应该考虑该从节点能承受几个客户端的压力主节点故障:如果主节点发生故障,在使用主节点进行读写操作的客户端就无法使用了,而使用从节点只进行读操作的客户端还是可以继续使用的,解决办法就是从从节点中选一个节点更改为主节点,并且将原主节点的客户端连接到新的主节点上,然后通过该客户端将其他从节点连接到新的主节点中主从复制确实可以解决故障问题,但是主从复制不能实现自动故障转移,其必须要通过一些手动操作,而且非常麻烦,所以要实现自动故障转移还需要另一个功能,Redis中提供了sentinel功能来实现自动故障转移。主从节点的故障处理 1. 读写分离:即客户端发来的读写命令分开,写命令交给主节点执行,读命令交给从节点执行,不仅减少了主节点的压力,而且增强了读操作的能力;但也会造成一些问题但是主从节点之间数据复制造成的阻塞延迟也可能会导致主从不一致的情况,也就是主节点先进行了写操作,但可能因为数据复制造成的阻塞延迟,导致在从节点上进行的读操作获取的数据与主节点不一致读取过期数据:主从复制会将带有过期时间的数据一并复制到从节点中,但是从节点是没有删除数据的能力的,即使是过期数据,所以主节点中的已经删除了过期数据,但是因为主从复制的阻塞延迟问题导致从节点中的过期数据没有删除,此时客户端就会读到一个过期数据 2. 主从配置不一致:造成的问题有比如配置中的maxmemory参数如果配置不一致,比如主节点2Gb,从节点1Gb,那么就可能会导致数据丢失;以及一些其他配置问题 3. 规避全量复制:全量复制的性能开销较大,所以要尽量避免全量复制,在第一次建立主从节点关系式一定会发生全量复制;可以适当减小Redis的maxmemory参数,这样可以使得RDB更快,或者选择在客户端操作低峰期进行,比如深夜从节点中保存的主节点run_id不一致时也一定会发生全量复制(比如主节点的重启);可以通过故障转移来尽量避免,例如Redis Sentinel 与 Redis Cluster 当主从节点的偏移量之差大于命令缓冲区repl_back_buffer中对应数据的偏移差时,也会发生全量复制,也就是上面的部分复制的复制过程中所说的;可以适当增大配置文件中repl-backlog-size即数据缓冲区可尽量避免 4. 规避复制风暴:单主节点导致的复制风暴,即当主节点重启后,要向其所有的从节点都进行一次全量复制,这非常消耗性能;可以更换主从节点的拓扑结构,更换为类似树形的结构,一个主节点只与少量的从节点建立主从关系,而而这些主节点又与其他从节点构成主从关系单主节点机器复制风暴:即如果过一台机器专门用来部署多个主节点,然后其他机器部署从节点,那么一旦主节点机器宕机重启,就会引起所有的主从节点之间的全量复制,造成非常大的性能开销;可以采用多台机器,分散部署主节点,或者使用自动故障转移来将某个从节点变为主节点实现一个高可用
-
对Redis而言,其数据是保存在内存中的,一旦机器宕机,内存中的数据会丢失,因此需要将数据异步持久化到硬盘中保存。这样,即使机器宕机,数据能从硬盘中恢复。常见的数据持久化方式:1.快照:类似拍照记录时光,快照是某时某刻将数据库的数据做拍照记录下其数据信息。如MYSQL的Dump,Redis的RDB模式2.写日志方式:是将数据的操作全部写到日志当中,需要恢复的时候,按照日志记录的操作记录重新再执行一遍。例如MYSQL的Binlog,Redis的AAOF模式、RDB说明:redis默认开启,将redis在内存中保存的数据,以快照的方式持久化到硬盘中保存。触发机制:1.save命令:阻塞方式,需要等redis执行完save后,才能执行其他get、set等操作。同步方式2.bgsave命令:非阻塞,其原理是调用linux 的 fork()函数,创建redis的子进程,子进程进行创建 rdb 文件的操作。异步方式,3.自动方式:在redis.conf文件中配置,如下 save <指定时间间隔> <执行指定次数更新操作> ,save 60 10000 表示 60秒年内有10000次操作会自动生成rdb文件。4.其他方式4.1 执行flushall命令,清空数据,几乎不用4.2 执行shutdown命令,安全关闭redis不丢失数据,几乎用不到。4.3 主从复制,在主从复制的时候,rdb文件作为媒介来关联主节点和从节点的数据一致。RDB优缺点优点:1 适合大规模的数据恢复。2 如果业务对数据完整性和一致性要求不高,RDB是很好的选择。缺点:1 不可控,容易丢失数据:数据的完整性和一致性不高,因为RDB可能在最后一次备份时宕机了。2 耗时耗性能:备份时占用内存,因为Redis 在备份时会独立创建一个子进程,将数据写入到一个临时文件(此时内存中的数据是原来的两倍哦),最后再将临时文件替换之前的备份文件。所以Redis 的持久化和数据的恢复要选择在夜深人静的时候执行是比较合理的。AOF说明: redis默认不开启,采用日志的形式来记录每个写操作,并追加到 .aof 文件中。Redis 重启的会根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作生成AOF的三种策略:1. always : 每条命令都会刷新到缓冲区,把缓冲区fsync到硬盘,对硬盘IO压力大,一般sata盘只有几百TPS,如果redis的写入量非常大,那对硬盘的压力也横刀。2. everysec: 每秒把缓冲区fsync 到硬盘,如果出现故障,会丢失1s(默认配置是1秒)的数据。一般使用这种。3. no : 由操作系统来定什么时候fsync到硬盘中。 缺点:不可控 AOF重写:把过期的,没有用的,重复的,可优化的命令简化为很小的aof文件。实际上是redis内存中的数据回溯成aof文件。如下图所示:作用:1.减少硬盘占用量2.加快恢复速度 AOF重写的实现方式1.bgrewriteaof 命令 : 从redis的主进程fork一个子进程生成包含当前redis内存数据的最小命令集、2.AOF重写配置:# 1. aof文件增长率 auto-aof-rewrite-percentage 100# 2. aof文件重写需要的尺寸 auto-aof-rewrite-min-size 64mb自动触发时机:(需要同时满足)当前的aof文件大小 > aof文件重写需要的尺寸 (aof当前文件大小 - 上次aof的文件大小)/ 上次aof文件大小 > aof文件增长率 AOF优缺点优点:1.数据的完整性和一致性更高缺点:1.因为AOF记录的内容多,文件会越来越大,数据恢复也会越来越慢。2. AOF每秒fsync一次指令硬盘,如果硬盘IO慢,会阻塞父进程;风险是会丢失1秒多的数据;在Rewrite过程中,主进程把指令存到mem-buffer中,最后写盘时会阻塞主进程。关于Redis持久化方式RDB和AOF的缺点原因是redis持久化方式的痛点,缺点比较明显。1、RDB需要定时持久化,风险是可能会丢两次持久之间的数据,量可能很大。2、AOF每秒fsync一次指令硬盘,如果硬盘IO慢,会阻塞父进程;风险是会丢失1秒多的数据;在Rewrite过程中,主进程把指令存到mem-buffer中,最后写盘时会阻塞主进程。3、这两个缺点是个很大的痛点。为了解决这些痛点,GitHub的两位工程师 Bryana Knight 和 Miguel Fernández 日前写了一篇 文章 ,讲述了将持久数据从Redis迁出的经验: http://www.open-open.com/lib/view/open1487736984424.html如何选择RDB和AOF建议全都要。1、对于我们应该选择RDB还是AOF,官方的建议是两个同时使用。这样可以提供更可靠的持久化方案。在redis 4.0 之后,官方提供了混合持久化模式,具体如下持久化文件结构上半段RDB格式,后半段是AOF模式。
上滑加载中
推荐直播
-
华为云码道Agent集成与鸿蒙实战2026/08/11 周二 19:00-21:00
王一男-华为云码道产品规划专家;李炎-华为云码道产品专家;彭江敏-华为云鸿蒙端云一体化开发专家
本次直播带你解读华为云码道7月份产品新特性、新功能。更有专家演示码道Agent Space × 钉钉机器集成实战,从0到1打通消息通道;码道鸿蒙端云一体化实战,快速搭建员工签到系统。
回顾中 -
华为云开发者AI素养直播课·第五期2026/09/04 周五 16:00-18:00
林华鼎-华为云AI开发者运营负责人;蒋春阳-华为云AI开发者案例开发专家
本期直播内容: AI工具体验营 · 第5-8课连讲。Agent-Team 多智能体协作完成毕业设计实践
回顾中 -
华为云开发者AI素养ClassRoom·第六期2026/09/08 周二 19:00-20:00
樊渊-2026华为软件挑战赛冠军
高手来了:看软挑高手解析二维排样问题—从工业难题到算法突破
回顾中
热门标签