Redis(一):基础数据结构

Redis 有 5 种基础数据结构,分别为:string (字符串)、list (列表)、set (集合)、hash (哈希) 和 zset (有序集合)。

image.png

1、String 字符串

字符串 string 是 Redis 最简单的数据结构。Redis 所有的数据结构都是以唯一的 key 字符串作为名称,然后通过这个唯一 key 值来获取相应的 value 数据。不同类型的数据结构的差异就在于 value 的结构不一样。字符串结构使用非常广泛,一个常见的用途就是缓存用户信息。我们将用户信息结构体使用 JSON 序列化成字符串,然后将序列化后的字符串塞进 Redis 来缓存。同样,取用户信息会经过一次反序列化的过程。

键值对命令:

image.png

批量键值对命令:可以批量对多个字符串进行读写,节省网络耗时开销

image.png

过期和 set 命令扩展:可以对 key 设置过期时间,到点自动删除,这个功能常用来控制缓存的失效时间

image.png

原子计数:如果 value 值是一个整数,还可以对它进行自增操作。自增是有范围的,它的范围是 signed long 的最大最小值,超过了这个值,Redis 会报错

image.png

2、list(列表)

Redis的列表相当于java中的LinkedList,是链表而不是数组,这意味着list的插入和删除操作非常快,时间复杂度为O(1),但是索引定位很慢,时间复杂度为O(n),当列表弹出了最后一个元素以后,该数据结构自动被删除,内存被回收;
Redis的列表结构常用来做异步队列使用,将需要延后处理的任务结构体序列化为字符串塞进Redis的列表,另一个线程从这个列表中轮询数据进行处理(netty做网络通信时,就可以使用,先把发送的消息存到redis列表中,然后另一个线程在轮询发送数据);

右边进左边出:队列

image.png

右边进右边出:栈

image.png
image.png

当然,也可以通过ltrim 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。

redis 127.0.0.1:6379> LTRIM KEY_NAME START STOP;
* 注:start 和stop 是指list中元素位置下标,都是从0开始,下标 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。
你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。

3、hash(字典)

Redis的字典相当于Java中的HashMap,是无序字典,内部实现结构上同Java的HashMap也是一致的,是数组+链表二维结构的,第一维hash的数组位置碰撞时,就会将碰撞的元素使用链表串接起来;
hash结构也可以用来存储用户信息,不同于字符串String需要一次性全部序列化整个对象,hash结构可以对用户结构中的每个字段单独存储,这样当我们需要获取用户信息时,可以进行部分获取,而以整个字符串的形式去保存用户信息的话,就只能一次性全部读取,这样就会比较浪费网络流量;
hash也有缺点,就是hash结构的存储消耗要高于单个字符串。

image.png

4、Set(集合)

实现一个无序不重复的列表,Redis的集合相当于Java中的HashSet,内部的键值对是无序的唯一的(无序是指不是按照插入数据顺序排序,而是按照字典序排序),内部实现相当于一个特殊的字典(hash),字典中所有的value都是一个值NULL,当集合中最后一个元素移除之后,数据结构自动删除,内存被回收。

image.png

set还支持交集 并集 差集

sdiff key1 key2       返回直接定集合的差集
sinter key1 key2     返回直接定集合的交集
sunion key1 key2   返回直接定集合的并集

5、zset(有序集合)

实现一个有序不重复的列表,zset类似于java的SortedSet和HashMap的结合体,一方面它是一个set,保证了内存value的唯一性,另一方面它可以给每个value赋予一个score,代表这个value排序权重。
zset可以用来存粉丝列表,value值是粉丝的用户ID,score的关注时间,可以可以对粉丝列表按关注时间进行排序;
zset还可以用来存储学生的成绩,value值是学生ID,score是他的成绩,我们可以对成绩按分数进行排序就可以得到他的名次;

image.png

6、高级命令

  • keys:全量遍历键,用来列出所有满足正则字符串规则的key,当redis数据量比较大时,性能比较差,要避免使用
image.png
  • scan:渐进式遍历键,scan参数提供了三个参数,第一个是cursor整数值,第二个是key的正则模式,第三个是遍历的limit hint;
    第一次遍历时,cursor值为0,然后将返回结果中第一个整数值作为下一次遍历的cursor,一直遍历到返回的cursor值为0时结束
image.png
image.png

Redis存储键值对实际使用的是hashtable的数据结构

image.png
  • info:查看redis服务运行信息,分为9大块,每个块都有非常多的参数,这9个块分别是:
    1、Server 服务器运行的环境参数
    2、Clients 客户端相关信息
    3、Memory 服务器运行内存统计数据
    4、Persistence 持久化信息
    5、Stats 通用统计数据
    6、Replication 主从复制相关信息
    7、CPU CPU 使用情况
    8、Cluster 集群信息
    9、KeySpace 键值对统计数量信息
image.png

7、核心原理

1、Redis的单线程和高性能

  • Redis为什么这么快,尤其是其采用单线程
    因为它的所有的数据都在内存中,所有的运算都是内存级别的运算,而且单线程避免了多线程的切换性能损耗问题;
    而且正因为Redis是单线程,所以要小心使用Redis指令,对于那些耗时的指令(比如keys),一定要谨慎使用,一步小心就可能会导致Redis卡顿;

  • Redis单线程处理多个并发客户端连接:IO多路复用
    Redis的IO多路复用:redis利用epoll来实现IO多路复用,将连接信息和事件放到队列中,依次放到文件事件分派器,事件分派器将事件分发给事件处理器。
    Nginx也是采用IO多路复用原理解决C10k问题

image.png

