【redis】简介
点击阅读更多查看文章内容
什么是redis
Redis诞生于2009年全称是Remote Dictionary Server,远程词典服务器,是一个基于内存的键值型NoSQL数据库。
特征:
- 键值型,value支持多种不同数据结构,功能丰富
- 单线程,redis处理命令请求是单线程的避免上下文切换
- 低延迟,速度快(基于内存、IO多路复用、良好的编码)
- 支持数据持久化(redis数据会落到磁盘持久化)
- 支持主从集群、分片集群
- 支持多语言客户端
安装redis
Install Redis on Windows | Docs
windows下需要安装wsl,通过wsl进入linux子系统
通过redis-cli进入redis命令行客户端进行交互
redis常见命令
数据结构介绍
key一般是String类型,不过value类型多样,前五种为基本类型,后三种为特殊类型
命令帮助文档:Commands | Docs
通过命令行中 help @数据类型,也可以查看对应类型的文档
通用命令
- keys:查看符合模板的所有key,pattern是redis的通配符,不建议在生产环境设备中使用,匹配时间较长单线程会阻塞

- del:删除一个指定的key
- exists:判断key是否存在
- expire:给一个key设置有效期,有效期到期时该key会被自动删除,节省内存空间(短期验证码)
- ttl:查看一个key的剩余有效期
String类型
根据字符串的格式不同,又可以分为3类:
- string:普通字符串
- int:整数类型,可以做自增、自减操作
- float:浮点类型,可以做自增、自减操作
不管哪种格式,底层都是字节数组形式存储,只不过编码方式不同,字符串类型的最大空间不超过512m
常见命令:
key的层级格式
Redis没有类似MySQL中的Table的概念,我们该如何区分不同类型的key呢?
例如,需要存储用户、商品信息到redis,有一个用户id是1,有一个商品id恰好也是1
Redis的key允许有多个单词形成层级结构,多个单词之间用 ‘:’ 隔开,例如:
如果value是一个对象,则可以将对象序列化为json字符串后存储:
Hash类型
其value是一个无序字典,类似于哈希表
存储对象时使用JSON序列化修改不方便
hash可以将对象中的每个字段独立存储,针对单个字段做CRUD
常见命令:
List类型
list可以看做是一个双向链表的结构,既可以支持正向检索也可以支持反向检索
- 有序
- 元素可以重复
- 插入和删除快(直接修改节点指向)
- 查询速度一般(遍历)
常用来存储一个有序数据:朋友圈点赞列表、评论列表等
Set类型
集合
- 无序
- 元素不可重复
- 查找快
- 支持交集、并集、差集等功能
常见命令:
SortedSet类型
SortedSet是一个可排序的set集合,SortedSet中的每一个元素都带有一个score属性,可以基于score属性对元素排序,底层的实现是一个跳表加hash表
- 可排序
- 元素不重复
- 查询速度快
因为SortedSet的可排序特性,经常被用来实现排行榜这样的功能
go-redis
redis/go-redis: Redis Go client
安装:go get github.com/redis/go-redis/v9
建立连接:
1 | rdb := redis.NewClient(&redis.Options{ |
Set/Get:
1 | ctx := context.Background() |
短信登陆
基于session
集群的session共享问题:多台服务器并不共享session存储空间,当请求切换到不同的服务器时会导致数据丢失的问题。
session的替代方案应该满足:
- 数据共享
- 内存存储
- key-value数据结构
基于Redis实现共享session登录
初始登陆:
校验登录状态:
商户查询缓存
什么是缓存?
缓存就是数据交换的缓冲区(Cache),是存贮数据的临时地方,一般读写性能较高。它可以位于内存中,也可以位于 CPU、磁盘等位置。缓存的主要目的是减少访问 慢速存储(如磁盘或数据库)的时间,从而提高系统性能。
CPU内部具有一个缓存,通过缓存读写数据比内存或磁盘快得多。
web应用开发中缓存的使用场景:
作用:
- 降低后端负载
- 提高读写效率,降低响应时间
成本:
- 数据一致性成本
- 代码维护成本(解决一致性问题)
- 运维成本(缓存穿透、缓存雪崩、缓存击穿)
添加Redis缓存
在查询商户时添加缓存
代码实现:
缓存更新策略
主动更新策略:
线程安全问题: 线程安全问题 是指在 多线程环境 中,多个线程同时访问共享资源时,如果没有采取适当的同步机制,可能导致程序的行为变得不确定,甚至出现错误的情况。
- 先删除缓存,再操作数据库:线程1删除缓存后,在更新数据库之前,线程2查询缓存未命中再查询数据库将旧的数据写入缓存。线程1更新数据库的操作是比较慢的,线程2查询的操作相对比较快,因此这种缓存与数据库不一致的现象发生的概率是比较高的。
- 先操作数据库,再操作缓存:线程1查询缓存未命中,查询数据库的数据,再将数据写入缓存之前,线程2更新了数据库并删除缓存了,此时线程1将更新前的数据写入缓存。线程1写缓存的速度是非常快的,在这期间内线程2要完成数据库更新以及缓存删除的操作,这种现象发生的概率是比较低的。
缓存穿透
缓存穿透:客户端请求的数据在缓存和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。
解决方案:
缓存空对象
实现简单,维护方便
额外的内存消耗;可能造成短期的不一致

布隆过滤
- 内存占用少,没有多余key
- 实现复杂,存在误判可能
使用缓存空对象解决缓存穿透问题:
除了以上两种被动解决方案(在发生缓存穿透之后的解决方案),还可以主动解决(在未发生缓存传统之前避免出现缓存传统):
- 增强id的复杂度,避免被猜测id规律,做好数据的基础格式校验
- 在请求到达缓存层之前,先进行基本参数检查,避免无意义的查询。
- 加强用户权限校验
- 做好热点参数的限流
缓存雪崩
缓存雪崩:在同一时段内大量的缓存key同时失效或者redis服务宕机,导致大量请求到达数据库,带来巨大压力
解决方案:
- 给不同的Key的TTL添加随机值
- 利用Redis集群提高服务的可用性
- 给缓存业务添加降级限流策略
- 给业务添加多级缓存
缓存击穿
缓存击穿:缓存击穿问题也叫热点Key问题,就是一个高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。
解决方案:
互斥锁
- 效率较低:只有一个线程在重建数据,其它的所有线程等在等待
逻辑过期:一个线程重建,其它线程返回旧数据。不为key设置TTL,而是添加一个expire字段,如果数据过期则获取锁开启新线程执行数据重建,其它的线程都直接返回旧数据

基于互斥锁解决缓存击穿问题:(互斥锁可以用redis的setnx设置一个key,只有第一个设置才会生效,后续的设置因为已经存在不会生效)
基于逻辑过期解决缓存击穿问题:
缓存雪崩:大量key到期;缓存击穿:少量热点key到期;这两种问题的后果都是大量请求打到数据库

