1. 什么是redis?
redis的全称是REmote DIctionary Service,翻译过来就是远程词典服务
2. SQL与NOSQL
sql(关系型数据库):
- 以表格的形式存储行数据
- 存储格式需要与表结构相同
- 表之间存在关联
- 支持关联查询
- 事务来保证数据一致性
但是在数据量很大时,修改表结构非常困难。关系型数据库需要将数据持久化到磁盘,频繁IO操作在高并发场景下读写压力大,由此引出NOSQL
nosql(非关系型数据库):
- 存储非机构化数据
- 表之间没有关联
- 支持海量存储和高并发读写
redis就是nosql的一种
3. redis特性
要了解redis特性,我们通过几个问题来寻找答案
- 为什么要把数据放到内存中?
1)因为内存中查询速度更快 10w QPS
2)减少数据库压力 - 如果是用内存作为缓存,为什么不会HashMap或者Memcached?
1)Memcache 只有简单的KV存储结构,redis有更丰富的数据类型
2)支持多种编程语言
3)功能丰富:持久化机制、内存淘汰机制、事务、发布订阅、pipeline、lua脚本
4)支持集群
4. redis存储结构
Redis 内部使用一个 redisObject 对象来表示所有的 key 和 value。
-
type :代表一个 value 对象具体是何种数据类型。
-
encoding :是不同数据类型在 redis 内部的存储方式,比如:type=string 代表 value 存储的是一个普通字符串,那么对应的 encoding 可以是 raw 或者是 int,如果是 int 则代表实际 redis 内部是按数值型类存储和表示这个字符串的,当然前提是这个字符串本身可以用数值表示,比如:“123” "456"这样的字符串。
-
vm 字段:只有打开了 Redis 的虚拟内存功能,此字段才会真正的分配内存,该功能默认是关闭状态的。 Redis 使用 redisObject 来表示所有的 key/value 数据是比较浪费内存的,当然这些内存管理成本的付出主要也是为了给 Redis 不同数据类型提供一个统一的管理接口,实际作者也提供了多种方法帮助我们尽量节省内存使用。
5. redis支持的数据类型
String、Hash、Set、ZSet、List、HyperLogLog(基数统计)、Geo(地理位置)、Streams
操作可以在redis官网查看http://www.redis.cn/commands.html
5.1. String
5.1.1. String Encoding
String可以用来存INT、float(单精度浮点数)、String(字符串)
字符串所使用的encoding一共有3种
int,存储8个字节的长整型(2^63-1)
embstr,存储小于44字节的字符串,在redis3.0和3.2版本之间,将39个字节改为44个字节
raw,存储大于44字节的字符串
类型转换
- int不再是正数 >>> raw
- int超过long的范围(2^63-1) >>> embstr
- embstr长度超过44 >>> raw
- 修改来embstr,就会编程raw,因为embstr是不可修改的
- 转换过程不可逆
5.1.2. 应用场景
缓存热点数据、报表数据等、分布式Session、使用setnx实现分布式锁、incrby实现全局id、计数器、限流,通过incr递增,超过次数返回false
5.2 Hash
- Hash用来存储多个无序的键值对,最大存储数量为2^32-1
- Hash本身是一个KV存储结构
- 由最低层到最高层,是数组+链表的结构
简单理解,不需要背。Hash和java中hashmap一样可以扩容,也可以缩容
Hash底层可以使用两种数据结构实现
ziplist:OBJ_ENCODING_ZIPLIST(压缩列表)
hashtable:OBJ_ENCODING_HT(哈希表)
结构
ziplist是由连续内存块组成的双向链表,它不存在指向上一个/下一个节点的指针,而是存储上一个节点长度和当前节点的长度
5.2.1 Encoding
ZIP_STR_06B(0<<6) //长度小于等于63字节
ZIP_STR_14B(1<<6) //长度小于等于16383字节
ZIP_STR_32B(2<<6) //长度小于等于4294967295字节
满足以下两个条件时使用ziplist存储
- 哈希对象保存的键值对数量<512个
- 所有的键值对的键和值字符串长度都<64byte
参数通过redis.conf可修改,如果任何一个不满足,则结构会转换成hashtable
5.2.2. 应用场景
可以用String的地方都可以使用Hash。Hash可以更直观的存储对象
5.3 List
存储字符串,从左到右。元素可以重复,最大存储数量为2^32-1
List底层可以有3数据结构实现。早期版本中,数据量小时用ziplist,到达临界值时转换为linkedlist进行存储。3.2版本之后,统一使用quickList存储。
- linkedlist(双端链表)节点带有prev、next指针、head指针和tail指针,获取前置节点、后置节点、表头节点和表尾节点的复杂度都是O(1)。len属性获取节点数量也为O(1)。与双端链表相比,压缩列表可以节省内存空间,但是进行修改或增删操作时,复杂度较高;因此当节点数量较少时,可以使用压缩列表;但是节点数量多时,还是使用双端链表划算。
- ziplist(压缩列表)
当一个列表键只包含少量列表项,且是小整数值或长度比较短的字符串时,那么redis
就使用ziplist(压缩列表)来做列表键的底层实现。ziplist是Redis为了节约内存而开发的,是由一系列特殊编码的连续内存块(而不是像双端链表一样每个节点是指针)组成的顺序型数据结构;具体结构相对比较复杂- quickList 它是 zipList 和 linkedList 的混合体。quickList存储了一个双向链表,每一个节点都是一个ziplist
quicklist 默认的压缩深度是 0,也就是不压缩。为了支持快速的 push/pop 操作,quicklist 的首尾两个 ziplist 不压缩,此时深度就是 1。为了进一步节约空间,Redis 还会对 ziplist 进行压缩存储,使用 LZF 算法压缩。
5.3.1. 应用场景
- 存储有序内容的场景,存储数据列表
- 队列/栈,使用阻塞弹出,blpop,rpush
5.4 Set
Set存储String类型的无序集合,最大存储数量2^32-1
5.4.1. 存储
- redis使用intset或hashtable存储set,如果元素都是整型,就用intset。否则用hashtable,如果元素超过512个,也会变成hashtable
- 可通过redis.conf修改
- 数据存储在key中,value存null
5.4.2. 应用场景
抽奖,通过spop随机取数;差集、交集、并集
5.5 ZSet 有序集合
sorted set存储有序的元素。每个元素有个score,安装score从小到大排名。score相同时,按照key的ASCII码排序。
5.5.1. 存储
默认使用ziplist,如果元素数量大于等于128个,或任意长度大于等于64byte时,使用skiplist+dict存储
5.5.2. skiplist跳表
比如现在有个list,3->7->11->19->22。如果现在要插入一条数据,只能进行遍历然后插入。假设我们每相邻的两个节点中的一个节点进行改变,该节点多一个指针指向下下个节点,这样新的链表节点就只有原来的一半。这样查找时,就可以根据新链表去查,当查找到比当前数大时,再查询小节点。这就是跳跃表
其余类型不常用,就不说了