redis基础详解

Redis

常用命令:

redis基础命令

数据持久化
RDB模式

RDB模式是指将数据以快照的形式保存到硬盘的二进制文件(dump.rdb),这个时redis的默认持久化模式,redis创建快照来获得数据在内存中的副本,在配置文件中,快照的自动创建时间可以通过指令进行配置,在多少时间内执行了多少语句来创建rdb文件;本质上是自动执行bgsave;

rdb文件可以用于数据的持久化,还可以同于reids多服务器之间的数据同步,即redis服务器主从复制;还可以用于本地服务器重启后恢复数据;

但是存在一个问题,创建rdb文件是需要资源的,主要有两个方式,一个是通过save命令,当执行save时,不会创建新的线程去生成rdb文件,所以会阻塞客户端的指令执行;另一种方式是通过bgsave命令,当执行bgsave时,redis会创建一个新的线程是执行rdb文件生成,不会阻塞客户端的指令;

数据一致性问题:RDB模式是单位时间内的数据到达才会创建rdb文件,所以会存在一个问题:当执行了一部分指令,但是还没有来得及生成rdb文件,突然就宕机了,这样的话,就会导致部分数据的一致性存在问题;

AOF模式

AOF即只追加文件,AOF模式是指将redis执行记录到aof文件中,到达数据持久化的目的;恢复数据时,会按照aof文件按原来的指令次序执行指令;AOF模式下有一个AOF缓冲区,用于内存与AOF磁盘的数据交换;

一般过程为:数据 --> redis内存 -->AOF缓冲区 --> AOF磁盘;

从AOF缓冲区到AOF磁盘有三个不同的持久化策略:

  1. appendfsync always : 每一次数据的修改指令都会将AOF缓冲区的数据放入到AOF磁盘中;这个方式会降低redis的性能;
  2. appendfsync everysec : 每秒钟进行一次,将AOF缓冲区的数据同步到AOF磁盘;
  3. appendfsync no : redis不进行数据同步,让操作系统决定何时同步数据到AOF磁盘;

AOF模式与RDB模式相比,AOF遇到宕机的情况下,AOF模式下丢失的数据会少于RDB模式,但是AOF模式的恢复速度没有RDB模式快,但存储速度更快,rdb文件数据量少;

但是存在一个问题,AOF文件的数据量较大,在AOF模式下,只要是修改数据的指令都会被记录到aof文件中,但是比如set k v1; set k v2; set k v3; 三个连续执行的修改操作,都存储到aof文件中,显然是不合理的,只要存放set k v3即可,所以redis实现了一个AOF重写机制:redis维护了一个AOF重写缓冲区,与AOF缓冲区同一个等级,当执行AOF重写操作时,会将AOF重写缓冲区的数据写入到新的aof文件中,使得新旧两个aof文件所保存的数据一致;最后服务器用新的aof文件代替旧的aof文件;

数据一致性问题:即使是aof模式的always也会存在宕机导致数据的不一致问题;解决方法:

实时策略 : 写入的过程,把数据存到数据库中,成功后,再让缓存失效,用户体验好,是默认应该使用的策略;
异步策略——适用于并发量大,但是数据没有那么关键的情况,好处是实时性好;
定时策略——并发量实在太大,数据量也大的情况,异步都难以满足的场景;

如果不让缓冲失效,而是让缓冲更新,那么就可能存在缓冲区和数据库的数据不一致的问题;

混合持久化

顾名思义,就是结合RDB和AOF的两者的优点,即在AOF重写的时候,将RDB直接写入到新aof文件中,然后再将aof缓冲区的数据写入到新aof文件中;

Redis内存模型

内存模型主要有系统数据、内存碎片、缓冲区内存、redis存储数据等,在redis中多种类型的数据结构都是使用节点结构存储的,这个dictEntry节点中有几个变量,分别是key、val、next,next为指向下一个节点的指针;val指向redisObject节点,用于存放values的值,包含了两个变量,一个为type存放值的类型,一个是pre存放值的地址;key指向SDS节点,用于存放k的值;可以说reids中的内存与dictEntry、redisObject、SDS直接相关

