Redis | 青训营笔记

111 阅读6分钟

这是我参与「第五届青训营 」笔记创作活动的第14天。

引言

今天的课程中老师首先讲述了什么是Redis、为什么需要Redis、Redis的基本工作原理和其如何保证持久化,之后老师通过6个Redis的应用案例,来讲述如何使用Redis中的常见数据结构和命令,并进而分析对应结构的底层原理。


一、本堂课重点内容

本堂课的知识点

  • Redis是什么
  • Redis应用案例
  • Redis使用注意事项

二、详细知识点介绍

Redis是什么?

Redis 全称 Remote Dictionary Server(即远程字典服务),它是一个基于内存实现的键值型非关系(NoSQL)数据库,由意大利人 Salvatore Sanfilippo 使用 C 语言编写。

Redis体系架构主要分为两个部分:

  • Redis服务端
  • Redis客户端

Redis 服务端的默认端口号为 6379

Redis的作者在自己的一篇博文中解释了为什么选用 6379 作为默认端口,因为 6379 在手机按键上 MERZ 对应的号码,而 MERZ 取自意大利女歌手 Alessia Merz 的名字

redis 是单线程 , 基于内存操作

cpu不是Redis的性能瓶颈 ,redis的瓶颈是根据机器的内存和网络带宽

Redis如何实现持久化?

数据的持久化存储是 Redis 的重要特性之一,它能够将内存中的数据保存到本地磁盘中,实现对数据的持久存储。这样即使在服务器发生故障之后,也能通过本地磁盘对数据进行恢复。

实现持久化的技术:AOF持久化、RDB持久化和混合持久化

RDB 即快照模式,它是 Redis 默认的数据持久化方式,它会将数据库的快照保存在 dump.rdb 这个二进制文件中。

所谓“快照”就是将内存数据以二进制文件的形式保存起来。

AOF 被称为追加模式,或日志模式,是 Redis 提供的另一种持久化策略,它能够存储 Redis 服务器已经执行过的的命令,并且只记录对内存有过修改的命令(类似MySQL中的binlog,记录对数据库执行的写入型操作命令),这种数据记录方法,被叫做“增量复制”,其默认存储文件为appendonly.aof

五大基本数据类型(针对value)

1️⃣String类型

在redis中实现字符串类型,是自定义了一个属于特殊结构 SDS(Simple Dynamic  String)即简单动态字符串),这是一个可以修改的内部结构。

struct sdshdr{
 //记录buf数组中已使用字符的数量,等于 SDS 保存字符串的长度
  int len;
  //记录 buf 数组中未使用的字符数量
  int free;
 //字符数组,用于保存字符串
 char buf[];

2️⃣Hash类型

底层有两种实现方式

第一种:当存储的数据量较少的时,hash 采用 ziplist 作为底层存储结构

当hash中存储的元素较少时,Redis 会使用一块连续的内存来存储这些元素,这个连续的结构被称为 ziplist(压缩列表) ,它将所有的元素紧挨着一起存储。

此时要求符合以下两个条件:

  • 哈希对象保存的所有键值对(键和值)的字符串长度总和小于 64 个字节。
  • 哈希对象保存的键值对数量要小于 512 个。

第二种:当无法满足上述条件时,hash 就会采用第二种方式来存储数据,也就是 dict(字典结构),该结构类似于 Java 的 HashMap,是一个无序的字典,并采用了数组和链表相结合的方式存储数据。在 Redis 中,dict 是基于哈希表算法实现的,因此其查找性能非常高效,其时间复杂度为 O(1)。

处理哈希冲突:再散列,拉链法

rehash操作

当hash中底层实现中大量key使用hash算法后映射到同一个位置(dict中的槽位)时,也即有大量的哈希冲突时,采用拉链法,但这样会导致利用key进行查询value的效率大大降低。

在以上场景中,redis会重新利用一个hash算法,在dict中增加更多的槽位,需要把之前数据拷贝到现在的hash容器中,如果直接针对之前的hash容器,进行数据迁移,会导致明显的阻塞用户请求。

采用渐进式rehash,每次用户访问时都只会迁移少量数据,将迁移平摊到多次请求中

3️⃣List类型

底层采用双向链表和listpack结合实现

在链表中一个节点可能存储多个数据(并不是只存储一个数据),从而增大节点的数据密度

4️⃣Set类型

底层类似hash类型

5️⃣SorteredSet(zset)类型

底层数据结构zskiplist,即跳表

大key和热key

解决 : 数据压缩,冷热数据区分,key拆分

三、实践练习例子

连续签到

掘金平台 有一个用户签到功能(每天只能签到一次),会显示用户累计签到次数,连续签到次数。当用户当天未签到(断签)时,连续签到次数会清零(归0),所以每天必须在23.59.59前签到

如何实现:key为cc_uid_用户id,value用户连续签到次数,expireAt:后天的0点

消息通知

使用list类型作为消息队列

在掘金平台上,用户发布了一篇文章,发布后将文章部分信息推送给ES,这样使用搜索功能就能查询到这篇文章了。

计数

在掘金平台的用户主页会显示用户的文章所获点赞数,文章所获总浏览量,粉丝数,关注用户数,收藏数....等。

如果一个数量对应一个redis中的key,这是十分不可取的,且增加了和redis的交互次数,因此可以利用redis中hash类型进行存储

{
    "got_digg_count": 10693, 
    "got_view_count": 2238438, 
    "followee_count": 176, 
    "follower_count": 9895, 
    "follow_collect_set_count": 0, 
    "subscribe_tag_count": 95
},

排行榜

当用户的积分变化时,排行榜要实时变化

利用redis中的zset数据结构

限流

要求接口1秒内放行的请求数最大为N,超过N则禁止访问

redis中增加key:comment_query_limit_时间戳 ,value 0

一个请求在访问接口前,调用上述key 执行incr(加1) 得到value大于N则禁止

分布式锁

并发场景下,要求一次只能有一个协程执行,执行完成后,等待中的协程才能执行

因为redis是单线程的,所以获取redis执行操作是串行的

利用redis中的setnx实现,即setnx keyxx value 只有此key不存在,才能执行成功。

四、课后个人总结

今天学习兼复习了许多redis的知识,进一步了解了五大基本数据类型的底层实现,也从6个实际使用的案例中学习到如何去使用redis,后期根据项目的实际情况,进一步通过实践去加深对redis的理解。

五、引用参考

[1] 青训营课程资料 【后端专场 学习资料七】第五届字节跳动青训营 - 掘金 (juejin.cn)

[2] 课件 ‍‬‌⁣⁤⁢‍⁡⁡⁣‬‬第五届青训-Redis-大厂程序员是怎么用的.pptx - 飞书云文档 (feishu.cn)