0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Redis能够胜任存储工作吗?

jf_ro2CN3Fa 来源:小姐姐味道 2023-10-09 14:52 次阅读

大多数数据库,由于经常和磁盘打交道,在高并发场景下,响应会非常的慢。为了解决这种速度差异,大多数系统都习惯性的加入一个缓存层,来加速数据的读取。redis由于它优秀的处理能力和丰富的数据结构,已经成为了事实上的分布式缓存标准

但是,如果你以为redis只能做缓存的话,那就太小看它了。

redis丰富的数据结构,使得它的业务使用场景非常广泛,加上rdb的持久化特性,它甚至能够被当作落地的数据库使用。在这种情况下,redis能够撑起大多数互联网公司,尤其是社交、游戏、直播类公司的半壁江山。

1. Redis能够胜任存储工作

redis提供了非常丰富的集群模式:主从、哨兵、cluster,满足服务高可用的需求。同时,redis提供了两种持久化方式:aof和rdb,常用的是rdb。

通过bgsave指令,主进程会fork出新的进程,回写磁盘。bgsave相当于做了一个快照,由于它并没有WAL日志和checkpoint机制,是无法做到实时备份的。如果机器突然断电,那就很容易丢失数据。

幸运的是,redis是内存型的数据库,主丛同步的速度是非常快的。如果你的集群维护的好,内存分配的合理,那么除非机房断电,否则redis的SLA,会一直保持在非常高的水平。

43cc6900-666c-11ee-939d-92fbcf53809c.jpgimg

听起来不是绝对可靠啊,有丢失数据的可能!这在一般CRUD的业务中,是无法忍受的。但为什么redis能够满足大多数互联网公司的需求?这也是由业务属性所决定的。

在决定最大限度拥抱redis之前,你需要确认你的业务是否有以下特点:

除了核心业务,是否大多数业务对于数据的可靠性要求较低,丢失一两条数据是可以忍受的?

面对的是C端用户,可根据用户ID快速定位到一类数据,数据集合普遍较小?无大量范围查询需求?

是否能忍受内存型数据的成本需求?

是否业务几乎不需要事务操作?

很幸运的是,这类业务需求特别的多。比如常见的社交,游戏、直播、运营类业务,都是可以完全依赖Redis的。

2. Redis 应用场景

Redis具有松散的文档结构,丰富的数据类型,能够适应千变万化的scheme变更需求,接下来我将介绍Redis除缓存外的大量的应用场景。

43d7429e-666c-11ee-939d-92fbcf53809c.jpgimg

2.1 基本用户数据存储

在传统的数据库设计中,用户表是非常难以设计的,变更的时候会伤筋动骨。使用Redis的hash结构,可以实现松散的数据模型设计。某些不固定,验证型的功能属性,可以以JSON接口直接存储在hash的value中。使用hash结构,可以采用HGET和HMGET等指令,只获取自己所需要的数据,在使用上也是非常便捷的。

>HSETuser:199929sexm
>HSETuser:199929age22
>HGETALLuser:199929
1)"sex"
2)"m"
3)"age"
4)"22"

这种非统计型的、读多写少的场景,是非常适合使用KV结构进行存储的。Redis的hash结构提供了非常丰富的指令,某个属性也可以使用HINCRBY进行递增递减,非常的方便。

2.2 实现计数器

上面稍微提了一下HINCRBY指令,而对于Redis的Key本身来说,也有INCRBY指令,实现某个值的递增递减。

比如以下场景:统计某个帖子的点赞数;存放某个话题的关注数;存放某个标签的粉丝数;存储一个大体的评论数;某个帖子热度;红点消息数;点赞、喜欢、收藏数等。

>INCRBYfeedlike1
>INCRBYfeedlike1
>GETfeedlike
"2"

像微博这样容易出现热点的业务,传统的数据库,肯定是撑不住的,就要借助于内存数据库。由于Redis的速度非常快,就不用再采用传统DB非常慢的count操作,所有这种递增操作都是毫秒级别的,而且效果都是实时的。

2.3 排行榜

排行榜能提高参与者的积极性,所以这项业务非常常见,它本质上是一个topn的问题。

