一、常用数据结构

121 阅读3分钟

常见数据类型

  • String(字符串):缓存对象、常规计数、分布式锁、共享 session 信息等
  • Hash(哈希):缓存对象、购物车等
  • List(列表):消息队列(需要自己实现全局唯一ID,不能实现消费者组消费)
  • Set(集合):聚合计算(并集、交集、差集)场景,像点赞、共同关注、抽奖活动等数据
  • Zset(有序集合):排序场景:排行榜、电话、姓名排序等
  • BitMap:二值状态统计,比如签到、判断用户登陆状态、连续签到用户总数等
  • HyperLogLog:海量数据基数统计的场景,比如百万级网页UV计数等
  • GEO:储存地理位置信息的场景,类似于滴滴叫车
  • Stream:消息队列,与List实现的消息队列相比,这个可以自动生成全局唯一消息ID、支持以消费者组的形式消费数据。

常见数据类型底层实现

字符串(String)

字符串的底层实现主要是SDS,SDS是一个简单动态字符串。

Redis没有使用c语言的字符串,而是使用了SDS。

SDS具有c语言字符串没有的特性:

  • sds有一个len参数,实现O(1)时间内查询到字符串的长度;而c语言字符串没有存储字符串的长度,而是使用strlen()函数遍历整个字符串,直到遍历到空字符串为止,时间复杂度是o(n);
  • 在sds中,使用len来判断空字符串而不是“\0”,确保了二进制安全。因为redis也会保存二进制文件,如果二进制文件中本身就存在 "\0",那么字符串就会被截断,非二进制安全。
  • c语言自带的字符串存在缓存溢出的问题,因为c语言字符串不记录自身的长度,在执行strcat的时候,可能会产生缓存区溢出,此时数组a装不下的内容就会溢出到其他数组中,导致其他数组内容被修改。而在redis sds 提供的所有修改字符串的API中,都会判断修改字符串后会不会有内存溢出,并自动处理内存扩容等操作。不需要开发人员来判断+扩容
  • 在c语言中,对于一个长度为N的字符串,底层实现总是一个N+1长度的数组,所以每一次对字符串的增长或者缩短,都需要进行一次内存重分配操作。比如增长操作需要提前进行内存分配,拓展底层数组的空间大小;缩短操作需要释放掉不再使用的那部分空间。内存重分配是一个比较耗时的操作。在redis中,字符串的修改比较频繁,因此不适合使用c的原生字符串。sds中有冗余空间,只要追加内容不大,都不需要重新分配内存。(空间预分配,惰性删除

因此,对于Redis来说,c语言的原生字符串并不是一个好的选择。因此Redis重新设计了一个SDS。

Redis 数据结构之简单动态字符串(SDS) - 掘金

链表(List)

哈希(Hash)