**dictEntry:**存放着键值对指向的具体数据节点;

**redisObject:**redis的五种基本类型用的都是这个数据结构,有几个变量:type:4bit记录数据的五种类型,encoding:4bit记录数据的编码格式,在redis中不同的数据类型不同的数据长度都有不同的编码格式

refcount:32bit引用计数,Redis里面的数据可以通过引用计数进行共享;pre指针,用于指向对象的值,即存放数据对象的具体地址;

**sds:**简单动态字符串,用于存放redis中的字符串,redis中key的类型均为字符串,sds主要用于存放key的值,或被redisObject指向,存放values中类型为字符串的数据;

redisObject与redis的变量类型直接挂钩,有五种普通类型分别为:String、list、set、hash、zset;

String类型

有三种数据存储结构,分别是int、embstr、raw;

int:保存long 型(长整型)的64位(8个字节)有符号整数,如果是浮点数, Redis 内部其实先将浮点数转化为字符串值,然后再保存;

**embstr:**当数据长度小于44btye时,用这个数据结构存储,本质上是embstr格式的sds;

**raw:**存放数据长度大于44字节的字符串;

当redis启动时,会预先自动创建10000个分别存储0-9999值的redisObjcect对象,当set时,如果在这之中存在这个对象,则会直接共享数据对象,不会额外占用新的内存空间;

三种数据结构的内存分布:

int类型不是字符串所以不会使用sds存放数据,而是直接使用redisObject对象,但是embstr和raw格式,是字符串类型,所以会使用sds结构存放数据,但是两者在内存地址也存在一定区别:embstr格式中,redisObject对象和sds对象的地址空间是连续分配的,但是raw格式中,redisObject对象和sds对象的地址是不连续的,使用pre指针指向sds具体的地址;

hash类型

在hash类型中存放数据的结构分为两种:ziplist以及hashTable;主要的判断条件有两个:hash中键值对的数量是否小于512个,键值对的单个数据长度是否小于64字节

zipList:压缩列表是一种紧凑编码格式,总体思想是多花时间来换取节约空间,即以部分读写性能为代价,来换取极高的内存空间利用率,因此只会用于 字段个数少,且字段值也较小 的场景。压缩列表内存利用率极高的原因与其连续内存的特性是分不开的。

ziplist节点是一个经过特殊编码的双向链表 ,上下指针不是指向上下节点的位置,而是存放上一节点和当前节点的数据的长度;在普通双向链表中,当数据量较小时,指针大小可能比数据的大小还要大,这样的浪费了内存空间,在压缩列表中,不在维护两个指针,而是记录上一个节点和当前节点的数据长度,通过数据长度判断上一个节点的位置,很明显,读写速度没有普通链表快,是一种用时间换空间的思想;但是redis是基于内存的速度本身就是优势;

压缩列表是 Redis 为节约空间而实现的一系列特殊编码的连续内存块组成的顺序型数据结构,本质上是字节数组

为什么数据量大时不用ziplist?
因为ziplist是一段连续的内存,插入的时间复杂化度为O(n),而且每当插入新的元素需要realloc做内存扩展;而且如果超出ziplist内存大小,还会做重新分配的内存空间,并将内容复制到新的地址。如果数量大的话,重新分配内存和拷贝内存会消耗大量时间。所以不适合大型字符串,也不适合存储量多的元素。

hashTable:

它是一个数组+链表的结构,它是真正的哈希表结构;总体的说,java中的hashMap有点相似;

List类型

在低版本的Redis中,list采用的底层数据结构是ziplist+linkedList;但高版本的Redis中底层数据结构是quicklist(它替换了ziplist+linkedList),而quicklist也用到了ziplist;

quicklist维护了一个双向链表,但是每一个节点是一个ziplist节点,一个ziplist可以存放多个数据节点;

为什么不直接使用linkedlist?

linkedlist的附加空间相对太高,prev和next指针就要占去16个字节,而且每一个结点都是单独分配,会加剧内存的碎片化,影响内存管理效率。