Redis中有一个叫做zset的数据结构,使用跳表实现的有序列表,可以很容易实现排行榜一类的问题。当存入zset中的数据,达到千万甚至是亿的级别,依然能够保持非常高的并发读写,且拥有非常棒的平均响应时间(5ms以内)。

使用zadd 可以添加新的记录,我们会使用排行相关的分数,作为记录的score值,然后使用zrevrange指令即可获取实时的排行榜数据,而zrevrank则可以非常容易的获取用户的实时排名。

>ZADDsorted2021-0755dog0
>ZADDsorted2021-0789dog1
>ZADDsorted2021-0732dog2
>ZCARDsorted2021-07
>3
>ZREVRANGEsorted2021-070-10WITHSCORES#top10排行榜
1)"dog1"
2)"89"
3)"dog0"
4)"55"
5)"dog2"
6)"32"

2.4 好友关系

set结构,是一个没有重复数据的集合,你可以将某个用户的关注列表、粉丝列表、双向关注列表、黑名单、点赞列表等,使用独立的zset进行存储。

使用ZADD、ZRANK等,将用户的黑名单使用ZADD添加,ZRANK使用返回的sorce值判断是否存在黑名单中。使用sinter指令,可以获取A和B的共同好友。

除了好友关系,有着明确黑名单、白名单业务场景的数据,都可以使用set结构进行存储。这种业务场景还有很多,比如某个用户上传的通讯录,计算通讯录的好友关系等等。

在实际使用中,使用zset存储这类关系的更多一些。zset同set一样,都不允许有重复值,但zset多了一个score字段,我们可以存储一个时间戳,用来标明关系建立所发生的时间,有更明确的业务含义。

2.5 统计活跃用户数

类似统计每天的活跃用户、用户签到、用户在线状态,这种零散的需求,实在是太多了。如果为每一个用户存储一个bool变量,那占用的空间就太多了。这种情况下,我们可以使用bitmap结构,来节省大量的存储空间。

>SETBITonline:2021-07-2338765203331
>SETBITonline:2021-07-2438765203331
>GETBITonline:2021-07-233876520333
1
>BITOPANDactiveonline:2021-07-23online:2021-07-24
>GETBITactive3876520333
1
>DEBUGOBJECTonline:2021-07-23
Valueat:0x7fdfde438bf0refcount:1encoding:rawserializedlength:5506446lru:16410558lru_seconds_idle:5
(0.96s)

注意,如果你的id很大,你需要先进行一次预处理,否则它会占用非常多的内存。

bitmap包含一串连续的2进制数字,使用1bit来表示真假问题。在bitmap上,可以使用and、or、xor等位操作(bitop)。

2.6 分布式锁

Redis的分布式锁,是一种轻量级的解决方案。虽然它的可靠性比不上Zookeeper之类的系统,但Redis分布式锁有着极高的吞吐量。

一个最简陋的加锁动作,可以使用redis带nx和px参数的set指令去完成。下面是一小段简单的分布式样例代码。

publicStringlock(Stringkey,inttimeOutSecond){
for(;;){
Stringstamp=String.valueOf(System.nanoTime());
booleanexist=redisTemplate.opsForValue().setIfAbsent(key,stamp,timeOutSecond,TimeUnit.SECONDS);
if(exist){
returnstamp;
}
}
}
publicvoidunlock(Stringkey,Stringstamp){
redisTemplate.execute(script,Arrays.asList(key),stamp);
}

删除操作的lua为。

localstamp=ARGV[1]
localkey=KEYS[1]
localcurrent=redis.call("GET",key)
ifstamp==currentthen
redis.call("DEL",key)
return"OK"
end

redisson的RedLock,是使用最普遍的分布式锁解决方案,有读写锁的差别,并处理了多redis实例情况下的异常问题。

2.7 分布式限流

使用计数器去实现简单的限流,在Redis中是非常方便的,只需要使用incr配合expire指令即可。

incrkey
expirekey1