2、持久化

  • RDB快照(snapshot)
    在默认情况下,Redis将数据库快照保存在名字为dump.rdb的二进制文件中。
    可以对Redis进行设置,让它在“N秒内数据集中至少有M个改的”这个条件被满足时,自动保存一次数据集。
//在满足“60s内至少有1000个键被改动”这一条件时,自动保存一次数据集
save 60 100
  • AOF(append-only file)
    快照功能并不是非常耐久(durable):如果Redis因为某些原因而造成故障停机,那么服务器将丢失最近写入、且仍未保存到快照中的哪些数据。
    从1.1版本开始,Redis增加了一种完全耐久的持久化方式,AOF持久化,将修改的每一条指令记录进文件,就是说,每当Redis执行一个改变数据集的命令时(比如SET),这个命令就会被追加到AOF文件的末尾,这样的话,当Redis重新启动时,程序就可以通过重新执行AOF文件中的命令来达到重建数据集的目的。
//通过修改配置文件来打开AOF功能
appendonly yes

可以配置 Redis 多久才将数据 fsync 到磁盘一次,有三个选项:
1、每次有新命令追加到 AOF 文件时就执行一次 fsync :非常慢,也非常安全。
2、每秒 fsync 一次:足够快(和使用 RDB 持久化差不多),并且在故障时只会丢失 1 秒钟的数据。
3、从不 fsync :将数据交给操作系统来处理。更快,也更不安全的选择。
推荐(并且也是默认)的措施为每秒 fsync 一次, 这种 fsync 策略可以兼顾速度和安全性。

  • RDB vs AOF
    如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。
    有很多用户都只使用 AOF 持久化, 但我们并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快。

  • Redis 4.0 混合持久化
    重启 Redis 时,我们很少使用 rdb 来恢复内存状态,因为会丢失大量数据。我们通常使用 AOF 日志重放,但是重放 AOF 日志性能相对 rdb 来说要慢很多,这样在 Redis 实例很大的情况下,启动需要花费很长的时间。 Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。AOF在重写(aof文件里可能有太多没用指令,所以aof会定期根据内存的最新数据生成aof文件)时将 rdb文件的内容和增量的 AOF 日志文件存在一起,AOF根据配置规则在后台自动重写,也可以人为执行命令bgrewriteaof重写AOF。这里的 AOF 日志不再是全量的日志,而是自持久化开始到持久化结束的这段时间发生的增量 AOF 日志,通常这部分 AOF 日志很小。 于是在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。

//开启混合持久化:
aof-use-rdb-preamble yes  

混合持久化aof文件结构

image.png

3、缓存淘汰策略
当 Redis 内存超出物理内存限制时,内存的数据会开始和磁盘产生频繁的交换 (swap)。交换会让 Redis 的性能急剧下降,对于访问量比较频繁的 Redis 来说,这样龟速的存取效率基本上等于不可用。
在生产环境中我们是不允许 Redis 出现交换行为的,为了限制最大使用内存,Redis 提供了配置参数 maxmemory 来限制内存超出期望大小。
当实际内存超出 maxmemory 时,Redis 提供了几种可选策略 (maxmemory-policy) 来让用户自己决定该如何腾出新的空间以继续提供读写服务。

  • noeviction:不会继续服务写请求 (DEL 请求可以继续服务),读请求可以继续进行。这样可以保证不会丢失数据,但是会让线上的业务不能持续进行。这是默认的淘汰策略。
  • volatile-lru:尝试淘汰设置了过期时间的 key,最少使用的 key 优先被淘汰。没有设置过期时间的 key 不会被淘汰,这样可以保证需要持久化的数据不会突然丢失。
  • volatile-ttl:跟上面一样,除了淘汰的策略不是 LRU,而是 key 的剩余寿命 ttl 的值,ttl 越小越优先被淘汰。
  • volatile-random:跟上面一样,不过淘汰的 key 是过期 key 集合中随机的 key。
  • allkeys-lru:区别于 volatile-lru,这个策略要淘汰的 key 对象是全体的 key 集合,而不只是过期的 key 集合。这意味着没有设置过期时间的 key 也会被淘汰。
  • allkeys-random:跟上面一样,不过淘汰的策略是随机的 key。

volatile-xxx 策略只会针对带过期时间的 key 进行淘汰,allkeys-xxx 策略会对所有的 key 进行淘汰。如果你只是拿 Redis 做缓存,那应该使用 allkeys-xxx,客户端写缓存时不必携带过期时间。如果你还想同时使用 Redis 的持久化功能,那就使用 volatile-xxx 策略,这样可以保留没有设置过期时间的 key,它们是永久的 key 不会被 LRU 算法淘汰

4、缓存失效策略

  • 定时删除策略
    在设置key的过期时间的同时,为该key创建一个定时器,让定时器在key的过期时间来临时,对可以进行删除。
    优点:保证内存尽快释放
    缺点:若key过多,删除这些key会占用很多cpu时间,而且每个key创建一个定时器,性能影响严重

  • 惰性删除策略
    key过期的时候不删除,每次从数据库redis获取key的时候去检查是否过期,若过期,则删除,同时返回null
    优点:cpu时间占用比较少
    缺点:若key很长时间没有被获取,将不会被删除,可能造成内存泄漏

  • 定期删除策略
    每隔一段时间执行一次删除(在redis.conf配置文件设置hz,1s刷新的频率)过期key的操作
    优点:可以通过控制删除操作的时长和频率,来减少cpu时间的占用,可以避免惰性删除时候内存泄漏的问题
    缺点:对内存友好方面,不如定时删除策略
    对CPU友好方面,不过惰性删除策略

Redis一般采用:惰性策略 + 定期策略两个相结合

Redis 的缓存淘汰策略用于处理内存不足时的需要申请额外空间的数据;
Redis 的过期策略用于处理过期的缓存数据

8、脑图

image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容