set类型

set的编码格式分为两种:intset,hashtable;当set元素为整数同时元素个数<set-max-inset-entries会使用intset格式,反之则为hashtable;

intset:本质上是数组,在这个结构中有encoding变量记录元素的编码格式,length变量记录set中的元素个数,以及contents[]变量用于存放set元素;

zset类型

zset的底层实现有两种数据存储结构:ziplist,skiplist;当元素的个数超过128或者元素的长度大于64字节时,redis会使用skiplist实现有序set集合;否则使用ziplist;

那么skiplist是一种什么样的结构呢?

由于链表,无法进行二分查找,因此借鉴数据库索引的思想,提取出链表中关键节点(索引),先在关键节点上查找,再进入下层链表查找。所以,跳表 = 链表 + 多级索引; 跳表是一种最典型的空间换时间的解决方案;

优缺点:只有在数据量较大的情况下才能体现出来优势。而且应该是读多写少的情况下才能使用,但是维护成本相对较高,新增或者删除时需要把所有索引都更新一遍,最后在新增和删除的过程中的更新,时间复杂度也是O(log n);

redis事务

开启事务:multi ,维护一个队列,所以之后的命令都会入队列中。但是如果有语法错误的语句,就不会入队
执行事务:exec,会依次执行队列中的命令。
CAS:watch ,在exec之前使用,如果在exec的时候,数据##版本##【不会有ABA问题】发生了修改,那么就终止队列所有命令的执行
取消事务:discard。终止执行,但是也不会回滚,即已经执行好了的操作不会进行回滚;

redis淘汰策略

redis 提供 6种数据淘汰策略:
volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
**allkeys-lru:**当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的).
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。

lru可以使用LinkedHashMap实现,LIinkedHashMap是有序的,可以删除链表的旧节点,删除数据中最近最少使用;

redis高速策略

redis是基于内存的,redis服务器为单线程执行,且每一次操作都是原子性的,所以redis中的操作不需要加锁,redis使用epoll的非阻塞多路复用技术(多个网络连接一个线程),即NIO;

Redis的单线程是如何工作的?

redis主要分为三个部分:IO多路复用,入队列,事件处理器消费;redis执行一条命令,主要过程为建立链接,发送请求,读取应答,对应:

AE_READABLE:建立链接,发送请求
AE_WRITABLE:读取应答

IO多路复用是基于epoll实现的,可以自动选择有IO请求的socket端,并放入到队列中;

Redis的分布式锁

什么是分布式锁?

在分布式系统中,如果有多个客户端对集群系统中对一个数据进行读写,对于单机的多线程可以通过加锁实现只有一个线程可以对数据进行操作,但是在分布式系统中,java普通的加锁操作无法锁住多个服务器上的线程,所以要保证他们的同步性,可以通过分布式锁实现;常用的方法有:

  1. redis分布式锁,使用setnx操作,只有当指定的key不存在时,才可以set,以获得分布式锁;同时可以搭配ex指令,设置key的过期时间;
  2. zookeeper分布式锁,我们可以基于唯一节点特性来实现分布式锁的操作,由于Zookeeper中节点的唯一性特性,使得只会有一个用户成功创建 /Lock 节点,剩下没有创建成功的用户表示竞争锁失败。
  3. Redisson框架的分布式锁

redis分布式锁实现存在的问题:当一个线程获得锁后,如果该线程突然出现错误导致执行中断导致锁没有被释放,这样的话就会形成死锁,解决方法,通过给key设置一个过期时间,可以防止发生死锁,但是又会存在一个问题,如果一个线程没有执行完成,key就过期了,同时锁被其他线程获得了,就是导致多线程的同步问题;这个问题的解决可以通过redission的看门狗机制解决,自动延长key的过期时间;

redission看门狗机制

使用redission加锁的时,会自动对key设置过期时间,并自动续期,如果超过了默认的时间,就会自动释放锁,但是如果在set锁的时候设置了过期时间就不会自动续期,续期时间是看门狗的默认时间的1/3续期一次,续成满时间。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值