Redis-五种数据类型

Redis基本特性

a) 非关系型的键值对数据库,可以根据键以O(1) 的时间复杂度取出或插入关联值

b) Redis 的数据是存在内存中的

c) 键值对中键的类型可以是字符串,整型,浮点型等,且键是唯一的

d) 键值对中的值类型可以是string,hash,list,set,sorted set 等

e) Redis 内置了复制,磁盘持久化,LUA脚本,事务,SSL, ACLs,客户端缓存,客户端代理等功能(6.0新特性)

f) 通过Redis 哨兵和Redis Cluster 模式提供高可用性

Redis应用场景

a) 计数器 可以对 String 进行自增自减运算,从而实现计数器功能。Redis 这种内存型数据库的读写性能非常高,很适合存储频繁读写的计数量。

b) 分布式ID生成 利用自增特性,一次请求一个大一点的步长如 incr 2000 ,缓存在本地使用,用完再请求。

c) 海量数据统计 位图(bitmap):存储是否参过某次活动,是否已读谋篇文章,用户是否为会员, 日活统计。

d) 会话缓存 可以使用 Redis 来统一存储多台应用服务器的会话信息。当应用服务器不再存储用户的会话信息,也就不再具有状态,一个用户可以请求任意一个应用服务器,从而更容易实现高可用性以及可伸缩性。

e) 分布式队列/阻塞队列 List 是一个双向链表,可以通过 lpush/rpushrpop/lpop 写入和读取消息。可以通过使用brpop/blpop 来实现阻塞队列。

f) 分布式锁实现 在分布式场景下,无法使用基于进程的锁来对多个节点上的进程进行同步。可以使用 Redis 自带的 SETNX 命令实现分布式锁。

g) 热点数据存储 最新评论,最新文章列表,使用list 存储,ltrim取出热点数据,删除老数据。

h) 社交类需求 Set 可以实现交集,从而实现共同好友等功能,Set通过求差集,可以进行好友推荐,文章推荐。

i) 排行榜 sorted_set可以实现有序性操作,从而实现排行榜等功能。

j) 延迟队列 使用sorted_set,使用 【当前时间戳 + 需要延迟的时长】做score, 消息内容作为元素,调用zadd来生产消息,消费者使用zrangbyscore获取当前时间之前的数据做轮询处理。消费完再删除任务 rem key member

Redis 的key 都是字符串(SDS, simple dynamic string)类型,命令由客户端发送给服务端都会转换为字节流的形式,虽然看起来可能是数字、浮点数、或者字符串多种类型

> set 1 a // 数字key
> get 1

> set 0.5 b // 浮点key
> get 0.5

> set a c // 字符串key
> get a

以上key 发送到服务端最终都是以字节流的形式

SDS 的特点:

a)二进制安全的数据结构,安全体现在不会丢数据(c:char data[]="g\0ao",C 语言以"\0" 作为字符串结尾标记,业务数据存在"\0"时存在安全问题)

b)内存预分配机制,防止频繁扩容产生的内存分配问题

c)兼容C语言的函数库

扩容示例:

redis:
  sds:
    free: 0
    len: 3
    char buf[]="gao" -> "gao123"
    
    len: 3
    addlen: 3
    (len + addlen) * 2 = 12  //扩容,(现有长度+需要增加长度)*2
    // 成倍扩容,当长度len=1024,再扩容每次增加1M,(所以使用 setbit 设置大点的空间,防止频繁扩容)
    
  append,setbit 
    sds:
      free: 6
      len: 6
      char buf[]="gao123"  

设计思想:

K-V:map -> dict,
数据库:海量数据的存储
1、数组:O(1) 随机访问,下表
2、链表:O(n) 头结点,遍历
3、数:log(n) 优化的比较好的场景,二分查找
    
hash(key) 任意数据进行随机散列,并且把hash 值转换为一个自然数[大]
创建一个大的数组:arr[4]
hash(key) -> 自然数[大] % 4 = [0, arr.size-1], redis 就是这么干的
1、任意相同的输入,一定能得到相同的输出
2、不同的输入,可能得到相同的输出
把现实中无限的数据放到有限的集合中,肯定会产生hash 冲突    
(k1,v1),(k2,v2),(k3,v3)
    
hash(k1) % 4 = 0
hash(k2) % 4 = 1
    
arr[0]->(k1,v1,next->null)
arr[1]->(k3,v3,next->k2)(k2,v2,next->null)
产生hash 冲突时,redis 使用的是头插法
redis 是比较基础的语言,没有那么多高级特性,没有那么多工具可以用,是redis 作者自己实现的
通过链表法来解决碰撞,java 中的hashMap 要复杂得多

key:string
value:string,hash,list,set,sorted set

Redis之String 类型数据结构直通车

