缓存

redis有哪些类型

String(Key-Value)有需要Java课程及资料的私信 博主免费领取噢

String是最常用的一种数据类型,普通的key/value存储都可以归为此类。

一个Key对应一个Value,string类型是二进制安全的。

Redis的string可以包含任何数据,比如jpg图片(生成二进制)或者序列化的对象。

Hash(Key-Value)

hash是一个string 类型的field和value的映射表。

hash特别适合存储对象。相对于将对象的每个字段存成单个string 类型。一个对象存储在hash类型中会占用更少的内存,并且可以更方便的存取整个对象。

Redis的Hash数据类型的value内部是一个HashMap,如果该Map的成员比较少,则会采用一维数组的方式来紧凑存储该MAP,省去了大量指针的内存开销

采用string类型的存储对象,需要将对象进行序列化 使用hash数据类型不需要

List

list是一个链表结构,主要功能是push, pop, 获取一个范围的所有的值等。操作中key理解为链表名字。

Redis的list类型其实就是一个每个子元素都是string类型的双向链表。我们可以通过push,pop操作从链表的头部或者尾部添加删除元素,这样list既可以作为栈,又可以作为队列(栈就是insertFirst+deleteFirst,队列就是insertLast+deleteFirst)。可以支持反向查找和遍历,方便操作,不过带来了部分额外的内存开销。

Set

是string类型的无序集合。set是通过hash table实现的,可以进行添加、删除和查找。对集合我们可以取并集,交集,差集.

zset

Redis sorted set的使用场景与set类似,区别是set不是自动有序的,而sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。

Redis 使用场景

  1. 缓存——热数据
  2. 计数器
  3. 队列
  4. 位操作(大数据处理)
  5. 分布式锁与单线程机制
  6. 最新列表
  7. 排行榜

Redis 持久化机制

RDB持久化

RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。

也是默认的持久化方式,这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb

可以通过配置设置自动做快照持久化的方式。我们可以配置redis在n秒内如果超过m个key被修改就自动做快照

RDB文件保存过程

  1. redis调用fork,现在有了子进程和父进程。
  2. 父进程继续处理client请求,子进程负责将内存内容写入到临时文件。由于os的写时复制机制(copy on write)父子进程会共享相同的物理页面,当父进程处理写请求时os会为父进程要修改的页面创建副本,而不是写共享的页面。所以子进程的地址空间内的数 据是fork时刻整个数据库的一个快照。
  3. 当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出。

client 也可以使用save或者bgsave命令通知redis做一次快照持久化。save操作是在主线程中保存快照的,由于redis是用一个主线程来处理所有 client的请求,这种方式会阻塞所有client请求。所以不推荐使用

AOF

AOF文件保存过程

  1. redis会将每一个收到的写命令都通过write函数追加到文件中(默认是 appendonly.aof)。
  2. 当redis重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。当然由于os会在内核中缓存 write做的修改,所以可能不是立即写到磁盘上。这样aof方式的持久化也还是有可能会丢失部分修改。不过我们可以通过配置文件告诉redis我们想要 通过fsync函数强制os写入到磁盘的时机。


Redis 集群方案与实现

实现基础——分区

  • 分区是分割数据到多个Redis实例的处理过程,因此每个实例只保存key的一个子集
  • 通过利用多台计算机内存的和值,允许我们构造更大的数据库
  • 通过多核和多台计算机,允许我们扩展计算能力;通过多台计算机和网络适配器,允许我们扩展网络带宽

客户端分片

  • 由客户端决定key写入或者读取的节点
  • 包括jedis在内的一些客户端,实现了客户端分片机制

基于代理的分片

  • 客户端发送请求到一个代理,代理解析客户端的数据,将请求转发至正确的节点,然后将结果回复给客户端。
  • 开源方案
  • Twemproxy
  • codis

路由查询

  • 将请求发送到任意节点,接收到请求的节点会将查询请求发送到正确的节点上执行。
  • 开源方案:Redis-cluster

Redis 为什么是单线程的

因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了

单线程只是在处理我们的网络请求的时候只有一个线程来处理,一个正式的Redis Server运行的时候肯定是不止一个线程的

Redis为什么这么快

  • 完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
  • 数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的
  • 采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗
  • 使用多路I/O复用模型,非阻塞IO
  • 使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求


消息队列

消息队列的使用场景

异步处理

应用解耦

流量削锋

日志处理


消息的顺序性和重复问题

消息的幂等性解决思路

消息的堆积解决思路

处理消息堆积的方法就是把它存下来。只是这个存储可以做成很多方式。比如存储在内存里,存储在分布式KV里,存储在磁盘里,存储在数据库里等等。但归结起来,主要有持久化和非持久化两种。 持久化的形式能更大程度地保证消息的可靠性(如断电等不可抗外力),并且理论上能承载更大限度的消息堆积(外存的空间远大于内存)。 但并不是每种消息都需要持久化存储。很多消息对于投递性能的要求大于可靠性的要求,且数量极大(如日志)。这时候,消息不落地直接暂存内存,尝试几次failover,最终投递出去也未尝不可