1redis基础

Redis基础

Redis和Memchched

  • 数据类型:Memcached 仅支持字符串类型,而 Redis 支持五种不同的数据类型,可以更灵活地解决问题。
  • 数据持久化:Redis 支持两种持久化策略:RDB 快照和 AOF 日志,而 Memcached 不支持持久化。
  • 分布式:Memcached 不支持分布式,只能通过在客户端使用一致性哈希来实现分布式存储,这种方式在存储和查询时都需要先在客户端计算一次数据所在的节点。
  • 内存管理机制
    • 在 Redis 中,并不是所有数据都一直存储在内存中,可以将一些很久没用的 value 交换到磁盘,而 Memcached 的数据则会一直在内存中。
    • Memcached 将内存分割成特定长度的块来存储数据,以完全解决内存碎片的问题。但是这种方式会使得内存的利用率不高,例如块的大小为 128 bytes,只存储 100 bytes 的数据,那么剩下的 28 bytes 就浪费掉了

性能

  • Redis默认有六个数据库,从0-5,每个数据库单例能处理key:2.5亿个,一个key或是value大小最大是512M;Redis性能还是极高的,读的速度是110000次/s,写的速度是81000次/s 。

通用命令

  • 仅当不存在时赋值,两种写法
    • set key value nx
    • SETNX
    • SETXX : 只有键已经存在时,才对键进行色赋值操作
  • setex key timeout value
    • 为指定的 key 设置值及其过期时间。如果 key 已经存在, SETEX 命令将会替换旧的值。
    • setpx 设置键的过期时间为毫秒
  • keys * 获取所有的key * 号可以匹配正则
  • type key 获取该key的数据类型
  • del key 删除指定的key value
  • exists key 确认一个key是否存在
  • expire key seconds 设置一个key的生存时间 ttl key : 返回一个key的剩余过期事件。
  • rename oldkeyname newkeyname 重命名key
  • SCAN
    • SACN 命令每次被调用之后, 都会向用户返回一个新的游标, 用户在下次迭代时需要使用这个新游标作为 SCAN 命令的游标参数, 以此来延续之前的迭代过程。当 SCAN命令的游标参数被设置为 0 时, 服务器将开始一次新的迭代, 而当服务器向用户返回值为 0 的游标时, 表示迭代已结束。

数据类型

String

  • 包含三种字符串、数值、bitmap
  • 存储:set key value
  • 获取:get key
  • 删除:del key
  • 追加:APPEND
  • 用指定的字符串覆盖给定 key 所储存的字符串值,覆盖的位置从偏移量 offset 开始
    • setrange k1 6 v2
  • 获取存储在指定 key 中字符串的子字符串。字符串的截取范围由 start 和 end 两个偏移量决定(包括 start 和 end 在内)。
    • getrange k1 start end (支持反向索引)
  • 重新赋值并返回原值: getset k1 v2
  • 获取字符串长度: STRLEN key
  • 同时赋值或获取: mset mget

二进制安全

  • 空字符’\0‘,C语言是判断空字符去判断一个字符的长度的,但是有很多数据结构经常会穿插空字符在中间,比如图片,音频,视频,压缩文件的二进制数据。
  • Redis的String使用长度去校验是否到了字符的结尾

数值增减

  • 整数才能进行数值增减
  • 原子性操作
  • 自增命令 incr
  • 制定步长自增 incrby key increment(步长)
  • 递减 decr decrby

Hash

  • 存储: hset key filed1 value1 filed2 value2
  • 获取: hget key filed1
  • 全部获取:hgetall key
  • 删除: hdel key filed1
  • 判断字段是否存在:HEXISTS key filed
  • 只获取字段名或字段数量: hkeys key 、hvalues key
  • 获取字段数量 : HLEN key
  • 哈希表中的字段值加上指定增量值: Hincrby key filed1 INCR_BY_NUMBER
  • 应用场景
    • 哈希支持单独修改字段,存储那些对象数据,特别是对象属性经常发生增删改操作的数据
  • Redis 7.0 中,压缩列表数据结构已经废弃了,交由 listpack 数据结构来实现了。

