SakuraMotion 2021 - 2024年2月
type: channel
link: https://yingwu2021.xyz/index.php/2024/02/
description: 长期的应试教育让我花了太久才明白：学习其实是一件只与自己相关的事。 现在的我，更愿意主动探索那些真正能激发好奇心、让生活变得更有趣的事物。 如果你有能够启发我、让我产生兴趣的想法、机会或故事，欢迎写信给我： （ljr0115@outlook.com） 我会优先回复那些 与我的时间安排契合、或与我当下关注方向相吻合 的邮件。
items: RocketMQ常见问题总结
items: Redis 常见数据类型和应用场景

RocketMQ常见问题总结
type: item
date: 2024-02-29T12:05:00+08:00
description: AI 摘要 文章系统梳理RocketMQ核心知识点：从消息队列诞生的异步、解耦、削峰三大价值，到RocketMQ主题模型、NameServer+Broker架构、零拷贝高性能存储、同步/异步刷盘与复制、事务消息、顺序与重复消费、消息堆积等常见问题及解决方案，并给出代码级最佳实践。 此摘要根据文章内容自动生成，仅用于文章内容的解释与总结title: RocketMQ常见问题总结category: 高性能tag:RocketMQ消息队列消息队列扫盲消息队列顾名思义就是存放消息的队列，队列我就不解释了，别告诉我你连队列都不知道是啥吧？所以问题并不是消息队列是什么，而是 消息队列为什么会出现？消息队列能用来干什么？用它来干这些事会带来什么好处？消息队列会带来副作用吗？消息队列为什么会出现？消息队``列算是作为后端程序员的一个必备技能吧，因为分布式应用必定涉及到各个系统之间的通信问题，这个时候消息队列也应运而生了。可以说分布式的产生是消息队列的基础，而分布式怕是一个很古老的概念了吧，所以消息队列也是一个很古老的中间件了。消息队列能用来干什么？异步你可能会反驳我，应用之间的通信又不是只能由消息队列解决，好好的通信为什么中间非要插一个消息队列呢？我不能直接进行通信吗？很好 👍，你又提出了一个概念，同步通信。就比如现在业界使用比较多的 Dubbo 就是一个适用于各个系统之间同步通信的 RPC 框架。我来举个 🌰 吧，比如我们有一个购票系统，需求是用户在购买完之后能接收到购买完成的短信。我们省略中间的网络通信时间消耗，假如购票系统处理需要 150ms ，短信系统处理需要 200ms ，那么整个处理流程的时间消耗就是 150ms + 200ms = 350ms。当然，乍看没什么问题。可是仔细一想你就感觉有点问题，我用户购票在购票系统的时候其实就已经完成了购买，而我现在通过同步调用非要让整个请求拉长时间，而短信系统这玩意又不是很有必要，它仅仅是一个辅助功能增强用户体验感而已。我现在整个调用流程就有点 头重脚轻 的感觉了，购票是一个不太耗时的流程，而我现在因为同步调用，非要等待发送短信这个比较耗时的操作才返回结果。那我如果再加一个发送邮件呢？这样整个系统的调用链又变长了，整个时间就变成了 550ms。当我们在学生时代需要在食堂排队的时候，我们和食堂大妈就是一个同步的模型。我们需要告诉食堂大妈：“姐姐，给我加个鸡腿，再加个酸辣土豆丝，帮我浇点汁上去，多打点饭哦 😋😋😋” 咦~~~ 为了多吃点，真恶心。然后大妈帮我们打饭配菜，我们看着大妈那颤抖的手和掉落的土豆丝不禁咽了咽口水。最终我们从大妈手中接过饭菜然后去寻找座位了...回想一下，我们在给大妈发送需要的信息之后我们是 同步等待大妈给我配好饭菜 的，上面我们只是加了鸡腿和土豆丝，万一我再加一个番茄牛腩，韭菜鸡蛋，这样是不是大妈打饭配菜的流程就会变长，我们等待的时间也会相应的变长。那后来，我们工作赚钱了有钱去饭店吃饭了，我们告诉服务员来一碗牛肉面加个荷包蛋 (传达一个消息) ，然后我们就可以在饭桌上安心的玩手机了 (干自己其他事情) ，等到我们的牛肉面上了我们就可以吃了。这其中我们也就传达了一个消息，然后我们又转过头干其他事情了。这其中虽然做面的时间没有变短，但是我们只需要传达一个消息就可以干其他事情了，这是一个 异步 的概念。所以，为了解决这一个问题，聪明的程序员在中间也加了个类似于服务员的中间件——消息队列。这个时候我们就可以把模型给改造了。这样，我们在将消息存入消息队列之后我们就可以直接返回了(我们告诉服务员我们要吃什么然后玩手机)，所以整个耗时只是 150ms + 10ms = 160ms。但是你需要注意的是，整个流程的时长是没变的，就像你仅仅告诉服务员要吃什么是不会影响到做面的速度的。解耦回到最初同步调用的过程，我们写个伪代码简单概括一下。那么第二步，我们又添加了一个发送邮件，我们就得重新去修改代码，如果我们又加一个需求：用户购买完还需要给他加积分，这个时候我们是不是又得改代码？如果你觉得还行，那么我这个时候不要发邮件这个服务了呢，我是不是又得改代码，又得重启应用？这样改来改去是不是很麻烦，那么 此时我们就用一个消息队列在中间进行解耦 。你需要注意的是，我们后面的发送短信、发送邮件、添加积分等一些操作都依赖于上面的 result ，这东西抽象出来就是购票的处理结果呀，比如订单号，用户账号等等，也就是说我们后面的一系列服务都是需要同样的消息来进行处理。既然这样，我们是不是可以通过 “广播消息” 来实现。我上面所讲的“广播”并不是真正的广播，而是接下来的系统作为消费者去 订阅 特定的主题。比如我们这里的主题就可以叫做 订票 ，我们购买系统作为一个生产者去生产这条消息放入消息队列，然后消费者订阅了这个…

