更多精彩文章,关注公众号【ToBeTopJavaer】,更有数万元精品vip资源免费等你来拿!!!
整体来讲:String、Hash、Set、List、Zset、Hyperloglog、Geo、Streamjava
经常使用的数据类型:redis
最基本最经常使用的数据类型就是String。set 和 get 命令就是 String 的操做命令。spring
一、 String字符串数据库
能够用来存储字符串,整数,浮点数。数组
1.一、 基本操做缓存
设置多个值(批量操做,原子性)安全
设置值,如何Key存在,设置不成功session
基于此可实现分布式锁。 用 del key 释放锁。数据结构
但若是释放锁的操做失败了, 致使其余节点永远获取不到锁, 怎么办?分布式
加过时时间。 单独用 expire 加过时, 也失败了, 没法保证原子性, 怎么办? 多参数。
set key value [expiration EX seconds|PX milliseconds][NX|XX]
(整数) 值递增
(整数) 值递减
浮点数增量
获取多个值
获取值长度
字符串追加内容
获取指定范围的字符
1.二、 存储实现原理
数据模型
set name javaHuang为例,由于 Redis 是 KV 的数据库,它是经过 hashtable 实现的(我
们把这个叫作外层的哈希)。因此每一个键值对都会有一个 dictEntry,里面指向了 key 和 value 的指针。next 指向下一个 dictEntry。源码以下:
typedef struct dictEntry { void *key; /* key 关键字定义 */ union { void *val; uint64_t u64; /* value 定义 */ int64_t s64; double d; } v; struct dictEntry *next; /* 指向下一个键值对节点 */ } dictEntry;
key 是字符串,可是 Redis 没有直接使用 C 的字符数组,而是存储在自定义的 SDS
中。
value 既不是直接做为字符串存储,也不是直接存储在 SDS 中,而是存储在
redisObject 中。实际上五种经常使用的数据类型的任何一种,都是经过 redisObject 来存储
的。
redisObject
redisObject 的定义的源码以下:
typedef struct redisObject { unsigned type:4; /* 对象的类型, 包括: OBJ_STRING、 OBJ_LIST、 OBJ_HASH、 OBJ_SET、 OBJ_ZSET */ unsigned encoding:4; /* 具体的数据结构 */ unsigned lru:LRU_BITS; /* 24 位, 对象最后一次被命令程序访问的时间, 与内存回收有关 */ int refcount; /* 引用计数。 当 refcount 为 0 的时候, 表示该对象已经不被任何对象引用, 则能够进行垃圾回收了 */ void *ptr; /* 指向对象实际的数据结构 */ } robj;
可使用 type 命令来查看对外的类型。
下面我来设置三种Key
而后咱们获取这三种Key的内部编码
因而咱们可知,字符串类型的内部编码有三种:
一、int,存储 8 个字节的长整型(long,2^63-1)。
二、embstr, 表明 embstr 格式的 SDS(Simple Dynamic String 简单动态字符串),
存储小于 44 个字节的字符串。
三、raw,存储大于 44 个字节的字符串(3.2 版本以前是 39 字节)。
/* object.c */ #define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44
2.一、什么是SDS
SDS是Redis 中字符串的实现。
在 3.2 之后的版本中,SDS 又有多种结构(sds.h):sdshdr五、sdshdr八、sdshdr1六、
sdshdr3二、sdshdr64,用于存储不一样的长度的字符串,分别表明 2^5=32byte,
2^8=256byte,2^16=65536byte=64KB,2^32byte=4GB。
struct __attribute__ ((__packed__)) sdshdr8 { uint8_t len; /* 当前字符数组的长度 */ uint8_t alloc; /*当前字符数组总共分配的内存大小 */ unsigned char flags; /* 当前字符数组的属性、 用来标识究竟是 sdshdr8 仍是 sdshdr16 等 */ char buf[]; /* 字符串真正的值 */ };
2.二、为何 Redis 要用 SDS 实现字符串?
咱们知道,C 语言自己没有字符串类型(只能用字符数组 char[]实现)。
一、使用字符数组必须先给目标变量分配足够的空间,不然可能会溢出。
二、若是要获取字符长度,必须遍历字符数组,时间复杂度是 O(n)。
三、C 字符串长度的变动会对字符数组作内存重分配。
四、经过从字符串开始到结尾碰到的第一个'\0'来标记字符串的结束,所以不能保存图片、音频、视频、压缩文件等二进制(bytes)保存的内容,二进制不安全。
SDS 的特色:
一、不用担忧内存溢出问题,若是须要会对 SDS 进行扩容。
二、获取字符串长度时间复杂度为 O(1),由于定义了 len 属性。
三、经过“空间预分配”( sdsMakeRoomFor)和“惰性空间释放”,防止屡次重分配内存。
四、判断是否结束的标志是 len 属性(它一样以'\0'结尾是由于这样就可使用 C 语言中函数库操做字符串的函数了),能够包含'\0'。
2.三、embstr 和 raw 的区别
embstr 的使用只分配一次内存空间(由于 RedisObject 和 SDS 是连续的),而 raw须要分配两次内存空间(分别为 RedisObject 和 SDS 分配空间)。
所以与 raw 相比,embstr 的好处在于建立时少分配一次空间,删除时少释放一次空间,以及对象的全部数据连在一块儿,寻找方便。
而 embstr 的坏处也很明显,若是字符串的长度增长须要从新分配内存时,整个RedisObject 和 SDS 都须要从新分配空间,所以 Redis 中的 embstr 实现为只读。
2.四、int 和 embstr 何时转化为 raw
当 int 数 据 不 再 是 整 数 , 或 大 小 超 过 了 long 的 范 围(2^63-1=9223372036854775807)时,自动转化为 embstr。
2.五、明明没有超过阈值,为何变成 raw 了
对于 embstr,因为其实现是只读的,所以在对 embstr 对象进行修改时,都会先转化为 raw 再进行修改。
所以,只要是修改 embstr 对象,修改后的对象必定是 raw 的,不管是否达到了 44个字节。
2.六、当长度小于阈值时,会还原吗
关于 Redis 内部编码的转换,都符合如下规律:编码转换在 Redis 写入数据时完成,且转换过程不可逆,只能从小内存编码向大内存编码转换(可是不包括从新 set)。
2.七、为何要对底层的数据结构进行一层包装呢
经过封装,能够根据对象的类型动态地选择存储结构和可使用的命令,实现节省空间和优化查询速度。
缓存
String 类型
例如:热点数据缓存,对象缓存,全页缓存。能够提高热点数据的访问速度。
数据共享分布式
STRING 类型,由于 Redis 是分布式的独立服务,能够在多个应用之间共享,例如:分布式 Session
<dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency>
分布式锁
STRING 类型 setnx 方法,只有不存在时才能添加成功,返回 true。
简单代码以下:
public Boolean getLock(Object lockObject){ jedisUtil = getJedisConnetion(); boolean flag = jedisUtil.setNX(lockObj, 1); if(flag){ expire(locakObj,10); } return flag; } public void releaseLock(Object lockObject){ del(lockObj); }
全局 ID
INT 类型,INCRBY,利用原子性
incrby userid 1000
计数器
INT 类型,INCR 方法
例如:文章的阅读量,微博点赞数,容许必定的延迟,先写入 Redis 再定时同步到数据库。
限流
INT 类型,INCR 方法
以访问者的 IP 和其余信息做为 key,访问一次增长一次计数,超过次数则返回 false。
位统计
String 类型的 BITCOUNT(1.6.6 的 bitmap 数据结构介绍)。
字符是以 8 位二进制存储的。
set k1 a setbit k1 6 1 setbit k1 7 0 get k1
a 对应的 ASCII 码是 97,转换为二进制数据是 01100001
b 对应的 ASCII 码是 98,转换为二进制数据是 01100010
由于 bit 很是节省空间(1 MB=8388608 bit),能够用来作大数据量的统计。
例如:在线用户统计,留存用户统计
setbit onlineusers 0 1 setbit onlineusers 1 1 setbit onlineusers 2 0
支持按位与、按位或等等操做。
BITOP AND destkey key [key ...] , 对一个或多个 key 求逻辑并, 并将结果保存到 destkey 。 BITOP OR destkey key [key ...] , 对一个或多个 key 求逻辑或, 并将结果保存到 destkey 。 BITOP XOR destkey key [key ...] , 对一个或多个 key 求逻辑异或, 并将结果保存到 destkey 。 BITOP NOT destkey key , 对给定 key 求逻辑非, 并将结果保存到 destkey 。
例子:计算出 7 天都在线的用户
BITOP "AND" "7_days_both_online_users" "day_1_online_users" "day_2_online_users" ... "day_7_online_users"
基本数据类型String基本算是分析透了,接下来还有几个经常使用的基本类型的深刻探讨,敬请期待。
更多精彩文章,关注公众号【ToBeTopJavaer】,更有数万元精品vip资源免费等你来拿!!!