List

  • 双向链表实现,有序重复,按照出入顺序排序,可以添加元素到头部和尾部
  • 在 Redis 3.2 版本之后,List 数据类型底层数据结构就只由 quicklist 实现了,替代了双向链表和压缩列表
  • 赋值:lpush key value1 | rpush key value1
    • 取值和赋值使用相同方向的命令可以模拟队列,使用方向相反的命令可以模拟栈
  • 获取元素个数:llen key
  • 获取
    • 范围获取:lrange key start end (0 -1):取出全部
  • 获取或设置指定索引的元素值
    • LINDEX key index LSET key index value
  • 只保留区间指定的元素
    • LTRIM key start stop
  • 向列表中插入元素
    • LINSERT key BEFORE|AFTER pivot value
      • 该命令首先会在列表中从左到右查找值为pivot的元素,然后根据第二个参数是BEFORE还是AFTER来决定将value插入到该元素的前面还是后面。
  • 将元素从一个列表转移到另一个列表
    • RPOPLPUSH source destination
  • 删除
    • lpop key : 删除一个元素并返回 rpop key
    • 删除类表中指定个数的指定值
      • LREM key count value
        • 当count>0时, LREM会从列表左边开始删除。
        • 当count<0时, LREM会从列表后边开始删除。
        • 当count=0时, LREM删除所有值为value的元素。
  • 阻塞查找
    • blpop
  • 应用场景
    • 消息队列
    • 时间线
    • 文章列表或者数据分页展示的应用。
      • 比如,我们常用的博客网站的文章列表,当用户量越来越多时,而且每一个用户都有自己的文章列表,而且当文章多时,都需要分页展示,这时可以考虑使用Redis的列表,列表不但有序同时还支持按照范围内获取元素,可以完美解决分页查询功能。大大提高查询效率。

Set

  • 无序,不允许重复元素,内部是hash实现,支持交集操作
  • 添加
    • sadd key value
  • 获取
    • smembers key : 获取所有元素
  • 删除
    • srem key value : 删除集合中的某个元素
  • 判断元素是否在集合中
    • SISMEMBER key member
  • 获取集合中的元素个数
    • SCARD key
  • 从集合中弹出某个元素
    • SPOP key
  • SRANDMEMBER key count
    • 用于返回集合中的一个随机元素
    • 如果countcount 为正数,且小于集合基数,那么命令返回一个包含 count 个元素的数组,数组中的元素各不相同。如果 count 大于等于集合基数,那么返回整个集合。
    • 如果count 为负数,那么命令返回一个数组,数组中的元素可能会重复出现多次,而数组的长度为 count 的绝对值。
  • SPOP key count
    • 用于移除集合中的指定 key 的count个随机元素,移除后会返回移除的元素。

运算指令

  • 差集运算
    • SDIFF key1 [key …]
      • 获得key1与其他集合的差集
  • 交集运算
    • SINTER key [key …]
  • 并集运算
    • SUNION key [key …]

Sortedset

  • 有序,不允许重复元素,通过哈希表实现,排序后的集合中的每个元素都与一个称为得分的浮点值相关联,按照分数进行排序
  • 存储
    • zadd key score value
  • 获取
    • zrange key start end (0 -1):取出全部 withsores(获取全部分数)
    • zrevrange 从大到小
  • 获取元素的分数
    • ZSCORE key member
  • 获得集合中元素的数量
    • ZCARD key
  • 获得指定分数范围的元素
    • ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
  • 增加某个元素的分数
    • ZINCRBY key increment member
  • 删除
    • zrem key value
  • 获得指定范围内的元素个数
    • ZCOUNT key min max
  • 按照排名范围删除元素
    • ZREMRANGEBYRANK key start stop
  • 按照分数范围删除元素
    • ZREMRANGEBYSCORE key min max
  • 获取元素的排名
    • ZRANK | ZREVRANK key member
  • 取交集
    • ZUNIONSTORE destination numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX]
    • numkeys 为key的数量 WEIGHTS 为各个key的权重,合并时会按照权重计算分数 AGGREGATE 为当value值相同,对于相同的value值的source的处理

排序的数据结构

  • 元素唯一且按分数(score)排序,结合哈希表(快速查找)和跳跃表(范围查询)。
  • skip list,类似于数据库的B+树,区别是skip list是随机选层

应用场景

  • 排行榜:有序集合经典使用场景。例如视频网站需要对用户上传的视频做排行榜,榜单维护可能是多方面:按照时间、按照播放量、按照获得的赞数等
  • Sorted Sets来做带权重的队列,比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择按score的倒序来获取工作任务。让重要的任务优先执行。

bitMap

+

  • 基于 String 的位操作,每个 bit 表示 0/1。
  • setbit k1 1 1 :将第一个字节的第二位变为1,默认为0, 0000 0000 -> 0100 0000
  • bitcount k start end: 统计start end中1出现的次数
  • bitpos k bit start end : 返回位图中第一个值为bit的位置
  • bitop operation destkey key1 key2…:对一个或多个保存二进制位的字符串key进行位元操作,并将结果保存到destksy上
    • operation可以是AND、OR、NOT、XOR