Redis 常见数据类型和应用场景
type: item
date: 2024-02-09T06:40:00+08:00
description: AI 摘要 文章系统梳理了 Redis 九大数据类型的底层结构、常用命令与典型场景：String 做缓存/计数/锁/Session，List 做队列，Hash 存对象/购物车，Set 去重与交集运算，Zset 排行榜，BitMap 二值统计，HyperLogLog 海量基数，GEO 附近位置，Stream 专业队列；并给出选型与内存优化建议。 此摘要根据文章内容自动生成，仅用于文章内容的解释与总结Redis 常见数据类型和应用场景大家好，我是樱舞。我们都知道 Redis 提供了丰富的数据类型，常见的有五种：String（字符串），Hash（哈希），List（列表），Set（集合）、Zset（有序集合）。随着 Redis 版本的更新，后面又支持了四种数据类型： BitMap（2.2 版新增）、HyperLogLog（2.8 版新增）、GEO（3.2 版新增）、Stream（5.0 版新增）。每种数据对象都各自的应用场景，你能说出它们各自的应用场景吗？面试过程中，这个问题也很常被问到，又比如会举例一个应用场景来问你，让你说使用哪种 Redis 数据类型来实现。所以，这次我们就来学习 Redis 数据类型的使用以及应用场景。PS：你可以自己本机安装 Redis 或者通过 Redis 官网提供的在线 Redis 环境 来敲命令。String介绍String 是最基本的 key-value 结构，key 是唯一标识，value 是具体的值，value 其实不仅是字符串，也可以是数字（整数或浮点数），value 最多可以容纳的数据长度是 512M。内部实现String 类型的底层的数据结构实现主要是 int 和 SDS（简单动态字符串）。SDS 和我们认识的 C 字符串不太一样，之所以没有使用 C 语言的字符串表示，因为 SDS 相比于 C 的原生字符串：SDS 不仅可以保存文本数据，还可以保存二进制数据。因为 SDS 使用 len 属性的值而不是空字符来判断字符串是否结束，并且 SDS 的所有 API 都会以处理二进制的方式来处理 SDS 存放在 buf[] 数组里的数据。所以 SDS 不光能存放文本数据，而且能保存图片、音频、视频、压缩文件这样的二进制数据。SDS 获取字符串长度的时间复杂度是 O(1)。因为 C 语言的字符串并不记录自身长度，所以获取长度的复杂度为 O(n)；而 SDS 结构里用 len 属性记录了字符串长度，所以复杂度为 O(1)。Redis 的 SDS API 是安全的，拼接字符串不会造成缓冲区溢出。因为 SDS 在拼接字符串之前会检查 SDS 空间是否满足要求，如果空间不够会自动扩容，所以不会导致缓冲区溢出的问题。字符串对象的内部编码（encoding）有 3 种：int、raw 和 embstr。如果一个字符串对象保存的是整数值，并且这个整数值可以用long类型来表示，那么字符串对象会将整数值保存在字符串对象结构的ptr属性里面（将void*转换成 long），并将字符串对象的编码设置为int。如果字符串对象保存的是一个字符串，并且这个字符串的长度小于等于 32 字节（redis 2.+版本），那么字符串对象将使用一个简单动态字符串（SDS）来保存这个字符串，并将对象的编码设置为embstr， embstr编码是专门用于保存短字符串的一种优化编码方式：如果字符串对象保存的是一个字符串，并且这个字符串的长度大于 32 字节（redis 2.+版本），那么字符串对象将使用一个简单动态字符串（SDS）来保存这个字符串，并将对象的编码设置为raw：注意，embstr 编码和 raw 编码的边界在 redis 不同版本中是不一样的：redis 2.+ 是 32 字节redis 3.0-4.0 是 39 字节redis 5.0 是 44 字节可以看到embstr和raw编码都会使用SDS来保存值，但不同之处在于embstr会通过一次内存分配函数来分配一块连续的内存空间来保存redisObject和SDS，而raw编码会通过调用两次内存分配函数来分别分配两块空间来保存redisObject和SDS。Redis 这样做会有很多好处：embstr编码将创建字符串对象所需的内存分配次数从 raw 编码的两次降低为一次；释放 embstr编码的字符串对象同样只需要调用一次内存释放函数；因为embstr编码的字符串对象的所有数据都保存在一块连续的内存里面可以更好的利用 CPU 缓存提升性能。但是 embstr 也有缺点的：如果字符串的长度增加需要重新分配内存时，整个 redisObject 和 sds 都需要重新分配空间，所以embstr 编码的字符串对象实际上是只读的，redis 没有为 embstr 编码的字符串对象编写任何相应…