string
类似于java中ArrayList的结构,为了对内存做极致的优化,不同长度的字符串用不同的结构体来表示。
SDS和C语言中字符串的区别:
- 多了一个len表示当前字符串的长度;
- 可以自动扩展空间,当对字符串进行操作时,可以通过len和malloc检查当前字符串的空间是否能满足本次操作的需求,如果不能满足就会自动扩容;
- 有效降低了内存的分配次数,当扩展空间时,会按照需求2倍的扩容,当缩容时,并不会立即将空间还给操作系统;
- 采用的是二进制数据格式,输入什么就存储什么,不做任何的过滤和限制,任何类型都能存储。
适用场景:
- 缓存功能:利用Redis做缓存,配合其他数据库做存储层,利用Redis高并发的特点,可以快速提高数据的读写能力,降低后台数据库的压力;
- 计数器:许多系统都用Redis来做计数器,以快速实现计数和更新功能,而且最终的数据结果还可以以特定的数值保存在数据库或者其他存储介质中永久的保存起来;
- 共享用户session:用户每次更新下页面,可能就需要访问数据来重新登录,或者访问用户缓存的cookie,可以利用Redis来将用户的session统一集中管理,这样就可以使用户session的查询和更新快速实现,提高响应速度。
list
类似于java中LinkedList结构
适用场景:
- 可以实现消息队列;
- 文章列表或者数据分页展示
hash
类似于Map的一种结构,也是通过数组+链表解决哈希冲突的,可以将结构化数据缓存在Redis中,当对缓存数据操作时,可以对hash的字段进行操作。
扩容缩容的时机:
- 一般当元素个数达到一维数组的容量时就会扩容,如果此时正在持久化,就先不扩容,但是如果元素个数超过了一维数组的5倍,就会强制扩容;
- 缩容并不会考虑是否正在持久化,如果元素个数少于一维数组容量的10%,就会缩容。
渐进式哈希:
里面有两个哈希表,在扩容时采用渐进式哈希,因为Redis是单线程的,可能元素个数也比较多,所以在扩容时,循序渐进的移动元素,如果有查询元素的操作就会在两张哈希表中查找,等扩容完后,再用新的哈希表代替旧的哈希表。
set
没有重复元素的结构,是无序的。
zset
也是没有重复元素的结构,但是是有序的,在插入元素时,会根据score自动排序。
底层数据结构:
跳跃表。
- 因为可能需要频繁的插入和删除元素,数组不适合,所有就用的是链表,在单链表上又进行了改进。
- 因为是有序的,所以在插入元素的时候需要找到合适的位置,如果是单链表的话那时间复杂度就是O(N),肯定不能满足需求,如果每隔两个节点就建立一个指针,指针又可以将这些节点组成一个新的单链表,在查找时先在新的链表中查找,如果遇到比要查找的节点大的节点就回到原来的表查找,类似于二分查找,时间复杂度可以达到O(logN)。
- 但是如果严格的在两个节点之间建立一个指针,那频繁的插入和删除还需要不断的调整,就会耗费大量时间,所以就又进行了改进,每个节点的层数是一个随机值,当插入和删除时只需要修改前后节点的指针就好了。
适用场景:
- 比如排行榜,热搜就可以用zset来实现;
- 带权重的队列。