HyperLogLog

  • 基数统计(估算集合中不重复元素数量),误差率约 **0.81%**。
  • 在 Redis 中实现的 HyperLoglog 也只需要 12 K 内存,在 标准误差 0.81% 的前提下,能够统计 2的64 次方数据
  • 应用场景
    • 统计独立 IP 访问量。
    • 大型数据集的去重计数(如搜索关键词去重)。

Geospatial

  • 基于 Sorted Set 存储经纬度,使用 GeoHash 编码。
  • 应用场景
    • 配送距离计算
    • 附近的人/地点

Stream

  • Redis 5.0 引入,用于消息队列,支持多消费者组和消息持久化。
  • 应用场景
    • 异步通信系统(类似 Kafka)
    • 日志收集(支持回溯消费)

数据结构

image-20250706230757299

键值对

  • image-20250706231124951

  • redisDb 结构,表示 Redis 数据库的结构,结构体里存放了指向了 dict 结构的指针;

  • dict 结构,结构体里存放了 2 个哈希表,正常情况下都是用「哈希表1」,「哈希表2」只有在 rehash 的时候才用

  • ditctht 结构,表示哈希表的结构,结构里存放了哈希表数组,数组中的每个元素都是指向一个哈希表节点结构(dictEntry)的指针;

  • 数组中的每个元素都是指向一个哈希表节点结构(dictEntry)的指针;key 指向的是 String 对象,而 \value 则可以指向 String 对象,也可以指向集合类型的对象,比如 List 对象、Hash 对象、Set 对象和 Zset 对象。

SDS

  • String键的底层实现 还被用作缓冲区
  • 不使用原生的字符串的原因
    • 原生C语言字符串采用遍历拿到字符串长度,效率太低
    • 无法识别\0,因此不能保存二进制数据
  • 底层是char数组
  • 还减少了修改字符串时带来的内存重新分配次数
    • 空间预分配:当我们对SDS进行扩展操作的时候,Redis会为SDS分配好内存,并且根据特定的公式,分配多余的free空间,还有多余的1byte空间(这1byte也是为了存空字符)
    • 惰性空间释放:当我们执行完一个字符串缩减的操作,redis并不会马上收回我们的空间,因为可以预防你继续添加的操作,这样可以减少分配空间带来的消耗,但是当你再次操作还是没用到多余空间的时候,Redis也还是会收回对于的空间,防止内存的浪费的。

intest

  • 整数集合(intset)是集合键的底层实现之一,当一个集合只包含整数值元素,并且这个集合的元素数量不多时,Redis就会使用整数集合作为集合键的底层实现
  • 整数集合(intset)是 Redis 用于保存整数值的集合抽象数据结构, 它可以保存类型为 int16_t 、 int32_t 或者 int64_t 的整数值, 并且保证集合中不会出现重复元素

dict(字典)

  • 是哈希键的底层实现
  • 字典内部包含两个hashtable

渐进式rehash

  • 渐进式 rehash 会在 rehash 的同时,保留新旧两个 hash 结构,查询时会同时查询两个 hash 结构,然后在后续的定时任务以及 hash 操作指令中,循序渐进的把旧字典的内容迁移到新字典中。当搬迁完成了,就会使用新的 hash 结构取而代之。

扩缩容的阈值

  • hash 表中 元素的个数等于第一维数组的长度时,就会开始扩容,扩容的新数组是 原数组大小的 2 倍,如果在进行持久化则尽量不去扩容,如果达到了数组长度的五倍,会强制扩容
  • **元素个数低于数组长度的 10%**,缩容不会考虑 Redis 是否在做 bgsave

跳表

  • 有序集合的底层实现

  • O(log n)

quicklist

  • 在 Redis 3.0 之前,List 对象的底层数据结构是双向链表或者压缩列表。然后在 Redis 3.2 的时候,List 对象的底层改由 quicklist 数据结构实现。
  • quicklist 就是「双向链表 + 压缩列表」组合,因为一个 quicklist 就是一个链表,而链表中的每个元素又是一个压缩列表。
  • 压缩列表是 Redis 为了节约内存而开发的,它是由连续内存块组成的顺序型数据结构
  • 解决压缩列表的不足
    • 通过控制每个链表节点中的压缩列表的大小或者元素个数,来规避连锁更新的问题

list park

  • quicklist 虽然通过控制 quicklistNode 结构里的压缩列表的大小或者元素个数,来减少连锁更新带来的性能影响,但是并没有完全解决连锁更新的问题。

  • listpark

  • 节点的数据结构

    • encoding,定义该元素的编码类型,会对不同长度的整数和字符串进行编码;
    • data,实际存放的数据;
    • len,encoding+data的总长度;

