Redis原理以及底层数据结构初探

什么是Redis?

  • 非关系型的键值对数据库,可以根据键以O(1)的时间复杂度取出或插入关联值
  • Redis的数据是存在内存中的
  • 键值对中键的类型可以是字符串,整型,浮点型等,且键是唯一的
  • 键值对中的值类型可以是sting, list, hash, set sorted set
  • Redis内置了复制,持久化,LUA脚本,事务, SSL, ACLS, 客户端缓存,客户端代理等功能。
  • 通过Redis哨兵和Redis Cluster模式提供高可用性。

存储海量数据结构

1. 链表, B+树,数组16个

数据库

object encoding some_int:可以查看redis底层数据结构。为int

raw string embstr(底层数据结构)

redis柔性策略设计原理

根据数据结构的长度来决定数据的范围。

String类型

 

sdshdr5

sdshdr8

sdshdr16

sdshdr32

sdshdr64

typedef char *sds
 struct_attribute_((__package)) sdshdr8{
 	uint8_t len;
 	uint8_alloc;
 	unsigned char flags;
 	char buff[];
 }

1 byte + 1byte + 1 byte + 1byte 

static inline char sdsReqType(size_t string_size){

if(string_size < 32)

    return SDS_TYPE_5

if(string_size<0xff) 2^8-1

    return SDS_TYPE_8

if(string_size<0xfff) 2^16-1

    return SDS_TYPE_16

if(string_size<0xffff) 2^32-1

    return SDS_TYPE_64

}

CPU缓存行通常是64个byte

 

String应用场景

(1) 单值缓存

set key value 

get key

(2)对象缓存

set user1: value(json格式数据)

mset user:1:name zhuge user:1:balance 1888

mget user1:name user:1:banlance

(3)分布式锁

setnx product:10001 true//返回1代表获取锁成功

setnx product: 10001 false//返回0代表获取锁失败

。。。执行业务操作

del product:10001 执行完业务释放锁

setnx product: 10001 true ex 10 nx//防止程序意外终止导致死锁

 

setnx key value

将key的值设为value,当且仅当key不存在

若给定的key以及存在,则setnx不做任何动作

(4) 计数器

incr article:readcount:{文章id}

get article:readcount:{文章id}

Web集群部署session共享

spring session + redis实现session共享

 

(5) 分布式系统全局序列号

incrby orderId 100(分库分表之后)redis批量生成序列号提升性能,以前生成100000条数据需要交互100000次,现在只需要交互1000次

Hash应用场景

双重map

对象缓存

idnamebalance
1aa100
2bb200

HMSET user {userId}:name aa {userId}:balance 100

HMSET user 1:name aa 1:balance 100

HMSET user 2:name aa 2:balance 100

HMGET user 1:name 1:balance

 

在实际应用场景中,不可能把整张表的数据都丢入内存,一般都是缓存一些热点数据。redis是一个单线程操作

优点:

  • 同类数据归类整合存储,方便数据管理。
  • 相比string操作消耗内存与cpu更小。
  • 相比string存储更节省空间。

缺点

  • 过期功能不能是使用在field上,只能用在key上。
  • Redis集群架构下不适合大规模使用。

List应用场景

LRANGE key start stop:拿取一个区间范围内的元素 

正数索引0123
负数索引-4-3-2-1

常用数据结构

Stack(栈) LPUSH+LPOP

Queue(队列): L:PUSH + RPOP

Blocking MQ(阻塞队列) = LPUSH + BRPOP(相当于实现了一个监听,如果队列为空,则不取数据,只有里面存放了数据之后才能从里面获取数据)

微博和微信公众号消息流

 如何实现超级大V?

Set应用场景

微信抽奖小程序

1) 点击参与抽奖加入集合

SADD key {userID}

2) 查看参与抽奖所有用户

SMEMBERS key

3)抽取COUNT名中奖者

SRANDMEMBER key [count] / SPOP key [count]

微信微博点赞,收藏,标签

1) 点赞

SADD like:{消息ID} {用户ID}

2)取消点赞

SREM like:{消息ID} {用户ID}

3) 检查用户是否点过赞

SISMEMBER like:{消息ID} {用户ID}

4) 获取点赞的用户列表

SMEMBERS like:{消息ID}

5) 获取点赞用户数

SCARD like:{消息ID}

集合操作实现微博微信关注模型

  • SINTER set1 set2 set3:交集
  • SUNION set1 set2 set3:并集
  • SDIFF set1 set2 set3:以第一个集合为基准,再删除后面两个集合的并集中所包含的元素。

关注模型

微关系

 

redis底层设计原理之String类型 

 简单动态字符串

成倍的扩容,

原本len = 6

(len + addLen) * 2 = 18:

SDS:

  free:9

  len:9

  char buf[] = "guojia123"

  • 1. 二进制安全的数据结构
  • 2. 内存预分配机制,避免了繁琐的内存分配
  • 3. 兼容C语言函数库

不是一直都会成倍的扩容,而是如果超过了1M  就不会再成倍的库容了。下次再分配只会再多分配1M的空间。

sdshdr5

前3个比特用来表示类型,后5个bit用来表示buffer的长度。

  • #define SDS_TYPE_5 0
  • #define SDS_TYPE_8 1
  • #define SDS_TYPE_16 2
  • #define SDS_TYPE_32 3
  • #define SDS_TYPE_64 4

sdshdr8

 sdshdr16

redis kv底层设计原理

k-v:数据库: 海量数据的存储

数组 : O(1)

链表:O(N)

hash(key)  ->自然数[大]

hash冲突采用的是头插法

字典的底层实现

 数组成倍地扩容,不是直接从旧数组全部迁移到新数组,而是当旧的里面的key值访问过了以后才会迁移。每次事件轮询的时候都会搬几个key.

BitMap

周活跃用户:  按位或

连续登录: 按位与

BItMap的使用注意事项

如何扩容? (addLen + len) * 2表示的是成倍地扩容

如果userId非常大,如何设置offset?

List的数据结构

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

 

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

 

ziplist是个非常紧凑的数据结构。

之后如果有修改的话,改的都是非常小的ziplist

list-max-ziplist-size -2

list-compress-depth 0 :表示多少个不进行压缩。

Hash的数据结构

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

Set的数据结构

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

redis跳表

 每一层最多找两次

index: 1: n /2

index: 2: n/2^2

index: 3: n /2^3

index: K: n/2^k

n/2^k = 2

2^k = n / 2

k = log2 (n-1) 层数

k = 2 * log 2 n (每层最多查询两次) -> logN(和折半查找的时间复杂度是一样的)

 

优点:

  • 1. 性能最大化,fork子进程来完成写操作,让主进程继续处理命令。使用单独子进程来进行持久化,保证了redis的高性能。
  • 2. 当重启恢复数据库的时候,数据量比较大时,Redis直接解析RDB二进制文件,生成对应的数据存储在内存中,比AOF的启动效率更高

 

 

 利用到了调和平均数

根据最后连续为0的个数来估算n的值?

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值