redis基础

redis为什么快

  • redis存在内存中,内存读写ns级,磁盘读写ms级;
  • redis 是k,v,没有关系操作;
  • value是有类型的,每种类型都有自己的本地方法,计算向数据移动,计算发生在内存;
  • io多路复用,epoll

redis数据结构

先上一张图
在这里插入图片描述

string

二进制安全,可以存字符串、整数、图片、序列化json对象,底层数据结构SDS(动态字符串),类型分为int、embstr(长度小于44)、raw(长度大于44)
int embstr raw

redis是基于c开发的,c的字符串是二进制不安全的,所以redis自己开发了新数据结构表示字符串,这种数据结构称之为简单动态字符串(Simple dynamic string),简称sds。在c语言中字符串是用char数组来存储,而且字符串结尾必须以一个空\0结尾,不记录长度,获取len必须遍历整个char数组,直到遇见第一个\0结束,获取长度复杂度O(n)。
c是以\0结尾,所以只能保存字符串,不能保存图片、视频、音频等信息,否则可能出现字符串不完整,所以是二进制不安全的。

redis为了实现二进制安全字符串,对c做了改进,字符中保留了以\0结尾的习惯是为了使用c原生api,获取长度直接取len,复杂度降为O(1),结构如下:

struct sdshdr {
  int len;//记录buf数组已使用的长度,即SDS的长度(不包含末尾的'\0')
  int free;//记录buf数组中未使用的长度
  char buf[];//字节数组,用来保存字符串
}

在Redis 3.2之后的版本,Redis对sds又做了优化,按照存储空间的大小拆分成为了sdshdr5、sdshdr8、sdshdr16、sdshdr32、sdshdr64,分别用来存储大小为:32字节(2的5次方),256字节(2的8次方),64KB(2的16次方),4GB大小(2的32次方)以及2的64次方大小的字符串(因为目前版本key和value都限制了最大512MB,所以sdshdr64暂时并未使用到)。sdshdr5只被应用在了Redis的key中,value中不会被使用到,因为sdshdr5和其它类型也不一样,其并没有存储未使用空间,所以比较适用于使用大小固定的场景(比如key值)

struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; //已使用空间大小
    uint8_t alloc; //总共申请的空间大小(包括未使用的)
    unsigned char flags; //用来表示当前sds类型是sdshdr8还是sdshdr16等
    char buf[]; //真实存储字符串的字节数组
};

主要命令:
在这里插入图片描述在这里插入图片描述

hash

mapmap,value也是k,v形式存储,底层数据结构ziplist + hashtable

为什么要有ziplist(主要是为了节省空间,因为redis存储在内存中)
有两点原因:
1、普通的双向链表,会有两个指针,在存储数据很小的情况下,我们存储的实际数据的大小可能还没有指针占用的内存大,是不是有点得不偿失?而且Redis是基于内存的,而且是常驻内存的,内存是弥足珍贵的,所以Redis的开发者们肯定要使出浑身解数优化占用内存,于是,ziplist出现了。
2、链表在内存中,一般是不连续的,遍历相对比较慢,而ziplist可以很好的解决这个问题。

当一个哈希对象可以满足以下两个条件中的任意一个,哈希对象会选择使用ziplist编码来进行存储:

  • 哈希对象中的所有键值对总长度(包括键和值)小于等于64字节(这个阈值可以通过参数hash-max-ziplist-value来进行控制)。
  • 哈希对象中的键值对数量小于等于512个(这个阈值可以通过参数hash-max-ziplist-entries来进行控制)。

一旦不满足这两个条件中的任意一个,哈希对象就会选择使用hashtable编码进行存储。

  • 主要命令:
    在这里插入图片描述
  • ziplist转hashtable在这里插入图片描述
  • 应用场景:存储用户信息;存储商品信息,有字段更新只用update对应字段;
set

集合,1. 不允许有重复的元素,2.集合中的元素是无序的,不能通过索引下标获取元素,3.支持集合间的操作,可以取多个集合取交集、并集、差集;底层是hashtable + intset,当一个集合满足以下两个条件时,Redis 会选择使用intset编码:

  • 集合对象保存的所有元素都是整数值。
  • 集合对象保存的元素数量小于等于512个(这个阈值可以通过配置文件set-max-intset-entries来控制)。

一旦集合中的元素不满足上面两个条件,则会选择使用hashtable编码。

  • 常用命令:
    在这里插入图片描述在这里插入图片描述
  • 应用场景:给一批用户打tag;朋友圈点赞;共同好友,可能认识的人;
list

List 说白了就是链表(redis 使用双端链表实现的 List),是有序的,value可以重复,可以通过下标取出对应的value值,左右两边都能进行插入和删除数据;底层数据结构有linkedlist + ziplist,3.2之后quicklist(底层使用ziplist)

  • 常用命令:
    在这里插入图片描述
  • 使用技巧:
    lpush+lpop=Stack(栈)
    lpush+rpop=Queue(队列)
    lpush+ltrim=Capped Collection(有限集合)
    lpush+brpop=Message Queue(消息队列)
  • 应用场景:例如微博的时间轴,有人发布微博,用lpush加入时间轴,展示新的列表信息。
zset

带有权重的set,有序集合和集合有着必然的联系,保留了集合不能有重复成员的特性,区别是,有序集合中的元素是可以排序的,它给每个元素设置一个分数,作为排序的依据。底层数据结构是ziplist + skiplist

skiplist 跳表,效率等同于平衡树,但是比平衡树实现简单;在skiplistnode中维护一个level数组;
typedef struct zskiplistNode {
sds ele;//元素
double score;//分值
struct zskiplistNode *backward;//后退指针
struct zskiplistLevel {//层
struct zskiplistNode *forward;//前进指针
unsigned long span;//当前节点到下一个节点的跨度(跨越的节点数)
} level[];
} zskiplistNode;

  • 常用命令
    在这里插入图片描述
  • 使用场景:排行榜

redis常见问题

缓存雪崩
  • 概念:
    高并发场景下,同一时刻大批量redis key过期,所有请求直接请求到db,导致db被打挂。
  • 解决方案:
    1)热点数据设置随机过期时间;
    2)热点数据不过期,手动更新修改;
缓存穿透
  • 概念:
    一个客户端请求访问的redis key,在redis中不存在,在db中也不存在,会一直打到db。
  • 解决方案:
    1)不存在的redis key,在访问db也不存在,直接设置set(key,null),此方案存在弊端,假如之后该key在db中有数据,访问不到;
    2)添加参数校验,接口层添加参数校验,权限校验,只能过滤一部分参数;
    3)布隆过滤器
缓存击穿
  • 概念:
    高并发下,一个热点redis key突然过期,所有请求直接打到db,导致db打挂;
  • 解决方案:
    1)热点数据设置不过期;
    2)添加锁,拿到锁的链接去db访问数据,其他线程自旋等redis有值再访问或者返回报错;

本文参考链接:https://www.kancloud.cn/zatko/redis/2274304

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值