常见数据类型
- 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。