这种简单的实现,通常来说不会有问题,但在流量比较大的情况下,在时间跨度上会有流量突然飙升的风险。根本原因,就是这种时间切分方式太固定了,没有类似滑动窗口这种平滑的过度方案。

同样是redisson的RRateLimiter,实现了与guava中类似的分布式限流工具类,使用非常便捷。下面是一个简短的例子:

RRateLimiterlimiter=redisson.getRateLimiter("xjjdogLimiter");
//只需要初始化一次
//每2秒钟5个许可
limiter.trySetRate(RateType.OVERALL,5,2,RateIntervalUnit.SECONDS);

//没有可用的许可,将一直阻塞
limiter.acquire(3);

2.8 消息队列

redis可以实现简单的队列。在生产者端,使用LPUSH加入到某个列表中;在消费端,不断的使用RPOP指令取出这些数据,或者使用阻塞的BRPOP指令获取数据,适合小规模的抢购需求。

Redis还有PUB/SUB模式,不过pubsub更适合做消息广播之类的业务。

在Redis5.0中,增加了stream类型的数据结构。它比较类似于Kafka,有主题和消费组的概念,可以实现多播以及持久化,已经能满足大多数业务需求了。

2.9 LBS应用

早早在Redis3.2版本,就推出了GEO功能。通过GEOADD指令追加lat、lng经纬数据,可以实现坐标之间的距离计算、包含关系计算、附近的人等功能。

关于GEO功能,最强大的开源方案是基于PostgreSQL的PostGIS,但对于一般规模的GEO服务,redis已经足够用了。

2.10 更多扩展应用场景

要看redis能干什么,就不得不提以下java的客户端类库redisson。redisson包含丰富的分布式数据结构,全部是基于redis进行设计的。

redisson提供了比如Set、 SetMultimap、 ScoredSortedSet、 SortedSet, Map、 ConcurrentMap、 List、 ListMultimap、 Queue、BlockingQueue等非常多的数据结构,使得基于redis的编程更加的方便。在github上,可以看到有上百个这样的数据结构:https://github.com/redisson/redisson/tree/master/redisson/src/main/java/org/redisson/api。

对于某个语言来说,基本的数组、链表、集合等api,配合起来能够完成大部分业务的开发。Redis也不例外,它拥有这些基本的api操作能力,同样能够组合成分布式的、线程安全的高并发应用。

由于Redis是基于内存的,所以它的速度非常快,我们也会把它当作一个中间数据的存储地去使用。比如一些公用的配置,放到redis中进行分享,它就充当了一个配置中心的作用;比如把JWT的令牌存放到Redis中,就可以突破JWT的一些限制,做到安全登出。

3. 一站式Redis面临的挑战

redis的数据结构丰富,一般不会在功能性上造成困扰。但随着请求量的增加,SLA要求的提高,我们势必会对Redis进行一些改造和定制性开发。

3.1 高可用挑战

redis提供了主从、哨兵、cluster等三种集群模式,其中cluster模式为目前大多数公司所采用的方式。

但是,redis的cluster模式,有不少的硬伤。redis cluster采用虚拟槽的概念,把所有的key映射到 0~16383个整数槽内,属于无中心化的架构。但它的维护成本较高,slave也不能够参与读取操作。

它的主要问题,在于一些批量操作的限制。由于key被hash到多台机器上,所以mget、hmset、sunion等操作就非常的不友好,经常发生性能问题。

redis的主从模式是最简单的模式,但无法做到自动failover,通常在主从切换后,还需要修改业务代码,这是不能忍受的。即使加上haproxy这样的负载均衡组件,复杂性也是非常高的。

哨兵模式在主从数量比较多的时候,能够显著的体现它的价值。一个哨兵集群,能够监控成百上千个集群,但是哨兵集群本身的维护是比较困难的。幸运的是,redis的文本协议非常简单,在netty中,甚至直接提供了redis的codec。自研一套哨兵系统,加强它的功能,是可行的。

3.2 冷热数据分离

redis的特点是,不管什么数据,都一股脑地搞到内存里做计算,这对于有时间序列概念,有冷热数据之分的业务,造成了非常大的成本考验。为什么大多数开发者喜欢把数据存放在MySQL中,而不是Redis中?除了事务性要求以外,很大原因是历史数据的问题。

