数据结构

发布时间 2023-12-19 10:22:42作者: 梅丹隆

相比于memcache 作为缓存服务,redis 提供了更为丰富的数据结构:String, List,Set,SortedSet,Hash等。
对于这五种数据结构,可以结合Java中的对应的类来进行理解,其中String数据结构对应Object类 (任意对象都会序列化成string来存储),List数据结构对应java.util.List接口的实现类java.util.LinkedList,Set数据结构对应java.util.Set接口,SortedSet数据结构对应java.util.SortedSet接口,Hash数据结构对应java.util.HashMap类。

1、String

1.1、使用场景

value较小、模型简单的 value可以使用String类型存储,对于一些特殊的数据结构,比如List、Set等,建议采用相应的下面介绍的List和Set数据结构进行存储,这样不仅可以节省存储空间还可以提高操作效率

1.2、特点

  • String类型是Redis中最为基础的数据存储类型,是二进制安全的字符串,该类型可以接受任何格式的数据
  • 在Redis中String类型的Value最多可以容纳的数据长度是512M,在squirrel-client中Value限制的大小是1M
  • 只有String类型的命令在写入key的时候可以带有默认的过期时间(在squirrel-client中,对于String类型的命令只有set,add和multiset命令会自动设置过期时间,且过期时间为使用category的过期时间),对于其他的数据结构,key默认是不过期的,如果需要设置过期时间,必须显示调用expire函数设置过期时间
  • 在squirrel-client中,所有的对象都会被序列化成String存到集群中,因此所有的数据都可以作为String类型来存储

2、List

2.1、使用场景

在评级系统中,比如社会化新闻网站,你可以把每个新提交的链接添加到一个list,用LRANGE可简单的对结果分页;在博客引擎实现中,你可为每篇日志设置一个list,在该list中推入进博客评论等等

2.2、特点

  • List类型是按照插入顺序排序的字符串链表。和数据结构中的普通链表一样,可以在其头部(left)和尾部(right)添加新的元素
  • 在插入时,如果该键并不存在,Redis将为该键创建一个新的链表。如果链表中所有的元素均被移除,那么该键也将会被从数据库中删除
  • 从元素插入和删除的效率视角来看,如果是在链表的两头插入或删除元素,这将会是非常高效的操作,即使链表中已经存储了百万条记录,该操作也可以在常量时间内完成。如果元素插入或删除操作是作用于链表中间,那将会是非常低效的

3、Set

3.1、使用场景

可以使用Redis的Set数据类型跟踪一些唯一性数据,比如访问某一博客的唯一IP地址信息。对于此场景,仅需在每次访问该博客时将访问者的IP存入Redis中,Set数据类型会自动保证IP地址的唯一性

3.2、特点

  • Set类型是没有排序的字符串集合,和List类型一样,也可以在该类型的数据值上执行添加、删除或判断某一元素是否存在等操作
  • Set集合中不允许出现重复的元素,如果多次添加相同元素,Set中将仅保留该元素的一份拷贝

4、SortedSet

4.1、使用场景

  1. 可以用于一个大型在线游戏的积分排行榜。每当玩家的分数发生变化时,可以执行ZADD命令更新玩家的分数,此后再通过ZRANGE命令获取积分TOP TEN的用户信息。当然也可以利用ZRANK命令通过username来获取玩家的排行信息。最后将组合使用ZRANGE和ZRANK命令快速的获取和某个玩家积分相近的其他用户的信息
  2. SortedSet类型还可用于构建索引数据
  3. 建立一个SortedSet中元素个数不要超过 1 W

4.2、特点

  • SortedSet和Set类型极为相似,它们都是字符串的集合,都不允许重复的成员出现在一个Set中
  • 主要差别是SortedSet中的每一个成员都会有一个分数(score)与之关联,Redis正是通过分数来为集合中的成员进行从小到大的排序
  • 尽管SortedSet中的成员必须是唯一的,但是分数(score)却是可以重复的
  • 在SortedSet中添加、删除或更新一个成员都是非常快速的操作,其时间复杂度为O(logn)
  • 由于SortedSet中的成员在集合中的位置是有序的,因此,即便是访问位于集合中部的成员也仍然是非常高效的

5、Hash

5.1、使用场景

  1. 对于海量数据的情况,可以自己对数据进行分桶,然后使用Hash结构来存储。对于很多value为简单的字符串,做过测试⬇️,采用hash存储更节省空间image.png
  2. 将对象存储为Hash结构而不是String,可以每次只更新、获取Hash中的一个field,这样可以提高效率
  3. User对象含有Username、Password和Age等属性,可以使用hash来存储User,每个field对应一个属性,好处是可以做到部分更新、获取

5.2、特点

  • Hash类型相当于Java中的HashMap。所以该类型非常适合于存储值对象的信息
  • 如果Hash中包含很少的字段,那么该类型的数据也将仅占用很少的磁盘空间

6、HyperLogLog

6.1、使用场景

  1. 可以用于统计一个网站的UV。利用HyperLogLog来统计访问一个网站的不同ip的个数

6.2、特点

  • HyperLogLog类型用来进行基数统计
  • 利用HyperLogLog,用户可以使用少量固定大小的内存,来统计集合中唯一元素的数量(每个HyperLogLog占用12KB内存,可以计算接近264个不同元素的基数)
  • 于HyperLogLog的原理,可以参见:神奇的HyperLogLog算法
  • 利用HyperLogLog得到的基数统计结果,不是精确值,而是一个带有0.81%标准差(standard error)的近似值。所以,HyperLogLog适用于一些对于统计结果精确度要求不是特别高的场景