发布订阅模式(Pub/Sub)

  • PUBLISH channel message
    • 推送message到channel
  • SUBSCRIBE channel
  • 订阅某个channel

redis事务

  • Redis的事务是通过MULTI,EXEC,DISCARD和WATCH这四个命令来完成的。
  • Redis的单个命令都是原子性的,所以这里确保事务性的对象是命令集合。
  • Redis将命令集合序列化并确保处于同一事务的命令集合连续且不被打断的执行
  • Redis不支持回滚操作
    • 大多数事务失败都是因为语法错误或者类型错误,这两种错误都是开发过程中可以预见的。
    • 性能问题

相关语句

  • MULIT
    • 用于标记事务块的开始,将后续的命令压入FIFO队列中,最终使用EXEC来执行这个队列
  • EXEC
    • 在一个事务中执行所有先前放入队列的命令,然后恢复正常的连接状态
  • DISCARD
    • 清除所有先前在一个事务中放入队列的命令,然后恢复正常的连接状态。
  • WATCH
    • 当某个事务需要按条件执行时,就要使用这个命令将给定的键设置为受监控的状态。
    • 通过CAS的乐观锁,当监控数据发生变化,事务不执行
  • UNWATCH
    • 清除所有先前为一个事务监控的键。

事务失败处理

  • redis语法错误(编译器错误)
    • 语法错误,所有的执行均不提交
  • redis类型错误(运行起错误)
    • 不回滚,执行错误语句前的所有语句并提交

key的过期策略

  • 定时策略:在设置key的过期时间的同时,为该key创建一个定时器,让定时器在key的过期时间来临时,对key进行删除。
    • 优点:保证内存被尽快释放,减少无效的缓存暂用内存。
    • 缺点:若过期key很多,删除这些key会占用很多的CPU时间,在CPU时间紧张的情况下,CPU不能把所有的时间用来做要紧的事儿,还需要去花时间删除这些key。定时器的创建耗时,若为每一个设置过期时间的key创建一个定时器(将会有大量的定时器产生),性能影响严重。一般来说,是不会选择该策略模式。
  • 惰性策略:key过期的时候不删除,每次从数据库获取key的时候去检查是否过期,若过期,则删除,返回null。
    • 优点:删除操作只发生在从数据库取出key的时候发生,而且只删除当前key,所以对CPU时间的占用是比较少的,而且此时的删除是已经到了非做不可的地步
    • 缺点:若大量的key在超出超时时间后,很久一段时间内,都没有被获取过,此时的无效缓存是永久暂用在内存中的,那么可能发生内存泄露(无用的垃圾占用了大量的内存)
  • 定期策略:每隔一段时间对设置了缓存时间的key进行检测,如果可以已经失效,则从内存中删除,如果未失效,则不作任何处理。
    • 优点:通过限制删除操作的时长和频率,来减少删除操作对CPU时间的占用–处理”定时删除”的缺点 定期删除过期key–处理”惰性删除”的缺点。
    • 缺点:在内存友好方面,不如”定时删除”,因为是随机遍历一些key,因此存在部分key过期,但遍历key时,没有被遍历到,过期的key仍在内存中。在CPU时间友好方面,不如”惰性删除”,定期删除也会暂用CPU性能消耗。

数据淘汰策略

  • 可以设置内存最大使用量,当内存使用量超出时,会实行数据淘汰策略。
  • 6 种淘汰策略
  • 策略 描述
    volatile-lru 从已设置过期时间的数据集中挑选最近最少使用的数据淘汰
    volatile-ttl 从已设置过期时间的数据集中挑选将要过期的数据淘汰
    volatile-random 从已设置过期时间的数据集中任意选择数据淘汰
    allkeys-lru 从所有数据集中挑选最近最少使用的数据淘汰
    allkeys-random 从所有数据集中任意选择数据进行淘汰
    noeviction 禁止驱逐数
  • 作为内存数据库,出于对性能和内存消耗的考虑,Redis 的淘汰算法实际实现上并非针对所有 key,而是抽样一小部分并且从中选出被淘汰的 key。
  • 使用 Redis 缓存数据时,为了提高缓存命中率,需要保证缓存数据都是热点数据。可以将内存最大使用量设置为热点数据占用的内存量,然后启用 allkeys-lru 淘汰策略,将最近最少使用的数据淘汰。
  • Redis 4.0 引入了 volatile-lfu 和 allkeys-lfu 淘汰策略,LFU 策略通过统计访问频率,将访问频率最少的键值对淘汰

参考文章


1redis基础
https://x-leonidas.github.io/2025/10/26/05数据库/redis/1redis基础/
作者
听风
发布于
2025年10月26日
更新于
2025年8月8日
许可协议