通常,这种冷热数据的切换,是由中间件完成的。我们上面也谈到了,Redis是一个文本协议,非常简单。做一个中间件,或者做一个协议兼容的Redisinterwetten与威廉的赔率体系 存储,是比较容易的。

比如我们Redis中,只保留最近一年的活跃用户。一个好几年不活跃的用户,突然间访问了系统,这时候我们获取数据的时候,就需要中间件进行转换,从容量更大,速度更慢的存储中查找。

这个时候,Redis的作用,更像是一个热库,更像是一个传统cache层做的事情,发生在业务已经上规模的时候。但是注意,直到此时,我们的业务层代码,一直都是操作的redis的api。它们使用这众多的函数指令,并不关心数据到底是真正存储在redis中,还是在ssdb中。

3.3 功能性需求

redis还能玩很多花样。举个例子,全文搜索。很多人都会首选es,但redis生态就提供了一个模块:RediSearch,可以做查询,可以做filter。

但我们通常还会有更多的需求,比如统计类、搜索类、运营效果分析等。这类需求与大数据相关,即使是传统的DB也不能胜任。这时候,我们当然要把redis中的数据,导入到其他平台进行计算啦。

如果你选择的是redis数据库,那么dba打交道的,就是rdb,而不是binlog。有很多的rdb解析工具(比如redis-rdb-tools),能够定期把rdb解析成记录,导入到hadoop等其他平台。

此时,rdb成为所有团队的中枢,成为基本的数据交换格式。导入到其他db后的业务,该怎么玩怎么玩,完全不会因为业务系统选用了redis就无法运转。

4. 总结

大多数业务系统,跑在redis上,这是很多一直使用MySQL做业务系统的同学所不能想象的。看完了上面的介绍,相信你能够对redis能够实现的存储功能有个大体的了解。打开你的社交app、游戏app、视频app,看一下它们的功能,能够涵盖多少呢?

我这里要强调的是,某些数据,并不是一定要落地到RDBMS才算安全,它们并不是一个强需求。

那既然redis这么厉害,为什么还要有mysql、tidb这样的存储呢?关键还在于业务属性上。

如果一个业务系统,每次交互的数据,都是一个非常大的结果集,并涉及到非常复杂的统计、过滤工作,那么RDBMS是必须的;但如果一个系统,能够通过某个标识,快速定位到一类数据,这一类数据在可以预见的未来,是有限的,那就非常适合Redis存储。

一个电商系统,选用redis做存储就是作死,但一个社交系统就快活的多。在合适的场景选用合适的工具,才是我们应该做的。

但是一个系统,能否在产品验证期,就能快速的响应变化,快速开发上线,才是成功的关键。这也是使用redis做数据库,所能够带来的最大好处。千万别被那概率极低的丢数据场景,给吓怕了。比起产品成功,你的系统即使是牢如钢铁,也一文不值。






审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 计数器
    +关注

    关注

    32

    文章

    2199

    浏览量

    93305
  • SLA
    SLA
    +关注

    关注

    1

    文章

    52

    浏览量

    18189
  • Hash算法
    +关注

    关注

    0

    文章

    43

    浏览量

    7366
  • Redis
    +关注

    关注

    0

    文章

    365

    浏览量

    10534