Redis6.0 多线程,但是最终执行用户请求是在单线程中进行的,渐进式扩容很有必要

有趣命令:

> type [key] //查看key的类型
string
> object encoding 100 //key 所对应的值,在redis 底层是一个什么样的编码格式
"int"
> object encoding str
"embstr"
> object encoding 0.1
"int"
    
int / embstr 都是redis 对内存方面的优化

Redis求模小优化:

任意数 % 2^n //对CPU 不友好,累除法
任意数 & (2^n - 1) //一次位运算

Redis 对于值是string 类型的底层编码结构分析:

//type [key],查看key 的类型
//object encoding [key],查值在底层的编码格式

//redis 中所有对象的封装
typedef struct redisObject {    
  unsigned type:4; //约束客户端命令,当前类型。位域的语法,占4个bit 位
  unsigned encoding:4; //编码格式,同样占4个bit 位
  unsigned lru:LRU_BITS; //24个bit 位,也就是3 个字节
  int refcount; //引用计数法,4个字节
  void *ptr; //数据真正存放的位置,8个字节
} robj;


redisObject 占16个字节的空间
cache line:64字节,64 - 16 = 48 字节,肯定可以利用起来
48个字节使用sdshdr8的数据结构存储,因为48在数据范围 2^5 ~ 2^8-1 之间

struct __attribute__ ((__packed__)) sdshdr8 {  // 表示 2^5 ~ 2^8-1 的数据    
    uint8_t len; /* used */    
    uint8_t alloc; /* excluding the header and null terminator */    
    unsigned char flags; /* 3 lsb of type, 5 unused bits */    
    char buf[];
};

sds本身消耗4个字节,其中len、alloc、flags各占一个字节,另外'\0'占一个字节,因为兼容C语言的函数库
最终:48 - 4 = 44 字节,用来存放实际的数据

对于string 类型的value 有结论如下:

value 值是string 类型的底层编码结构:
1、value值的长度小于等于20,尝试转换成数字转换成功,底层使用int 编码格式
2、value值不满足条件1,且长度小于等于44redis 底层使用embstr 编码结构
3、value值不满足1和2条件,使用row 编码结构也就是sds 的数据结构,简单动态字符串

扩展分析:

亿级日活的统计:

bit 的特点,除了0 就是1,所以可以用1个bit 位表示登陆状态
像这样1个字节就可以表示8个用户

       1 1 0 1 0 1 0 1 1 1 0 1 0 0 1 0 1   -- 登陆状态
offset:0 1 2 3 4 5 6 7 8 9 ...

命令:setbit key offset 0|1   
日期可以作为一个key 使用,“2022-5-26”

变种:userId 是数字时,可以作为偏移量使用
setbit a-bit-map userId 1
setbit a-bit-map userId 1

getbit a-bit-map 8
userId:
bit: 0/1 (默认值是0)

简单测试:

> setbit login_05_26 100 1
> setbit login_05_26 100 1
> setbit login_05_26 100 1

> getbit login_05_26 100  //获取日期5月26日用户100的活跃状态,id特别大可以减固定值优化

> setbit login_05_26 100 0 //恢复状态
> getbit login_05_26 100

> STRLEN login_05_26 //长度13 字节。100/8bit = 12.5 所以分配13个字节
> type login_05_26 //string 类型的数据
> get login_05_26 //可以执行命令,获取的数据是内存中的存储,没有实际意义


> setbit login_05_26 100 1
> setbit login_05_26 101 1
> setbit login_05_26 102 1
> setbit login_05_26 103 1

> BITCOUNT login_05_26 //统计bit位是1的个数:4
> strlen login_05_26 //13个字节
> BITCOUNT login_05_26 0 12 //一共13个字节,从索引0开始,所以是0和12。可以统计一部分数据根据索引

string 表示数据最大是512M,索引位是2^32-1。最多可表示这么多的用户

另一个扩展分析:

login_05_26: 0 1 0 1 0 1 1 1 0 1 0 1 1 0 0 1 1
login_05_27: 1 1 0 1 0 1 0 1 1 1 0 1 0 0 1 0 1
连续登陆情况:按位与再统计
> BITOP and login_05_26-27 login_05_26 login_05_27 //按位与操作,结果存到login_05_26-27中
> BITCOUNT login_05_26-27 //再做一次bitcount可以得到连续登陆的结果

