前言:
为什么要系统的重新认识一下redis?最近关注到一些小伙伴redis 使用只是停留在 get, set 这写简单命名中。对于复杂数据结构。也是很笨重的方式在实现,比如一个大的List 行数1000+,结果直接给你全取出来然后 修改完 再完全的放回去。并发稍微高点 带宽跑满 CPU 爆了。 整个系统 三天两头出问题。
所以呢 我觉得大家在对 redis 认识和使用过程中 还是停留在表面。没有深入的了解和使用redis。
鸣谢:不要小看一个Redis!马士兵最新Redis从入门到精通全套教程开源:3天带你走向实战,让你掌握Redis面试所有核心知识!_哔哩哔哩_bilibili不要小看一个Redis!马士兵最新Redis从入门到精通全套教程开源:3天带你走向实战,让你掌握Redis面试所有核心知识!共计66条视频,包括:【redis体系课】1、redis的技术全景、【redis体系课】2、redis能否取代关系型数据库?、【redis体系课】3、Redis的版本选择与安装等,UP主更多精彩视频,请关注UP账号。
https://www.bilibili.com/video/BV1PKDsYCEh2
缓存应用 :
为什么要使用缓存?
像计算机一样为什么要使用内存?。应为硬盘的使用 读写很低,而 CPU处理速度很快。这个和时候 CPU 处理数据的速度 跟不上 硬盘读写数据的速度。这个时候就需要 使用内存条 做一个蓄水池的功能。 先把数据加载到内存 提供给CPU. 来加整个系统的速度;
我们的服务器应用一样的道理: (数据库读写数据和硬件关系很大这个数据只是中间值)
- 数据库的读取: 2000/s
- 数据库的写入: 600/s
如果所有的都走数据库,数据库必定扛不住,然后 redis 官方给的并发数值是 10万/s.。 这个数值 比 数据库 高很多。集群部署的话 还能更多
数据类型
Redis 数据库使用标准C开发的,(C语言中 Sting类型实现为 char[] 数组, Redis 中的 String类型 为 自己实现的)
● String
- set 设置一个数值
- get 获取一个数值
- getset 获取旧的值 覆盖新的值,如果key 不存在返回 nil
- append key value 给一个值追加字符串
- setnx 不存在则设置成功,存在则失败
- setxx 不存在则设置失败,存在则成功
- mset 批量设置键值 (提高效率)
- mget 批量或者 (提高效率)
- incr 自增
- decr 自减
- incrby incr_key2 3 自增并设置 步长
● Hash
- hser key field value 一次设置一个 field 字段
- hmset key field value field value 一次设置一个 field 字段
- hget key field 获取 hash key 对应的字段
- hget key field0 field1 field2 一次获取多个 hash field
- hash 必须使用 hget get 不可用 。 hmget 没有这个指令
- hdel key field
- hlen key 获取 指定hash 字段数目
- hexists key filed 判断可以 对应的 file 是否存在
- hkeys key 所有field
- hvals key 所有的值
- hgetall key 所有的键值
- HINCRBY key id 自增
● String 于 Hash 的使用场景
String:
- 优点:简单直观,每个键 对应一个值
- 缺点:键数过多,占用内存多 数据过于分散,不利于管理
将对象序列化为String:
- 优点:编程简单,序列化之后内存使用低
- 缺点:序列化和反序列化有一定开销 查询不直观。需要查询整个字符串
Hash:建议表中的数据使用 hash
- 优点:简单直观,使用合理可以减少内存开销
- 缺点:老版本注意编码
● List
列表特点:
- 元素有序
- 元素可以重复
命令:
- rpush key value0 value1 从右边插入数据
- lpush key value0 value1 从左边插入
- lrange key star stop 获取 下标指定长度 -1 指 到末尾
- lpop key count 弹出元素 左边开始
- lrem key count value 删除 count 个 value
- ltrim key start stop 截取 开始到 结束位置的 作为新的(保留)
- lset key index value 从左边设置 第 index位置 值 为 value
- lindex key index 获取 指定位置 value
- blpop key time 有数据时从左端拿去一个数据。 当value为长度 为0 的时候 阻塞客户端(time时间, 当time为0 一直堵塞 直到 有数据push 进来 或者 阻塞 time时间返回 nil)
- brpop key time 有数据时从右端拿去一个数据。 当value为长度 为0 的时候 阻塞客户端(time时间, 当time为0 一直堵塞 直到 有数据push 进来 或者 阻塞 time时间返回 nil)
使用场景:支持范围的查询
● Set
集合特点:
- 无序,不能以下标方式 获取数据
- 数据不重复
- 可做 交集 并集 差集
命令:
- sadd key value0 value1 value3 向集合中 添加 不重复的数据 返回成功个数
- srem value0 value3 返回成功个数 改变元集合大小
- smembers key 获取集合内容
- scard key 获取集合 元素个数
- sismember key value 元素是否存在于集合 返回 0/1
- srandmember key count(可省略 默认1个) 随机返回 集合数据 不改变元集合大小
- spop key count (可省略 默认1个) 弹出count 个元素 改变元集合大小
- sinter key1 key2 交集 (含所有同时属于 A 和 B 的元素的集合)返回数据
- sinterstore desKey key1 key2 求交集并输出到 desKey 集合
- sunion key1 key2 并集 (包含所有属于 A 或属于 B(或者两者都属于)的元素的集)
- sunionstore desKey key1 key2 计算并集 并输出到 desKey 集合
- sdiff key1 key2 差集 (包含所有属于 A 但不属于 B 的元素的集合 和 顺序有关)
- sdiffstore desKey key1 key2 计算差集 并输出到 desKey 集合
交集: 含所有同时属于 A 和 B 的元素的集合
> sadd set1 a b c d e f 6 > sadd set2 e f h i g k 6 > sinter set1 set2 f e > sinterstore desSet set1 set2 2 > smembers desSet e f
并集:包含所有属于 A 或属于 B(或者两者都属于)的元素的集
> sadd set1 a b c d e f 6 > sadd set2 e f h i g k 6 > sunion set1 set2 i e d c b a f g k h
差集:包含所有属于 A 但不属于 B 的元素的集合 和 顺序有关
> sadd set1 a b c d e f 6 > sadd set2 e f h i g k 6 > sdiff set1 set2 d a b c > sdiff set2 set1 h g k i > sdiff set1 set2 d a c b
使用场景:打标签,去交集,
- A用户 喜欢: 美女,旅游,看电影,夜店,读书, 养花
- B用户 喜欢: 美女,看电影, 钓鱼,睡觉,唱歌,夜店
- C用户 喜欢: 跑步,健身,交友,骑行
那么 A B 用户交集更多,可做好友
● Zset
有序集合:
命令:
- zadd key score value 向集合中添加一个元素
- zadd nx key score value 向集合中添加一个不存在的元素,若存在则报错
- zadd xx key score value 向集合中添加一个存在的元素, 若不存在则报错
- zcard key 返回元素个数
- zscore key value 返回成员分数
- zrank key value 返回分数对应的排名
- zrevrank key value 反序查找对应的 值的排名
- zrem key value 删除指定的 value 返回删除个数 0 则没有
- zincrby key count value 给指定 value 值累加 count 返回现有值
- zrange key start stop 返回 指定排名的 value
- zrange key start stop withscores 返回 指定排名的 value 和 score
- zrevrange key start stop 反序查找
- zrangebyscore key startscore stopscore 通过分数查找 对应的value
- zrangebyscore key -inf +inf withscores -inf/ +inf 通过分数查找 对应的value,最大值 最小值
应用场景:求两组 Zset 中的平均值 两个zset 中都都存在 value
> zrange zset 0 -1 withscores wanbo 90 bobo 105 jiieng 188 > zrange zset1 0 -1 withscores bobo 90 marun 95 zzy 101 > zrange zsetavg 0 -1 withscores bobo 97.5
● Bitmaps
特点:可参与位运算,压缩空间使用,它的值 只能是 0 或者 1 适合存储和操作大量的布尔值数据 支持对单个位或多个位进行设置、获取、统计等操作
命令:
- setbit key offset value 设置bitmaps 某个位置的 值
- getbit key offset
- bitcount key start stop 查找 从开始位置到结束位置 value 为 1 的数量
> setbit 2025-04-17 0 1 0 > setbit 2025-04-17 1 1 0 > setbit 2025-04-17 2 1 0 > setbit 2025-04-17 99 1 0 > setbit 2025-04-16 99 1 0 > setbit 2025-04-16 2 1 0 > bitcount 2025-04-16 0 999 2 > bitcount 2025-04-17 0 999 4
应用场景:适用于需要高效存储和操作大量布尔值或二进制状态的场景,以下是具体应用
- 用户在线状态:
- 签到系统
- 权限管理
- 统计活跃用户
布隆过滤器与Bitmaps:
- 1970年 布隆提出了一种布隆过滤器的算法。用来判断一个元素是否在 一个集合中
- 这种算法由一个 二进制数组 和 一个Hash 算法组成
- 场景:需要快速判断元素是否存在(如 URL 去重、恶意 IP 过滤)。
- 实现:通过多个 hash 函数将元素映射到 Bitmap 的不同位,1 表示可能存在,0 表示一定不存在。
- 优点:空间效率高,查询速度快。
- 布隆过滤器基于 hash 计算 会有概率会发生,存在的不一定存在,不存在的一定不存在。可以通过算法减少 或 消除 这种概率
缓存穿透的方案:
以数据库 作为兜底的方案的 查询操作。当 redis 缓存中不存在数据的时候,如果大量请求 直接顶到数据库, 数据库就会奔溃。这里可以采用 布隆过滤器 进行快速判断
拦截不存在缓存,防止 大量数据查询 顶到数据库
● HyperLogLog
如果要维护一个大型网站, 有一个一个产品经理要网站每个网页每天的 UV(独立访客 Unique Visitor) 数据。然后让你来开发这个统计模块。 如何高效实现 尽量少占用存储空间: HyperLogLog 是一种概率性数据结构,用于高效估算集合中 唯一元素的基数(cardinality) 去重
- Redis 提供了 HyperLogLog 数据结构就是就是用来解决这种统计问题
- HyperLogLog 提供不精确的去重计数方案,虽然不精确但是也不是非常不精确标准误差为 0.81%。 这样的精确已经可以满足上面的 uv 统计需求
- 网站UV统计:估算网站每日独立访客数(IP去重),数据量大时节省内存。
- 大数据去重计数:如社交平台中统计帖子的独立浏览量或点赞用户数。
- 实时分析:快速统计大规模数据流中的唯一元素,如日志分析中的独立用户或设备。
- 广告系统:估算广告的独立曝光量或点击用户数。
- 数据库查询优化:在需要近似唯一计数时,替代精确去重以提高性能。
- 百万级用户访问网
处理方式\存储 1天 1个月 1年 集合 80M 2.4G 28G HyperLogLog 15K 450K 5M
命令:
- pfadd key vaule0 value1 value2 添加元素
- pfcount key 获取元素数量
- pfmerge
![]()
● GEO
Redis GEO 是 Redis 3.2 版本引入的地理位置(Geospatial)数据结构,基于 有序集合(ZSET) 和 GeoHash 算法,用于高效存储和查询经纬度相关的地理位置数据。它支持添加地理位置、计算距离、查找附近点等操作,适用于实时、基于位置的应用场景。主要命令包括
GEOADD
、GEODIST
、GEORADIUS
等。核心功能:
- 存储经纬度坐标及其关联的成员(如地点、用户)。
- 计算两点之间的距离(基于球面距离公式)。
- 查找指定坐标或成员为中心、特定半径内的其他点。
- 返回结果支持距离排序、坐标或 GeoHash 编码。
底层实现:
- 使用 GeoHash 将经纬度编码为 52 位整数,作为有序集合的分数(score),成员名为地点或对象。
- 借助 ZSET 的范围查询能力,实现高效的空间检索和距离计算。
- 内存占用低,查询性能高,适合实时场景。
Redis GEO 适用场景:
Redis GEO 适用于需要快速处理地理位置数据的场景,尤其在以下领域表现出色:
附近的人/地点搜索:
- 社交平台(如微信、Tinder)查找附近的用户或朋友。
- 地图应用(如 Google Maps)搜索附近的餐厅、酒店、ATM 等。
距离计算:
- 外卖平台(如美团、Uber Eats)计算用户与商家的距离。
- 物流系统估算配送点到目的地的距离。
位置推荐系统:
- 推荐附近的优惠活动、商店或服务(如本地化广告)。
- 旅游应用推荐附近景点或活动。
实时位置监控:
- 共享出行(如滴滴、Lyft)跟踪司机或车辆位置,匹配附近订单。
- 共享单车系统查找附近可用的单车。
地理围栏(Geo-fencing):
- 判断用户是否进入特定区域(如商场促销区)。
- 触发基于位置的推送通知或事件(如进入机场触发登机提醒)。
数据分析:
- 分析用户位置分布或热点区域(如城市交通流量、零售店选址)。
- 支持商业智能中的地理数据洞察。
Redis GEO 特点
- 高性能:基于内存操作,查询延迟低,适合实时应用。
- 内存高效:GeoHash 编码压缩经纬度,单个位置占用字节少。
- 灵活性:支持多种距离单位(米、公里、英里、英尺),结果可包含距离、坐标或 GeoHash。
- 易用性:API 简单,集成方便,无需复杂配置。
- 可扩展:可与 Redis 其他数据结构(如 Hash、List)结合,构建复杂功能。
Redis GEO 不适用场景
- 高精度定位:GeoHash 精度有限(误差约几米),不适合厘米级精度的场景(如自动驾驶)。
- 复杂空间分析:如多边形区域查询、路径规划,需使用专业 GIS 系统(如 PostGIS、MongoDB Geo)。
- 海量历史数据存储:Redis 是内存数据库,不适合存储大规模离线位置数据,需结合其他数据库(如 MySQL、Elasticsearch)。
Redis GEO 常用命令
GEOADD key longitude latitude member
:添加地理位置。GEODIST key member1 member2 [unit]
:计算两成员间的距离(单位:m
、km
、mi
、ft
)。GEORADIUS key longitude latitude radius unit [WITHCOORD] [WITHdist]
:查找指定坐标为中心、某半径内的成员。GEORADIUSBYMEMBER key member radius unit
:查找以某成员为中心、某半径内的成员。GEOPOS key member
:获取成员的经纬度。GEOHASH key member
:返回成员的 GeoHash 字符串。