一 、redis是什么?
Redis = Remote Dictionary Server,即远程字典服务。
是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
二、redis五大基本类型
Redis是一个开源,内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。它支持字符串、哈希表、列表、集合、有序集合,位图,hyperloglogs等数据类型。内置复制、Lua脚本、LRU收回、事务以及不同级别磁盘持久化功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动分区。
由于redis类型大家很熟悉,且网上命令使用介绍很多,下面重点介绍五大基本类型的底层数据结构与应用场景,以便当开发时,可以熟练使用redis。
1、String(字符串)
1、String类型是redis的最基础的数据结构,也是最经常使用到的类型。
而且其他的四种类型多多少少都是在字符串类型的基础上构建的,所以String类型是redis的基础。
2、String 类型的值最大能存储 512MB,这里的String类型可以是简单字符串、
复杂的xml/json的字符串、二进制图像或者音频的字符串、以及可以是数字的字符串
应用场景
1、缓存功能:String字符串是最常用的数据类型,不仅仅是redis,各个语言都是最基本类型,因此,利用redis作为缓存,配合其它数据库作为存储层,利用redis支持高并发的特点,可以大大加快系统的读写速度、以及降低后端数据库的压力。
2、计数器:许多系统都会使用redis作为系统的实时计数器,可以快速实现计数和查询的功能。而且最终的数据结果可以按照特定的时间落地到数据库或者其它存储介质当中进行永久保存。
3、统计多单位的数量:eg,uid:gongming count:0 根据不同的uid更新count数量。
4、共享用户session:用户重新刷新一次界面,可能需要访问一下数据进行重新登录,或者访问页面缓存cookie,这两种方式做有一定弊端,1)每次都重新登录效率低下 2)cookie保存在客户端,有安全隐患。这时可以利用redis将用户的session集中管理,在这种模式只需要保证redis的高可用,每次用户session的更新和获取都可以快速完成。大大提高效率。
2、List(列表)
list类型是用来存储多个有序的字符串的,列表当中的每一个字符看做一个元素
2.一个列表当中可以存储有一个或者多个元素,redis的list支持存储2^32次方-1个元素。
3.redis可以从列表的两端进行插入(pubsh)和弹出(pop)元素,支持读取指定范围的元素集,
或者读取指定下标的元素等操作。redis列表是一种比较灵活的链表数据结构,它可以充当队列或者栈的角色。
4.redis列表是链表型的数据结构,所以它的元素是有序的,而且列表内的元素是可以重复的。
意味着它可以根据链表的下标获取指定的元素和某个范围内的元素集。
应用场景
1、消息队列:reids的链表结构,可以轻松实现阻塞队列,可以使用左进右出的命令组成来完成队列的设计。比如:数据的生产者可以通过Lpush命令从左边插入数据,多个数据消费者,可以使用BRpop命令阻塞的“抢”列表尾部的数据。
2、文章列表或者数据分页展示的应用。比如,我们常用的博客网站的文章列表,当用户量越来越多时,而且每一个用户都有自己的文章列表,而且当文章多时,都需要分页展示,这时可以考虑使用redis的列表,列表不但有序同时还支持按照范围内获取元素,可以完美解决分页查询功能。大大提高查询效率。
3、Set(集合)
1.redis集合(set)类型和list列表类型类似,都可以用来存储多个字符串元素的集合。
2.但是和list不同的是set集合当中不允许重复的元素。而且set集合当中元素是没有顺序的,不存在元素下标。
3.redis的set类型是使用哈希表构造的,因此复杂度是O(1),它支持集合内的增删改查,
并且支持多个集合间的交集、并集、差集操作。可以利用这些集合操作,解决程序开发过程当中很多数据集合间的问题
应用场景
1、标签:比如我们博客网站常常使用到的兴趣标签,把一个个有着相同爱好,关注类似内容的用户利用一个标签把他们进行归并。
2、共同好友功能,共同喜好,或者可以引申到二度好友之类的扩展应用。
3、统计网站的独立IP。利用set集合当中元素不唯一性,可以快速实时统计访问网站的独立IP。
数据结构
set的底层结构相对复杂写,使用了intset和hashtable两种数据结构存储,intset可以理解为数组。
4、sorted set(有序集合)
redis有序集合也是集合类型的一部分,所以它保留了集合中元素不能重复的特性,但是不同的是,有序集合给每个元素多设置了一个分数。
redis有序集合也是集合类型的一部分,所以它保留了集合中元素不能重复的特性,但是不同的是,有序集合给每个元素多设置了一个分数,利用该分数作为排序的依据。
应用场景
1、 排行榜:有序集合经典使用场景。例如视频网站需要对用户上传的视频做排行榜,榜单维护可能是多方面:按照时间、按照播放量、按照获得的赞数等。
2、用Sorted Sets来做带权重的队列,比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择按score的倒序来获取工作任务。让重要的任务优先执行。
5、 hash(哈希)
Redis hash数据结构 是一个键值对(key-value)集合,它是一个 string 类型的 field 和 value 的映射表,
redis本身就是一个key-value型数据库,因此hash数据结构相当于在value中又套了一层key-value型数据。
所以redis中hash数据结构特别适合存储关系型对象
应用场景
1、由于hash数据类型的key-value的特性,用来存储关系型数据库中表记录,是redis中哈希类型最常用的场景。一条记录作为一个key-value,把每列属性值对应成field-value存储在哈希表当中,然后通过key值来区分表当中的主键。
2、经常被用来存储用户相关信息。优化用户信息的获取,不需要重复从数据库当中读取,提高系统性能。
三、Redis持久化模型
如果我们要使用 Redis 存储任何类型的数据同时要求安全保存,了解 Redis 是如何做到这一点很重要。在许多用例中,如果你丢失了 Redis 存储的数据,这并不是世界末日。将其用作缓存或在其支持实时分析的情况下,如果发生数据丢失,则并非世界末日。
在其他场景中,我们希望围绕数据持久性和恢复有一些保证。
![](https://i-blog.csdnimg.cn/blog_migrate/61daead92c79fbd50623410d55a33604.png)
1、无持久化
无持久化:如果你愿意,可以完全禁用持久化。这是运行 Redis 的最快方式,并且没有持久性保证。
2、RDB文件
RDB(Redis 数据库):RDB 持久化以指定的时间间隔执行数据集的时间点快照。
这种机制的主要缺点是快照之间的数据会丢失。此外,这种存储机制还依赖于主进程的 fork,在更大的数据集中,这可能会导致服务请求的瞬间延迟。话虽如此,RDB 文件在内存中的加载速度要比 AOF 快得多。
3、AOF
AOF(Append Only File):AOF 持久化记录服务器接收到的每个写入操作,这些操作将在服务器启动时再次被执行,重建原始数据集。
这种持久性的方法能够确保比 RDB 快照更持久,因为它是一个仅附加文件。随着操作的发生,我们将它们缓冲到日志中,但它们还没有被持久化。该日志与我们运行的实际命令一致,以便在需要时进行重放。
然后,如果可能,我们使用 fsync 将其刷新到磁盘(当此运行可配置时),它将被持久化。缺点是格式不紧凑,并且比 RDB 文件使用更多的磁盘。
为什么不兼得?
RDB + AOF:可以将 AOF 和 RDB 组合在同一个 Redis 实例中。如果你愿意的话,可以以速度换取持久化是一种折衷方法。我认为这是设置 Redis 的一种可接受的方式。在重启的情况下,请记住如果两者都启用,Redis 将使用 AOF 来重建数据,因为它是最完整的。
Forking
现在我们了解了持久化的类型,让我们讨论一下我们如何在像 Redis 这样的单线程应用程序中实际执行它。
![](https://i-blog.csdnimg.cn/blog_migrate/f7ccf3b59ce033c9b24c7533f99b37fb.png)
在我看来,Redis 最酷的部分是它如何利用 forking 和写时复制来高效地促进数据持久化。
Forking 是操作系统通过创建自身副本来创建新进程的一种方式。这样,你将获得一个新的进程 ID 和一些其他信息和句柄,因此新 forking 的进程(子进程)可以与原始进程父进程通信。
现在事情变得有趣了。Redis 是一个分配了大量内存的进程,那么它如何在不耗尽内存的情况下进行复制呢?
当你 fork 一个进程时,父进程和子进程共享内存,并且在该子进程中 Redis 开始快照(Redis)进程。这是通过一种称为写时复制的内存共享技术实现的——该技术在创建分叉时传递对内存的引用。如果在子进程持久化到磁盘时没有发生任何更改,则不会进行新的分配。
在发生更改的情况下,内核会跟踪对每个页面的引用,如果某个页面有多个更改,则将更改写入新页面。子进程完全不知道更改以及具有一致的内存快照的事情。因此,在只使用了一小部分内存的情况下,我们能够非常快速有效地获得潜在千兆字节内存的时间点快照!