login_05_24: 0 1 0 1 0 1 1 1 0 1 0 1 1 0 0 1 1
login_05_25: 1 1 0 1 0 1 0 1 1 1 0 1 0 0 1 0 1
login_05_26: 0 1 0 1 0 1 1 1 0 1 0 1 1 0 0 1 1
login_05_27: 1 1 0 1 0 1 0 1 1 1 0 1 0 0 1 0 1
login_05_28: 0 1 0 1 0 1 1 1 0 1 0 1 1 0 0 1 1
login_05_29: 1 1 0 1 0 1 0 1 1 1 0 1 0 0 1 0 1
login_05_30: 0 1 0 1 0 1 1 1 0 1 0 1 1 0 0 1 1
周活:按位或,有一天登陆就行
> BITOP or login_05_26-27-active login_05_26 login_05_27 //两天内有一天登陆就行
> BITCOUNT login_05_26-27-active
    
redis 源码有判断只能是0或1,按位与 按位或,效率非常高
汉明重量 按位操作 有兴趣的同学可以了解

List常用API

/> help @list

LPUSH key element [element ...]
RPOP key
RPUSH key element [element ...]
LPOP key
BLPOP key [key ...] timeout
BRPOP key [key ...] timeout
BRPOPLPUSH source destination timeout
RPOPLPUSH  source destination
LINDEX key index
LLEN key
LINSERT key BEFORE|AFTER pivot element
LRANGE key start stop
LREM key count element
LSET key index element
LTRIM key start stop 

List是一个有序(按加入的时序排序)的数据结构,Redis采用quicklist(双端链表) 和 ziplist 作为List的底层实现。

可以通过设置每个ziplist的最大容量,quicklist的数据压缩范围,提升数据存取效率

list-max-ziplist-size  -2        //  单个ziplist节点最大能存储  8kb  ,超过则进行分裂,将数据存储在新的ziplist节点中
list-compress-depth  1        //  0 代表所有节点,都不进行压缩,1, 代表从头节点往后走一个,尾节点往前走一个不用压缩,其他的全部压缩,2,3,4 ... 以此类推

Redis - quicklist 数据结构:

Redis - ziplist 数据结构:

Hash常用API

/> help  @hash 

HSET key field value [field value ...]
HGET key field
HMGET key field [field ...]
HKEYS key
HGETALL key
HVALS key
HEXISTS key field
HDEL key field [field ...]
HINCRBY key field increment
HINCRBYFLOAT key field increment
HLEN key
HSCAN key cursor [MATCH pattern] [COUNT count]
HSETNX key field value
HSTRLEN key field 

Hash 数据结构底层实现为一个字典( dict ),也是RedisBb用来存储K-V的数据结构,当数据量比较小,或者单个元素比较小时,底层用ziplist存储,数据大小和元素数量阈值可以通过如下参数设置。

hash-max-ziplist-entries  512    //  ziplist 元素个数超过 512 ,将改为hashtable编码 
hash-max-ziplist-value    64      //  单个元素大小超过 64 byte时,将改为hashtable编码

Redis - hash 数据结构:

Set常用API

/> help  @set

SADD key member [member ...]              
SCARD key                                                 
SISMEMBER key member
SPOP key [count]
SDIFF key [key ...]
SINTER key [key ...]
SUNION key [key ...]
SMEMBERS key
SRANDMEMBER key [count]
SREM key member [member ...]
SMOVE source destination member
SUNIONSTORE destination key [key ...]
SDIFFSTORE destination key [key ...]
SINTERSTORE destination key [key ...]
SSCAN key cursor [MATCH pattern] [COUNT count] 

Set 为无序的,自动去重的集合数据类型,Set 数据结构底层实现为一个value 为 null 的 字典( dict ),当数据可以用整形表示时,Set集合将被编码为intset数据结构。两个条件任意满足时 Set将用hashtable存储数据。1, 元素个数大于 set-max-intset-entries , 2 , 元素无法用整形表示

set-max-intset-entries 512       // intset 能存储的最大元素个数,超过则用hashtable编码

Redis - set 数据结构:

ZSet常用API

/> help  @sorted_set

ZADD key [NX|XX] [CH] [INCR] score member [score member ...]
ZCARD key
ZCOUNT key min max
ZINCRBY key increment member
ZRANGE key start stop [WITHSCORES]
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
ZRANK key member
ZREM key member [member ...]
ZREMRANGEBYRANK key start stop
ZREMRANGEBYSCORE key min max
ZREVRANGE key start stop [WITHSCORES]
ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]
ZREVRANK key member
ZSCAN key cursor [MATCH pattern] [COUNT count]
ZSCORE key member

ZSet 为有序的,自动去重的集合数据类型,ZSet 数据结构底层实现为 字典(dict) + 跳表(skiplist) ,当数据比较少时,用ziplist编码结构存储。

zset-max-ziplist-entries  128    // 元素个数超过128 ,将用skiplist编码
zset-max-ziplist-value     64     //  单个元素大小超过 64 byte, 将用 skiplist编码

Redis - zset 数据结构:

Redis - skiplist 数据结构:

你可能感兴趣的:(redis,java)