zoukankan      html  css  js  c++  java
  • Redis实践---集合类型的选择

    集合类型常见的四种统计模式,包括聚合统计、排序统计、二值状态统计和基数统计。

    聚合统计

    所谓的聚合统计,就是指统计多个集合元素的聚合结果,包括:

      统计多个集合的共有元素(交集统计);

      把两个集合相比,统计其中一个集合独有的元素(差集统计);

      统计多个集合的所有元素(并集统计)。

    当你需要对多个集合进行聚合计算时,Set 类型会是一个非常不错的选择。不过,我要提醒你一下,这里有一个潜在的风险。

    Set 的差集、并集和交集的计算复杂度较高,在数据量较大的情况下,如果直接执行这些计算,会导致 Redis 实例阻塞。

    所以,我给你分享一个小建议:你可以从主从集群中选择一个从库,让它专门负责聚合计算,或者是把数据读取到客户端,在客户端来完成聚合统计,这样就可以规避阻塞主库实例和其他从库实例的风险了。

    排序统计

    应对集合元素排序需求, 这就要求集合类型能对元素保序,也就是说,集合中的元素可以按序排列,这种对元素保序的集合类型叫作有序集合。

    在 Redis 常用的 4 个集合类型中(List、Hash、Set、Sorted Set),List 和 Sorted Set 就属于有序集合。

    List 是按照元素进入 List 的顺序进行排序的,而 Sorted Set 可以根据元素的权重来排序,我们可以自己来决定每个元素的权重值。比如说,我们可以根据元素插入 Sorted Set 的时间确定权重值,先插入的元素权重小,后插入的元素权重大。

    在面对需要展示最新列表、排行榜等场景时,如果数据更新频繁或者需要分页显示,建议你优先考虑使用 Sorted Set。

    二值状态统计

         Bitmap 本身是用 String 类型作为底层数据结构实现的一种统计二值状态的数据类型。这是 Redis 提供的扩展数据类型。可以把 Bitmap 看作是一个 bit 数组。

    Bitmap 提供了 GETBIT/SETBIT 操作,使用一个偏移值 offset 对 bit 数组的某一个 bit 位进行读和写。不过,需要注意的是,Bitmap 的偏移量是从 0 开始算的,也就是说 offset 的最小值是 0。当使用 SETBIT 对一个 bit 位进行写操作时,这个 bit 位会被设置为 1。Bitmap 还提供了 BITCOUNT 操作,用来统计这个 bit 数组中所有“1”的个数。

    setbit key offset value 其中offset ....从0开始。 如: SETBIT uid:sign:3000:202008 2 1

    Bitmap 支持用 BITOP 命令对多个 Bitmap 按位做“与”“或”“异或”的操作,操作的结果会保存到一个新的 Bitmap 中。

    在实际应用时,最好对 Bitmap 设置过期时间,让 Redis 自动删除不再需要的签到记录,以节省内存开销。

    基数统计

    基数统计。基数统计就是指统计一个集合中不重复的元素个数。

    在 Redis 的集合类型中,Set 类型默认支持去重,所以看到有去重需求时,我们可能第一时间就会想到用 Set 类型。

    对于一个搞大促的电商网站而言,这样的页面可能有成千上万个,如果每个页面都用这样的一个 Set,就会消耗很大的内存空间。

    用 Hash 类型记录:例如,你可以把用户 ID 作为 Hash 集合的 key,当用户访问页面时,就用 HSET 命令(用于设置 Hash 集合元素的值),对这个用户 ID 记录一个值“1”,表示一个独立访客,用户 1 访问 page1 后,我们就记录为 1 个独立访客,如下所示:

    HSET page1:uv user1 1

    即使用户 1 多次访问页面,重复执行这个 HSET 命令,也只会把 user1 的值设置为 1,仍然只记为 1 个独立访客。当要统计 UV 时,我们可以用 HLEN 命令统计 Hash 集合中的所有元素个数。但是,和 Set 类型相似,当页面很多时,Hash 类型也会消耗很大的内存空间。

    HyperLogLog是一种用于统计基数的数据集合类型,它的最大优势就在于,当集合元素数量非常多时,它计算基数所需的空间总是固定的,而且还很小。

    HyperLogLog 专门用于基数统计,对于当前没有的值会返回 true,已有则会返回false

    PFADD page1:uv user1

    (integer) true

    PFADD page1:uv user1

    (integer) false

    计数结果为1:

    PFCOUNT page1:uv

    PFADD page1:uv user1

    (integer) true

    PFADD page1:uv user1

    (integer) 1

    HyperLogLog 的统计规则是基于概率完成的,所以它给出的统计结果是有一定误差的,标准误算率是 0.81%。这也就意味着,你使用 HyperLogLog 统计的 UV 是 100 万,但实际的 UV 可能是 101 万。虽然误差率不算大,但是,如果你需要精确统计结果的话,最好还是继续用 Set 或 Hash 类型。

    面向 位置信息服务(Location-Based Service,LBS) 应用的 GEO 数据类型

     GEO类型的底层数据结构使用Sorted Set来实现的。

    GeoHash 的编码方法

    当我们要对一组经纬度进行 GeoHash 编码时,我们要先对经度和纬度分别编码,然后再把经纬度各自的编码组合成一个最终编码

    对于一个地理位置信息来说,它的经度范围是[-180,180]。GeoHash 编码会把一个经度值编码成一个 N 位的二进制值,我们来对经度范围[-180,180]做 N 次的二分区操作,其中 N 可以自定义。

    在进行第一次二分区时,经度范围[-180,180]会被分成两个子区间:[-180,0) 和[0,180](我称之为左、右分区)。此时,我们可以查看一下要编码的经度值落在了左分区还是右分区。如果是落在左分区,我们就用 0 表示;如果落在右分区,就用 1 表示。这样一来,每做完一次二分区,我们就可以得到 1 位编码值。然后,我们再对经度值所属的分区再做一次二分区,同时再次查看经度值落在了二分区后的左分区还是右分区,按照刚才的规则再做 1 位编码。当做完 N 次的二分区后,经度值就可以用一个 N bit 的数来表示了。

    举个例子,假设我们要编码的经度值是 116.37,我们用 5 位编码值(也就是 N=5,做 5 次分区)。得到了经度值的 5 位编码值,即 11010。

     对纬度的编码方式,和对经度的一样,只是纬度的范围是[-90,90],下面这张表显示了对纬度值 39.86 的编码过程。

     当一组经纬度值都编完码后,我们再把它们的各自编码值组合在一起,组合的规则是:最终编码值的偶数位上依次是经度的编码值,奇数位上依次是纬度的编码值,其中,偶数位从 0 开始,奇数位从 1 开始。

    我们刚刚计算的经纬度(116.37,39.86)的各自编码值是 11010 和 10111,得到最终编码值 1110011101,如下图所示:

     用了 GeoHash 编码后,原来无法用一个权重分数表示的一组经纬度(116.37,39.86)就可以用 1110011101 这一个值来表示,就可以保存为 Sorted Set 的权重分数了。

    到这里,我们就知道了,GEO 类型是把经纬度所在的区间编码作为 Sorted Set 中元素的权重分数,把和经纬度相关的车辆 ID 作为 Sorted Set 中元素本身的值保存下来,这样相邻经纬度的查询就可以通过编码值的大小范围查询来实现了。

    如何操作 GEO 类型?

    GEOADD 命令:用于把一组经纬度信息和相对应的一个 ID 记录到 GEO 类型集合中;

    GEORADIUS 命令:会根据输入的经纬度位置,查找以这个经纬度为中心的一定范围内的其他元素。当然,我们可以自己定义这个范围。

    时间序列数据

    这种数据的写入特点很简单,就是插入数据快,这就要求我们选择的数据类型,在进行数据插入时,复杂度要低,尽量不要阻塞。

    为了避免客户端和 Redis 实例间频繁的大量数据传输,我们可以使用 RedisTimeSeries 来保存时间序列数据。

    基于 RedisTimeSeries 模块保存时间序列数据

    RedisTimeSeries 是 Redis 的一个扩展模块。它专门面向时间序列数据提供了数据类型和访问接口,并且支持在 Redis 实例上直接对数据进行按时间范围的聚合计算。

    因为 RedisTimeSeries 不属于 Redis 的内建功能模块,在使用时,我们需要先把它的源码单独编译成动态链接库 redistimeseries.so,再使用 loadmodule 命令进行加载,如下所示:

      loadmodule redistimeseries.so

    当用于时间序列数据存取时,RedisTimeSeries 的操作主要有 5 个:

    用 TS.CREATE 命令创建时间序列数据集合;

    用 TS.ADD 命令插入数据;

    用 TS.GET 命令读取最新数据;

    用 TS.MGET 命令按标签过滤查询数据集合;

    用 TS.RANGE 支持聚合计算的范围查询。

  • 相关阅读:
    [LeetCode] 824. Goat Latin
    [LeetCode] 21. Merge Two Sorted Lists
    [LeetCode] 1021. Remove Outermost Parentheses
    [Note] Clipboard.js 使用
    [LeetCode] 665. Non-decreasing Array
    [LeetCode] 897. Increasing Order Search Tree
    [LeetCode] 136. Single Number
    [LeetCode] 822. Card Flipping Game
    [LeetCode] 922. Sort Array By Parity II
    [LeetCode] 965. Univalued Binary Tree
  • 原文地址:https://www.cnblogs.com/520lq/p/14654500.html
Copyright © 2011-2022 走看看