目录
1.Streams
事件驱动机制
事件
JS (做界面)
事件是干什么的?
2.geospatial
3.HyperLogLog
4.bitmaps
5.bitfields
redis 常用的 data types 有 10 种,我们前面已经讲到了 5 种,这篇文章将对剩下的 5 种特殊数据结构进行讲解~
1.Streams
- 应用场景:主要用为队列(阻塞队列)
事件驱动机制
事件
- epoll / io 多路复用
- 每次网卡/socket上有可读可写的数据都会通过这种事件回调机制来通知到咱们的应用程序代码。
JS (做界面)
- 点击事件、键盘事件、窗口大小改变/位置改变事件...
事件是干什么的?
- 有些操作,我们也不知道它啥时候出现,只能等这个 事件 出现了之后,再采取动作
消防员的角度
- 一旦着火,立即使用干粉灭火器~~
- 如果没着火,只能等着~~
此处的“着火”就是“事件”
- “使用干粉灭火器”就是“事件触发的回调函数”
官方文档的意思,就是stream类型
- 就可以用来模拟实现这种事件传播的机制~~
- stream就是一个队列 (阻塞队列)
- 属于是 List blpop / brpop升级版本。
2.geospatial
Introduction to the Redis Geospatial data type
- 用来 存储坐标 (经纬度)
- 存储一些点之后,就可以让用户给定一个坐标,去从刚才存储的点里进行查找。(按照半径、矩形区域...)
- 这个功能在“地图”应用中非常重要~~
理解:
3.HyperLogLog
Introduction to the Redis HyperLogLog data type
- 应用场景只有一个:估算集合中的元素个数。
场景分析:
- Set,有一个应用场景,统计服务器的UV(用户访问的次数)。
-
- 使用Set当然可以统计UV,但是最大的问题在于,如果UV数据量非常大,Set就会消耗很多的内存空间。
- 假设Set存储userid,每个userid按照8个字节算:
-
- 1亿UV => 8亿字节 => 0.8G => 800MB
- HyperLogLog可以最多使用12KB空间,实现上述效果:
-
- HyperLogLog不存储元素的内容,但是能够记录元素的特征,从而在新增元素的时候,能够知道当前新增的元素是一个已经存在的元素,还是一个崭新的第一次出现的元素。
- 这个东西具体还是得分析源码~~ 核心思路“位操作” => 精确性~~(存在一定的误差)
- 用来计数(记录出当前集合中有多少个不同的元素)
-
- 但是不能告诉你这些元素都是啥~~
- The Redis HyperLogLog implementation uses up to 12 KB and provides a standard error of 0.81%。
- 对于实现的理解:
-
- 分析源码
- 数学方式来进行计算~~
4.bitmaps
Introduction to Redis bitmaps
- 使用 bit 位来表示整数。
-
- 把 10 存储到位图中
- 0000 0000 0000 000
- 计算机进行位运算,一般都是比较高效的~~
- 位图本质上,就还是一个集合,属于是 Set 类型针对整数的特化版本~~
-
- bitmap 类型存储元素了!!
- 节省空间
- HyperLogLog
-
- 更省空间呀!!
- hyperloglog 存储元素的时候,提取特征的过程是不可逆的!! (信息量丢失了)
- 给你个猪肉,能把它做成火腿肠
- 给你个火腿肠,能还原回猪肉嘛~
- bitmap 节省空间,也存在一定的可逆性
内存占用(结合使用场景选择:
- hyperloglog<bit<set
5.bitfields
- 理解:就是C中说到的位段,这里称为位域
- C 进阶,自定义数据类型 => 结构体在内存中的布局。
-
- 位域本质上是让我们精确进行位操作的一种方法~~
- 上述 Redis 中的 bitfield 和 C 中的位域,非常相似的!!
- bitfield 可以理解成一串二进制序列 (字节数组)
- 同时可以把这个字节数组中的 某几个位,赋予特定的含义,并且可以进行 读取/修改/算术运算 相关操作~~
- 位域这个东西,相比于之前的 String / hash 来说,目的仍然是节省空间
示例
- struct Test {
-
- int a:8;
- int b:16;
- int c:8;
- }
- 此处的数字,就描述这个成员实际占几个 bit 位!!
- 位域本质上是让我们精确进行位操作的一种方法~~
感兴趣的 uu 可以读一下这篇文章
https://redditinc.com/blog/how-we-built-rplace
摘录:
为了解决竞争条件问题,可以引入分布式锁来确保同一时间只有一个用户能够执行图块放置操作。此外,可以使用 Redis 的 BITFIELD
命令来实现更细粒度的控制和优化数据存储。下面是结合了这些改进后的绘制图块步骤:
- 获取分布式锁:在开始处理用户的请求之前,首先尝试获取一个分布式锁(例如使用 Redis 的
SETNX
命令)。如果无法获取锁,则拒绝该请求,并返回错误信息给用户。 - 读取并检查冷却时间:从 Cassandra 中读取用户上次放置图块的时间戳。如果时间戳显示用户还没有度过冷却期(5分钟),则释放锁,并向用户返回错误。
- 写入磁贴详细信息:
-
- 使用 Redis 的
BITFIELD
命令来更新位图,标记新的图块位置。 - 将磁贴详细信息写入 Redis 和 Cassandra。这一步骤中,你可以利用 Redis 的原子性来确保操作的完整性。
- 使用 Redis 的
- 更新用户最后放置时间:将当前时间戳作为用户最后一次放置图块的时间写入 Cassandra。
- 通知 WebSocket 服务:指示 WebSocket 服务发送消息到所有连接的客户端,告知他们新图块的信息。
- 释放分布式锁:完成所有操作后,释放分布式锁,允许其他请求继续处理。
通过这种方式,我们可以确保在同一时间内只有一个用户可以成功地放置图块,从而避免了竞争条件。同时,使用 Redis 的 BITFIELD
可以高效地管理图块的状态,因为它是基于位的操作,非常适合表示二进制状态的数据,如图块是否被占据等。
另外,为了进一步提高系统的健壮性,还可以考虑实施更严格的速率限制策略,比如使用 Redis 的 INCR
或 EVAL
脚本来实现滑动窗口算法,以防止用户或机器人频繁地进行图块放置请求。
结构体的内存对齐 回顾:
结构体对齐规则
1. 结构体的第一个成员放在结构体变量在内存中存储位置的0偏移量开始
2. 从第2个成员往后的所有成员,都放在一个对齐数(成员的大小和默认对齐数的较小值)的整数的整数倍的地址处
- VS中对齐数的默认值为8
3. 结构体的总大小是结构体的所有成员的对齐数中最大的那个对齐数的整数倍
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
为什么存在内存对齐?
平台原因(移植原因):
- 不是所有的硬件平台都能访问任意地址上的任意数据的
- 某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常
性能原因:
- 数据结构(尤其是栈)应该尽可能地在自然边界上对齐
- 原因:为了访问未对齐的内存,处理器需要作两次内存访问,而对齐的内存访问仅需要一次访问
- 总的来说,结构体的内存对齐是拿空间来换取时间的做法
设计结构体时,如何既满足对齐,又要节省空间?
- 合理安排结构体顺序:让占用空间小的成员尽量集中在一起
(感兴趣的 uu 可以看一下博主的这篇前文19.(C进阶)结构体(全),上面是截取的部分内容
《魔兽世界》(World of Warcraft,简称WoW)和《Dota 2》是两款非常受欢迎的多人在线游戏