原文标题:Redis只能做缓存?太out了!

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    如何使用Rust连接Redis

    Redis是一款快速、开源、键值存储数据库,被广泛应用于缓存、发布/订阅系统、定时任务等场景中。Rust提供了很多Redis的客户端库,本教程将会介绍如何使用Rust连接Redis,以
    的头像 发表于 09-19 16:22 1479次阅读

    Redis Stream应用案例

    ,还有一个很重要的能力是跨平台,甚至是作为一个嵌入式的存储系统跑在基于ARM的平台上,比如作者之前就宣称,Redis成功的跑在了“树莓派”上。试想一下,在IoT时代,会有无数随时随地可以接入互联网
    发表于 06-26 17:15

    redis概述

    REmote DIctionary Server(Redis)是一个基于key-value键值对的持久化数据库存储系统。redis和大名鼎鼎的Memcached缓存服务软件很像,但是redis
    发表于 07-17 07:38

    如何使得redis中的数据不再有

    嵌入式Linux系统重启后如何使得redis中的数据不再有今天在工作中遇到一个问题:网页展示redis中的数据,然而再Linux系统重启后网页还能展示redis中的数据,感觉很奇怪,到
    发表于 11-05 08:50

    Redis混合存储产品与架构介绍

    ,存在热点数据;内存不足以放下所有数据,且Value较大(相对于Key而言)线程模型Redis混合存储实例采用单工作线程的模式,主线程为工作线程,负责处理用户请求等主要逻辑。此外,
    发表于 08-30 16:09 140次阅读

    关于redis中数据存储的机制解析

    不同于memcached等完全基于内存的缓存中间件,Redis同时还提供了持久化功能,这也是为什么Redis不仅可以用来做数据缓存还可以用来做数据存储,服务器节点宕机之后可以通过事先持久化的数据还原数据到某个时间点的状态。
    发表于 09-02 10:46 1020次阅读
    关于<b class='flag-5'>redis</b>中数据<b class='flag-5'>存储</b>的机制解析

    redis工作原理

    Redis作为内存数据库,拥有非常高的性能,单个实例的QPS能够达到10W左右。但我们在使用Redis时,经常时不时会出现访问延迟很大的情况,如果你不知道Redis的内部实现原理,在排
    的头像 发表于 09-24 15:57 3405次阅读

    Redis持久化机制的实现原理和使用技巧

    Redis将数据存储在内存中,宕机或重启都会使内存数据全部丢失, Redis的持久化机制用来保证数据不会因为故障而丢失。
    的头像 发表于 09-13 16:42 848次阅读

    深入探究Redis存储原理

    **Redis是用C语言开发的一个开源的高性能键值对(key-value)内存数据库。** **Redis数据存储原理**
    的头像 发表于 02-15 15:52 582次阅读
    深入探究<b class='flag-5'>Redis</b><b class='flag-5'>存储</b>原理

    Redis架构演化之路

    这个架构非常简单,你的业务应用可以把 Redis 当做缓存来使用,从 MySQL 中查询数据,然后写入到 Redis 中,之后业务应用再从 Redis 中读取这些数据,由于 Redis
    的头像 发表于 08-03 16:54 353次阅读
    <b class='flag-5'>Redis</b>架构演化之路

    Redis中的使用

    Redis 作为内存的存储中间件,已经是面试的面试题必问之一了,今天一起来看看 Redis 的事务吧。 事务提供了一种"将多个命令打包,一次性提交并按顺序执行"的机制,提交后在事务执行中不会
    的头像 发表于 10-08 15:27 253次阅读
    <b class='flag-5'>Redis</b>中的使用

    redis两种持久化方式的区别

    Redis是一款高性能、开源的键值存储数据库,它支持多种数据结构,并且具有高效的内存读写以及持久化功能。Redis的持久化机制可以确保数据的持久存储,即使在
    的头像 发表于 12-04 11:12 285次阅读

    redis hash底层实现原理

    Redis是一个开源的内存数据库,使用键值对存储数据。其中,Redis中的数据结构之一就是哈希(Hash),它提供了一种将多个字段(Field)存储在一个键(Key)中的方法。那么
    的头像 发表于 12-04 16:27 302次阅读

    redis的原理和使用场景

    、消息队列、实时分析、排行榜和计数器等场景。本文将详细介绍Redis的原理和使用场景。 一、Redis的原理 Redis的原理主要包括以下几个方面: 内存数据库:Redis是一种内存数
    的头像 发表于 12-04 16:29 263次阅读

    redis是关系型数据库吗

    Server)是一个开源的高性能键值对存储系统。它使用C语言编写,具有内存中数据存储和持久化到硬盘的功能。Redis支持多种数据结构,如字符串、哈希表、列表、集合和有序集合等,这使得它可以应对各种类型的应用需求。
    的头像 发表于 12-05 10:32 790次阅读