# redis笔记202509 **Repository Path**: nieps/redis-notes-202509 ## Basic Information - **Project Name**: redis笔记202509 - **Description**: redis笔记 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-09-04 - **Last Updated**: 2025-09-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Redis笔记 > 关系型数据库: > > * mysql > * postgresql ( gis ) > * oracle > * 达梦(国产的) 高并发场景下,只使用关系型数据库 满足不了我们的需求 借助中间件: * 缓存的 数据库 NoSQL (not only sql ) * MQ ## Redis介绍 Redis 全称 Remote Dictionary Server(即远程字典服务),它是一个**基于内存**实现的**键值型**非关系(NoSQL)数据库。 QPS: query per second ## Redis 的核心特征 Redis 之所以成为主流的高性能存储解决方案,源于其独特且实用的核心特性: ### 1. 基于内存的超高性能 - 数据主要存储在内存中,读写操作响应时间可达微秒级 - 单节点支持每秒 10 万 + 次操作,远超传统磁盘数据库 - 采用单线程模型避免线程切换开销,同时通过 IO 多路复用处理并发请求 ### 2. 丰富的数据结构支持 提供 8 种核心数据结构,满足多样化业务需求: - **String**:字符串 / 数字,支持自增自减 - **Hash**:键值对集合,适合存储对象 - **List**:有序列表,支持两端操作 - **Set**:无序集合,支持交集、并集等操作 - **Sorted Set**:带分数的有序集合,适合排序场景 - **Bitmap**:位操作,适合布尔型数据统计 - **HyperLogLog**:基数统计,高效计算独立元素数量 - **Geospatial**:地理空间数据,支持位置距离计算 ### 3. 完善的持久化机制 确保内存数据不会因重启而丢失: - **RDB**:定时生成内存快照,适合备份和快速恢复 - **AOF**:记录所有写操作,支持实时或每秒持久化 - 混合持久化:结合 RDB 和 AOF 的优点,平衡性能和安全性 ### 4. 高可用与分布式支持 - **主从复制**:实现数据备份和读写分离 - **哨兵机制**:自动监控节点状态,实现故障转移 - **Redis Cluster**:分布式集群方案,支持数据分片和水平扩展 ### 5. 其他重要特性 - 支持事务和 Lua 脚本,保证操作原子性 - 提供过期键自动删除机制 - 发布 / 订阅功能,支持消息通信 - 支持数据过期策略和内存淘汰机制 ## Redis 的典型应用场景 ### 1. 缓存系统(最主要场景) - 存储高频访问的数据,减轻数据库负担 - 示例:电商商品详情、用户信息、热门文章等 - 优势:响应速度快,支持设置过期时间自动更新 ### 2. 会话存储 - 存储用户登录状态和会话信息 - 解决分布式系统中会话共享问题 - 支持设置过期时间,实现自动登出 ### 3. 实时排行榜 - 基于 Sorted Set 实现各类排名功能 - 示例:游戏积分排名、商品销量排行、用户贡献榜 - 优势:支持实时更新和快速查询,复杂度为 O (logN) ### 4. 计数器与限流 - 利用 String 的 INCR 命令实现原子计数 - 示例:文章阅读量、点赞数、接口请求次数统计 - 结合过期时间实现接口限流功能,防止恶意请求 ### 5. 消息队列 - 基于 List 实现简易消息队列 - 支持生产者 - 消费者模式 - 示例:异步任务处理、系统解耦 - (注:复杂场景建议使用专业消息队列) ### 6. 地理位置服务 - 基于 Geospatial 数据结构实现位置相关功能 - 示例:附近的人、商家定位、地理围栏 - 支持距离计算和范围查询 ### 7. 分布式锁 - 利用 SET 命令的 NX(不存在才设置)特性实现 - 解决分布式系统中的并发资源竞争问题 - 示例:秒杀系统、库存扣减 ### 8. 数据去重与统计 - 基于 Set 实现数据去重 - 利用 HyperLogLog 高效统计 UV(独立访客) - 示例:网站访问量统计、用户行为分析 ## 安装 ### ubuntu安装 ~~~shell #前提更新软件包列表 apt update -y #搜索redis apt list |grep redis #安装 apt install redis-server -y #查看状态 service redis status ~~~ ### docker安装 > redis端口号:6379 ~~~shell #安装 docker run -d --name redis -p 6379:6379 redis #连接 docker exec -it redis /bin/bash #进入redis redis-cli #测试 redis是否正常 ping #返回pong 说明服务正常 ~~~ ## 配置项 * 配置文件 > 1. linux /etc/redis/redis.conf 查看或更改配置项 永久更改 更改后要重启redis服务器 * 命令 > ~~~shell > #查看所有配置项 > config get * > #查看具体配置 > config get key # key 配置项名称 > #如查看日志级别 > config get loglevel > > #设置配置 > config set key value #给配置项设置值 临时的 如果redis重启 值就会失效 > #示例 > config set loglevel 'debug' > ~~~ > > ## 常用命令 ### 字符串(string) * String 是 Redis 最基本的数据类型,一个 key 对应一个 value * string 类型是二进制安全的 * string 类型的值最大能存储 512MB 应用场景:缓存、限流、分布式锁、计数器、分布式 Session 等。 #### set命令 ~~~shell SET key value [NX | XX] [GET] [EX seconds | PX milliseconds | EXAT unix-time-seconds | PXAT unix-time-milliseconds | KEEPTTL] ~~~ 选项:(常用 的加粗) * **NX** 如果对应的key值不存在,设置成功 (实现分布式锁) > ~~~shell > set name abc nx #如果key=name 在redis中不存在 ,保存name=abc ,如果存在不保存 > ~~~ * XX 如果对应的key存在,设置成功(只做更新,不做新增) > ~~~shell > set age 23 xx #如果内存中没有key=age 失败,如果有修改值为23 > ~~~ * GET 返回key对应的老值,如果不存在返回nil * **EX** seconds 以秒为单位 设置过期时间 (场景:订单30分钟未支付 自动取消 ) > 查看key剩余时间: **TTL key** 返回剩余的秒数 * `PX` *milliseconds* 以毫秒为单位 ,设置过期时间 > **PTTL key** 返回剩余的毫秒时间 * EXAT unix-time-seconds 设置key在指定时间戳过期(精确到秒) > 时间戳: 指从1970年1月1日 0:0:0 开始到 现在的秒时间 > > linux获取秒时间戳: > > ~~~shell > date +%s > ~~~ * PXAT unix-time-milliseconds 设置key在指定时间戳过期(精确到豪秒) > ~~~shell > date +%s%3N #毫秒 > ~~~ * KEEPTTL 保留key之前的过期时间 #### get命令 get key 返回key的值 ![image-20250905091748681](assets/image-20250905091748681.png) ### List * Redis List 中的元素是字符串类型 * 元素按照插入顺序进行排列,允许重复插入 * 可以添加一个元素到列表的头部(左边)或者尾部(右边) * 最多可插入的元素个数为 2^32 -1 个(大约40亿个) 应用场景:简单队列、关注列表时间轴。 ![image-20250905092449997](assets/image-20250905092449997.png) 数据结构: * 队列 FIFO 先进先出 > 方案: > > 1. lpush rpop > > ![image-20250905093158333](assets/image-20250905093158333.png) > > 2. rpush lpop * 栈 FILO 先进后出 > 方案: > > 1. lpush lpop > > ![image-20250905093418815](assets/image-20250905093418815.png) > > 2. rpush rpop ![image-20250905100451199](assets/image-20250905100451199.png) ### Hashes 哈希 * Redis hash 是一个键值(key=>value)对集合 * Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。 存储: key---field----value 应用场景: 用户信息、用户主页访问量、组合查询等。 ![image-20250905105553464](assets/image-20250905105553464.png) ### Set * Redis Set 是一个字符串类型元素构成的**无序**集合,**不允许重复** * 集合是通过哈希映射表实现的,所以无论是添加元素、删除元素,亦或是查找元素,它们的时间复杂度都为 O(1)。 应用场景: 赞、踩、标签等 交集 并集 差集 ![image-20250905111501487](assets/image-20250905111501487.png) ### Sorted set 有序集合 * Redis zset 是一个字符串类型元素构成的**有序集合**,**不允许重复** * 每个元素会关联一 个 double 类型的分数,该分数允许重复,redis正是通过分数来为集合中的成员进行从小到大的排序 应用场景: 排行榜、好友关系链表。 #### zadd ~~~shell ZADD key [NX | XX] [GT | LT] [CH] [INCR] score member [score member ...] ~~~ 选项: * NX 对应元素不存在 添加 * XX 如果元素存在更新,不存在失败 * GT (**greater than**) 如果元素存在,新分数大于当前分数,更新 ;如果元素不存在 新增 * LT (less than )如果元素存在,新分数小于当前分数,更新 ;如果元素不存在 新增 * CH 元素更新的数量 元素分数修改或新增的数量 * INCR 在已有元素的分数基础上增加分数 ### 查看key ![image-20250905101744488](assets/image-20250905101744488.png) 两种方法: * keys * 查看所有的key 在生产环境,很少用, 该命令会阻塞 (适用于平进查看少量数据) * SCAN cursor [MATCH pattern] [COUNT count] [TYPE type] > 迭代或扫描,参数说明: > > * cursor 游标 当游标设置为0时,迭代开始,当服务器返回的游标为0时,迭代结束 > * MATCH pattern 返回符合指定模式的key 如 match a* 匹配以a开头的所有key 类似于分页显示 > * COUNT count 每次迭代显示key的数量 (类似于每页记录数 默认是10) > > > `SCAN`命令以及其衍生命令并不保证每一轮迭代返回的元素数量,但是可以使用`COUNT`属性凭经验调整`SCAN`命令的行为。`COUNT`指定每次调用应该完成遍历的元素的数量,以便于遍历集合,本质只是一个提示值。 > > > > 1. `COUNT`默认值为10。 > > 2. 当遍历的目标`Set`、`Hash`、`Sorted Set`或者`Key`空间足够大可以使用一个哈希表表示并且不使用`MATCH`属性的前提下,`Redis`服务端会返回`COUNT`或者比`COUNT`大的遍历元素结果集合。 > > 3. 当遍历只包含`Integer`值的`Set`集合(也称为`intsets`),或者`ziplists`类型编码的`Hash`或者`Sorted Set`集合(说明这些集合里面的元素占用的空间足够小),那么`SCAN`命令会返回集合中的所有元素,直接忽略`COUNT`属性。 ![image-20250905151850362](assets/image-20250905151850362.png) ### Key命令 #### expire ~~~shell EXPIRE key seconds [NX | XX | GT | LT] ~~~ 选项: * NX 如果指定 的key没有过期 时间 设置成功 * XX 如果指定的key有过期时间 ,更新成功 * GT 如果新的过期时间大于当前 时间更新成功 * LT 如果新的过期时间小于当前 时间更新成功 ![image-20250905152912938](assets/image-20250905152912938.png) ### 其它 ~~~shell #清除当前数据库中所有的key flushdb #清除所有 数据库 flushall #清屏 clear #退出 exit #查看当前库中所有的key keys * ~~~ ## 在SpringBoot中使用Redis ### starter引入 ~~~xml org.springframework.boot spring-boot-starter-data-redis 2.7.4 ~~~ ### 配置文件 ~~~yaml spring: redis: # Redis服务器地址 写你的ip host: 127.0.0.1 # Redis服务器连接端口 port: 6379 # Redis服务器连接密码(默认为空) password: jedis: pool: # 连接池最大连接数(使用负值表示没有限制 类似于mysql的连接池 max-active: 200 # 连接池最大阻塞等待时间(使用负值表示没有限制) 表示连接池的链接拿完了 现在去申请需要等待的时间 max-wait: -1 # 连接池中的最大空闲连接 max-idle: 10 # 连接池中的最小空闲连接 min-idle: 0 # 连接超时时间(毫秒) 去链接redis服务端 timeout: 6000 ~~~ ### RedisTemplate配置 ~~~java @Configuration public class RedisConfig { /** * 向容器注入自定义的工具类 * * @return */ @Bean public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate redisTemplate = new RedisTemplate(); redisTemplate.setConnectionFactory(redisConnectionFactory); //设置序列化器 StringRedisSerializer srs = new StringRedisSerializer();//字符串序列化 //设置rediskey redisTemplate.setKeySerializer(srs); //设置hash field redisTemplate.setHashKeySerializer(srs); //value用javabean Jackson2JsonRedisSerializer jsr = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常 om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL); jsr.setObjectMapper(om); //设置value redisTemplate.setValueSerializer(jsr); //设置hashvalue redisTemplate.setHashValueSerializer(jsr); return redisTemplate; } } ~~~ ### RedisTemplate ![image-20250905160759046](assets/image-20250905160759046.png) ## Redis缓存 ![image-20250905173529592](assets/image-20250905173529592.png) ## Redis应用 * 验证码 存储到redis * token 双token > 登录成功: > > * accessToken 访问token 前端请求,接口必须携带的token ,只有有token且有效才 才允许 访问 > > * refreshToken ----(如使用uuid生成36位字符串 ) -- 根据该toke请求后端接口获取新的token > > 用redis存储refreshToken ,值是用户信息 * **秒杀** * 实现 **后端购物车功能** 可以实现多端同步, * 订单30分钟未支付 自动取消 ## 秒杀 需求: 1. 快速响应 抢购成功与否 快速给出响应 2. 避免重复消费 允许用户点 击抢购多少次 只会最多生成一个订单 3. 如何用户在指定 时间 未支付 自动取消 (库存要还回去) 4. 解决库存超卖问题 ### 缓存替代页面静态化 ### 秒杀按钮 如果秒杀未开始 ,按钮置灰,或倒计时 ,时间了 按钮可用 防抖处理 ### 读多写少 做秒杀 商品的数量 有限 ,查看商品的人多,真正抢到的少 通过预减库存实现。在redis存储要秒杀商品的数量 ,在 用户请求时,尝试扣减redis的库存,如果扣减完大于等于0,抢购成功,如果为-1 逻辑: 1. 如果一个用户已 抢过 不允许 抢 > 可以使用字符串setNx 作业:用redis实现购物车功能 ,可以存放多个商品,每个商品数量不一样 1. 加入购物车 2. 增加数量 /减少数量 3. 查看购物车 4. 删除购物车中的商品 5. 全选 、反选 ## Bitmap(了解) ![image-20250908173437211](assets/image-20250908173437211.png) 适合用于开关,既只有2个值 考勤: ![image-20250908171305414](assets/image-20250908171305414.png) ## hyperloglog(了解) 场景:做访问统计 > PV(页面浏览量 pageview ):指在一定时间内,网页被浏览的总次数。每次用户访问页面都会计入一次PV,无论是同一用户的多次访问还是不同用户的访问。 > > UV(独立访客):指在一定时间内,访问网站的独立用户数量。每个独立用户只计算一次,即使他们多次访问。 > > VV(视频播放量):指视频被播放的次数,通常用于衡量视频内容的受欢迎程度。 > > 这些指标在网站分析和广告投放中非常重要,有助于优化网站流量和提高广告效果。 使用set 实现uv , 用户访问一次,可以将用户的ip 保存到set里 ,最后统计 元素的数量就是独立访客量 ![image-20250908173414338](assets/image-20250908173414338.png) ## Geo(了解) 附件的朋友、商家 ,根据位置计算 模拟共享单车: * b1 34.807338 113.578109 * b2 34.806862,113.578130 * b3 34.807611,113.575963 * b4 34.807250,113.573656 ### 范围查找 核心:找一个中心点 ``` GEOSEARCH key | BYBOX width height > [ASC | DESC] [COUNT count [ANY]] [WITHCOORD] [WITHDIST] [WITHHASH] ``` 选项: * FROMMEMBER member 通过成员 指定中心点 * FROMLONLAT longitude latitude 通过经纬度指定中心点 * BYRADIUS radius 以radius 为半径 画圆 在半径范围内车辆都会显示 * BYBOX width height 以中心点为矩形交叉点 按指定 width height 画矩形 * ASC | DESC 排序 asc 升序 desc 降序 * WITHDIST 显示距离 * COUNT count [ANY] 显示的数量 * WITHCOORD 显示经纬度 ## 发布订阅(了解) ![image-20250909112905645](assets/image-20250909112905645.png) ## 事务(了解) ![image-20250909112848381](assets/image-20250909112848381.png) ## 持久化机制 > 面试题: > > 1. rdb 和aof区别? > 2. redis是单进程单线程的吗? 配置项: * dbfilename redis备份文件的名称 * dir redis备份文件的路径 备份命令: * save 阻塞的 * bgsave 后台运行 非阻塞 ### AOF和RDB对比 | RDB持久化 | AOF持久化 | | ------------------------------------------------------------ | -------------------------------------------- | | 全量备份,一次保存整个数据库。 | 增量备份,一次只保存一个修改数据库的命令。 | | 每次执行持久化操作的间隔时间较长。 | 保存的间隔默认为一秒钟(Everysec) | | 数据保存为二进制格式,其还原速度快。 | 使用文本格式还原数据,所以数据还原速度一般。 | | 执行 SAVE 命令时会阻塞服务器,但手动或者自动触发的 BGSAVE 不会阻塞服务器 | AOF持久化无论何时都不会阻塞服务器。 | 如果进行数据恢复时,既有 dump.rdb文件,又有 appendonly.aof 文件,您应该先通过 appendonly.aof 恢复数据,这能最大程度地保证数据的安全性。 ## 分布式锁(重点) 分布式系统中,有一个变量 lock=0 ---1 线程A----- 先获取锁 > setNx lock 假如redis没有key=lock ----设置成功---抢到锁了 ---调用资源 ----假如1s执行完 ----手动释放锁(del lock ) > > expire lock 5 设置5s超时时长 线程B-----尝试获取锁 -----setNx lock----redis存在,设置失败 ---抢锁失败 -----没有权限调用资源 ~~~shell #抢锁 set lock 1 nx px 5 #如果成功 ----调用目标服务 #释放锁 del lock ~~~ > 假如使用mysql实现分布式锁: > > 假如有一条记录: > > id lock expire > > 1 0 > > 线程A----抢锁: > > ~~~mysql > # 1. 抢锁 lock=1 代表被占用 > UPDATE s_lock > SET islock = 1, > expire = DATE_ADD( now(), INTERVAL 5 MINUTE ) > WHERE > id = 1 > AND ( > islock = 0 > OR ( islock = 1 AND expire < now() )); > > #2.释放锁 > update s_lock set islock=0 where id=1; > > > ~~~ > > ## 缓存问题(重点 高频) ## 删除策略和淘汰策略 > 删除: > > 1. 定时删除 > 对于每一个设置了过期时间的key都会创建一个定时器,一旦到达过期时间就立即删除。该策略可以立即清除过期的数据,对内存较友好,但是缺点是占用了大量的CPU资源去处理过期的数据,会影响Redis的吞吐量和响应时间。 > > 2. 惰性删除 > 当访问一个key时,才判断该key是否过期,过期则删除。该策略能最大限度地节省CPU资源,但是对内存却十分不友好。有一种极端的情况是可能出现大量的过期key没有被再次访问,因此不会被清除,导致占用了大量的内存。 > > 3. 定期删除 > 每隔一段时间,扫描Redis中过期key字典,并清除部分过期的key。该策略是前两者的一个折中方案,还可以通过调整定时扫描的时间间隔和每次扫描的限定耗时,在不同情况下使得CPU和内存资源达到最优的平衡效果。 > 淘汰:8种 > > 过期策略分两大类: > > * 针对设置了过期时间的key volatile > * 针对所有的key allkeys > > 策略: > > * lru 最近最少使用 > * randome > * lfu > > 特殊: > > * 默认: 没有内存,写入失败,读取正常 > * 有过期时间的 volatile-ttl 马上到期的删除 ## 主从 三种实现方式 ### 启动服务 指定 选项--slaveof * 主机: 6379 写的 * 从机: > 1. 6000 > > ~~~shell > #启动redis 在 6000端口 做为从机(读的功能) daemonize 守护进程 指向 主机 192.168.221.128 6379 > redis-server --port 6000 --daemonize yes --slaveof 127.0.0.1 6379 > ~~~ > > > > 2. 7000 > > ~~~shell > #启动redis 在 6000端口 做为从机(读的功能) daemonize 守护进程 指向 主机 192.168.221.128 6379 > redis-server --port 7000 --daemonize yes --slaveof 127.0.0.1 6379 > ~~~ > > > > 3. 8000 ### 提供配置文件 提供配置文件如redis6000.conf ,内容如下 ~~~ port 6000 slaveof 127.0.0.1 6379 ~~~ 启动服务指定主机: ~~~shell redis-server redis6000.conf ~~~ ### 命令 ~~~shell 127.0.0.1:6000> SLAVEOF 127.0.0.1 6379 ~~~ > 使用nginx做负载均衡 ## Lua脚本 ## 用过redis没?谈谈你对redis的了解? 1. 什么 是redis? 是一个基于内存 kv型数据库 可以介绍里面常用 的数据类型及对应的应用场景 2. 使用redis最主要的应用场景 缓存 > 基本的逻辑:先查缓存,有返回,没有查询数据库,再缓存并返回 使用缓存需要注意 问题: 1. 缓存穿透 2. 缓存击穿 3. 缓存雪崩 当更新数据库时,要保持redis与mysql数据一致问题? > 常见的方法: > > * 先更新数据库 再删除缓存 > > ![image-20250910151349332](assets/image-20250910151349332.png) > > ![image-20250910151940160](assets/image-20250910151940160.png) > > * 先删除缓存 再更新数据库 > > ![image-20250910151727927](assets/image-20250910151727927.png) * 延时双删 ## 布隆过滤器 ![image-20250910154257942](assets/image-20250910154257942.png)