当前位置: 首页 > news >正文

Redis常用数据结构以及多并发场景下的使用分析:list类型

文章目录

  • 前言
  • redis 中的list结构
    • 为什么使用 ziplist
    • 为什么使用linkedlist
    • quicklist = ziplist+linkedlist
  • 使用场景
    • 维护最近访问记录
    • 简单的消息队列
  • 总结

前言

首先我们学习到了 String 和Hash 两种数据类型 首先兄弟们先去复习下这两种结构哟~
Redis常用数据结构以及多并发场景下的使用分析:String类型
Redis常用数据结构以及多并发场景下的使用分析:Hash类型

本文将去探索 redis内部的list的实现原理 以及基于此数据结构 有关于list的应用
首先我们先去看看 redis 内部的list是如何去维护的 还挺有趣的这部分

redis 中的list结构

Redis版本List底层结构
Redis 2.x 及之前ziplist(压缩列表) + linkedlist(双向链表)
Redis 3.2 起引入 quicklist,替代上述两种
Redis 5.x 后默认 List 使用 quicklist,不再使用 linkedlist/ziplist 单独实现

为什么使用 ziplist

ziplist 是什么?
是一种连续内存块组成的紧凑结构
每个元素按顺序排列在一块连续内存中 节省空间,
适合少量元素的小型列表

很好理解 redis是在内存中工作的 而为了 最大效率的实现内存中操作的效率 那此时可以想到利用【cache】的原理 去加速访问数据

将数据连续的存储 放在一个连续的内存地址中 那么就可以大大的利用上内存中的缓存原理

就像数组 arr[0], arr[1], arr[2] 在一块内存里,L1/L2 Cache 一次可以加载多个元素 → 内存局部性好。

为什么使用linkedlist

ziplist 有什么缺点?
删除操作耗时 时间复杂度为O(n) 因为需要将后面的元素全部移动到删除节点
如何提高 插入删除速度 -》基于链表的队列

linkedlist 是什么? Redis 早期使用的 List 结构

每个节点是一个对象,包含前指针、后指针、value

其实就是通用意义上的linkedList 相比于ziplist 具有 插入删除快的性质 【O(1),只修改指针】

quicklist = ziplist+linkedlist

ok 你了解了ziplist 也了解了linkedlist 那么你就能了解到为什么 需要将两个结构结合起来 使用了

ziplist 能利用缓存的局部性 内存占用低 内存紧凑
linkedlist 插入删除操作快 内存占用高 内存破碎

那么就讲这两个结构结合起来 就变成了edis 5.x 后 默认的结构 quicklist

quicklist 是 ziplist + linkedlist 的融合产物 是 Redis List 的默认实现结构

quicklist 是什么?
是 多个 ziplist 的组合,用 双向链表连接起来

每个节点是一个 ziplist
整个链表类似于:“ziplist1 <-> ziplist2 <-> ziplist3…”

图示如下:
[quicklist]|├── [ziplist node 1][ziplist node 2][ziplist node 3] ...

插入的具体分析:

例子:LPUSH mylist A(从左边插入)
定位到最左边的 ziplist 节点(quicklist 的 head)如果该 ziplist 未超过最大容量(list-max-ziplist-size),则直接插入到该 ziplist 的头部否则:创建一个新的 ziplist 节点将该新 ziplist 插入到链表头部插入数据到新 ziplist
RPUSH mylist B(从右边插入)
逻辑一样,只不过是定位到尾部 ziplist

示例流程:假设 max-ziplist-size = 3,执行如下命令:

LPUSH mylist A
LPUSH mylist B
LPUSH mylist C
LPUSH mylist D

这就是插入的最终结果

quicklist:↓ head                         ↓ tail
[ziplist-1] ←→ [ziplist-2]ziplist-1: [D C B]
ziplist-2: [A]

访问顺序仍是:

DCBA

但是需要注意 quicklist 由于还是使用的多个ziplist 组 去进行连接 所以对于 基于索引的插入 还是还是 O(n)的时间复杂度哟

想想为什么?

底层是 quicklist:
虽然是链表 + ziplist,但 不支持快速定位某个索引
要插入前,Redis 需要从头开始遍历整个 quicklist 和每个 ziplist
时间复杂度:O(n),而不是 O(1)

quicklist结合了两者的优点

特性说明
综合了两者优点ziplist 节省空间 + linkedlist 快速插入删除
快速定位支持头尾插入、遍历,性能好
节点压缩可以配置压缩多个节点,减少内存
灵活性强可调参数:每个 ziplist 的元素个数、压缩策略等

使用场景

在讲解使用场景之前 首先问你一个问题 基于以上的数据结构 你觉得可以去完成什么事情。
其实可以把它当作一个

一个全局共享的(存放在redis中)、有序可变链表结构(quicklist),支持线程安全(redis 单线程特性决定的)的头尾插入/弹出操作 的数组(头尾有指针去维护)。

你去这样想 就能够理解redis中的list到底是什么了 其实数组能做的 它都能应用

维护最近访问记录

@Service
@RequiredArgsConstructor
public class RecentVisitService {private final RedisTemplate<String, String> redisTemplate;// 最大保留记录数private static final int MAX_RECENT_COUNT = 10;/*** 添加用户最近访问记录(文章ID)* @param userId 用户ID* @param articleId 文章ID*/public void addRecentVisit(String userId, String articleId) {String key = "user:" + userId + ":recent:articles";// 插入到列表头部redisTemplate.opsForList().leftPush(key, articleId);// 截断:保留前 10 个redisTemplate.opsForList().trim(key, 0, MAX_RECENT_COUNT - 1);}/*** 获取最近访问记录* @param userId 用户ID* @return 最近访问的文章ID列表(按访问时间倒序)*/public List<String> getRecentVisits(String userId) {String key = "user:" + userId + ":recent:articles";return redisTemplate.opsForList().range(key, 0, MAX_RECENT_COUNT - 1);}
}

测试类

    @Testpublic void testAddAndGetRecentVisit() {for (int i = 1; i <= 12; i++) {recentVisitService.addRecentVisit(userId, "article-" + i);}List<String> recentArticles = recentVisitService.getRecentVisits(userId);// 断言最多保留10条assertEquals(10, recentArticles.size());// 断言最近的是 article-12assertEquals("article-12", recentArticles.get(0));// 断言最后一条是 article-3(前3被裁剪掉)assertEquals("article-3", recentArticles.get(9));}

结果
在这里插入图片描述

简单的消息队列

生产者 LPUSH 进入队列
消费者 RPOP 消费队列元素

LPUSH queue:order order123    // 原子性插入
RPOP queue:order             // 原子性弹出
LLEN queue:order             // 原子性获取长度// 支持多生产者多消费者并发访问
生产者A: LPUSH queue:payment payment1
生产者B: LPUSH queue:payment payment2  
消费者C: RPOP queue:payment  → payment1
消费者D: RPOP queue:payment  → payment2

总结

Redis List在高并发场景的核心价值:

极低延迟:内存操作,毫秒级响应
高吞吐量:单机可达10万+QPS
简单可靠:操作原子性,代码简洁
实时性强:适合需要即时处理的场景
成本低:无需额外的MQ组件

适用场景:

实时消息通知
用户动态时间线
简单的任务队列
最近访问记录
实时日志收集

学到了list实现原理理解上面的场景都比较容易

http://www.dtcms.com/a/268935.html

相关文章:

  • Qt的第一个程序(2)
  • Karmada Multi-Ingress(MCI)技术实践
  • verilog中timescale指令的使用
  • javaweb———html
  • 【taro react】 ---- RuiVerifySlider 行为验证码之滑动拼图使用【天爱验证码 tianai-captcha 】实现
  • android ui thread和render thread
  • 上海新华医院奉贤院区:以元宇宙技术重构未来医疗生态
  • RAG 之 Prompt 动态选择的三种方式
  • 华为OD机试 2025B卷 - 小明减肥(C++PythonJAVAJSC语言)
  • 编辑器Vim的快速入门
  • Session的工作机制及安全性分析
  • Qt(信号槽机制)
  • 解数独(C++版本)
  • 永磁同步电机PMSM的无传感器位置控制
  • dotnet publish 发布后的项目,例如asp.net core mvc项目如何在ubuntu中运行,并可外部访问
  • 自动化运维:使用Ansible简化日常任务
  • Word 怎么让字变大、变粗、换颜色?
  • 运维打铁: PostgreSQL 数据库性能优化与高可用方案
  • Flutter 入门
  • 能源管理综合平台——分布式能源项目一站式监控
  • 海岛分布式能源系统调度 粒子群算法优化
  • 基于拉普拉斯变换与分离变量法的热传导方程求解
  • 网安系列【10】之深入浅出CSRF攻击:从原理到实战(DVWA靶场演示)
  • 商城小程序的UI设计都有哪些风格
  • 磷酸镧:多功能稀土材料,助力未来科技
  • 如何排查服务器中已经存在的后门程序?
  • SOC估算综述:电池管理中的关键挑战与前沿技术
  • 【数据结构】第七弹——Priority Queue
  • 苹果开源 DiffuCoder :用于代码生成的掩码扩散模型
  • 深度学习机器学习比较