Redis讲解

redis整理

– redis是啥?

  • redis是一个高性能的key-value数据库(好像很简单的样子)
– 那么问题来了,为什么性能这么高,也就是为啥快?
  1. redis是以内存作为存储介质。
    • 所以读写效率高。以设置和读写一个256字节的字符串来说,读取速度为11万次/s,写的速度是8.1万次/s
  2. 数据结构是kv
    • 查找复杂度是o(1)
  3. 单线程
    • 避免了频繁的上下文切换
  4. 使用多路复用I/O模型,非阻塞Io

    《Redis讲解》 20180531085855659.jpg

  • redis-client在操作的时候,会产生具有不同事件类型的socket。
  • 在服务端,有一段I/O多路复用程序,将其置入队列之中
  • 然后,文件事件分派器,依次去队列中取,转发到不同的事件处理器中。
    具体可以参考
– 那除了快还有啥特点,memcache也不慢,为啥要用redis?
  1. 支持的类型多,除了支持string,还支持list、set、zset和hash
  2. 支持数据的持久化,可以将内存中的数据存储到硬盘
其他的呢
  1. 支持主从模式,可以配置集群、数据备份。
  2. 支持原子、事务
  3. 支持publish/subscribe, 通知, key 过期等等特性。

– 应用场景呢,干啥能用到啊?

  1. 缓存(热数据)变动比较小的数据
    • 像组织架构。查询比较多的数据,像会员详情。
  2. 单线程,可以避免并发
    1. 计数器 incr,统计访问次数。
    2. 全局增量ID生成,(比如插入会员的时候,在进入队列之后就要返回id,所 以不能等数据库生成id之后再返回)
  3. list类型可以做队列(要求高点的的还是用activeMQ或者其他的吧)
  4. 统计日活跃数(位操作)
    • 每天新建一个byteArray,初始化都是0,member_id作为offset,利用setbit设置为1,统计的时候利用bitcount就可以统计了!
  5. 验证重复请求
    • 将前段的requestIP,参数的hash当做key,设置时间,下次请求如果有这个key,则判断为重复请求,防止刷数据。
  6. 秒杀
    • 把库存放到redis中,秒杀的时候先把库存-1,如果大于0,则成功,否则,失败。不能先判断大于0,然后再-1,会导致超卖。
  7. 最新列表
    • 利用list的lpush即可。
  8. 排行榜
    • 利用有序集合

– redis是放到内存里的,系统重启了不就丢失了吗?

这里就要说说redis的持久化了
主要有两种方式

  1. RDB 不定期的通过异步方式保存到磁盘上(这称为“半持久化模式”)
  2. AOF也可以把每一次数据变化都写入到一个append only file(aof)里面(这称为“全持久化模式”)。
    具体可以参考这篇文章redis持久化

– 现在你搭建了一个redis运行起来了,我想知道redis占用了多少内存

通过redis-cli登陆redis客户端,然后输入info命令,这里我们查看memory

《Redis讲解》 reids memory

  1. used_memory 是redis分配器分配的内存总量,包括使用的虚拟内存。即used_memory=数据内存+虚拟内存
  2. used_memory_rss 是redis进程占据的系统内存,除了包括分配器分配的内存,还有包括**线程本身运行需要的内存,内存碎片****,不包括虚拟内存!!used_memory_rss=数据内存+线程内存+内存碎片
  3. mem_fragmentation_ratio 内存碎片比率 used_memory_rss/used_memory
name转化一下
used_memory_rss数据内存+线程内存+内存碎片
used_memory数据内存+虚拟内存
  • 对于jemalloc来说,比值在1.03左右比较健康。
  • 所以该值越大,代表内存碎片越大! (上图数据这么大,是因为里面数据太少);

啥叫内存碎片:比如对数据修改频繁,且数据大小相差较大,导致redis释放的内存在物理内存中并没有被释放,但redis又无法合理利用,这就会导致内存碎片。

啥叫虚拟内存:就是暂时把不经常访问的数据从内存交换到磁盘中,从而腾出宝贵的 内存空间用于其他需要访问的数据。

    • redis没有使用os提供的虚拟内存机制而是自己在用户态实现了自己的虚拟内存机制 理由有两个:
      • os 的虚拟内存是已4k页面为最小单位进行交换的。而redis的大多数对象都远小于4k,所以一个os页面上可能有多个redis对象。
      • ②redis可以将被交换到磁盘的对象进行压缩,保存到磁盘的对象可以去除指针和对象元数据信息。一般压缩后的对象会比内存中的对象小10倍
      • 具体可以看这个文章,虚拟内存
    • 虚拟内存使用场景呢?
      • 数据库中只是包含少量的keys,而每一个key所关联的value却非常大,那么这种场景对于使用虚存就再合适不过了。
      • 如果你的数据库中有大量的keys,其中每个key仅仅关联很小的value,那么这种场景就不是非常适合使用虚拟内存
      • key-value变成 key-field-value,原来的key变成了field!为了能让虚存更为充分的发挥作用以帮助我们提高系统的运行效率,我们可以将带有很多较小值的Keys合并为带有少量较大值的Keys。其中最主要的方法就是将原有的Key/Value模式改为基于Hash的模式,这样可以让很多原来的Keys成为Hash中的属性。
      • 参考这篇文章 虚拟内存
  • 如果<1,代表虚拟内存开启,并且占用比较大。虚拟内存的媒介是硬盘,速度很慢,所以遇到应及时处理,增加redis节点(集群后面讲)或者增大redis内存、优化应用

redis是如何存储数据的呢!

比如 set hello world (key–>hello value–>world)

  • 《Redis讲解》 redis数据结构
    • key(hello)并不是已字符串储存的,而是用sds(simple dynamic string简单动态字符串)。

    • val是指针,指向value存储位置,准确的说是值的包装

    • 其中reidsObject不是真正的值,而是值的包装,其中type表名value的类型,ptr指向值的存储位置。

    • 这些对象默认都是jemalloc来分配内存的,已dictEntry为例,64位计算机,3个字节占用24个字节,jemalloc会给他分配32个字节。(为什么呢?)
      看下图,了解一下jemalloc的内存分配就懂了!

  • jemalloc划分的内存单元 《Redis讲解》 image

  • redisObject
typedef struct redisObject {
  unsigned type:4;
  unsigned encoding:4;
  unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */
  int refcount;
  void *ptr;
} robj;
    • type是类型,主要是string list等
    • encoding 表示对象的内部编码,对于redis支持的每种类型,都有两种以上的内部编码,对于string,有int 、embstr、raw三种编码。redis会根据不同的场景来对对象进行不同的编码,大大提高了redis的灵活性和效率。
    • int 8个字节的长整型
    • embstr <=39个字节的字符串
    • raw >39个字节的字符串

    embstr与raw比较?
    embstr使用时只分配一次内存空间(redisObject与sdc是连续的),raw分配两次(分别为redisObject和sds分配),好处:embstr使用时少分配一次,embstr删除时少释放一次,以及对象所有数据连在一起,查找方便。坏处:很明显,如果字符串的长度增加,则需要重新分配空间,因此,embstr实现方式为只读*

    编码转换: 当int数据不再是整数,或大小超过了long的范围时,自动转化为raw。在对embstr进行修改时,会先转换成raw,所以修改后的数据,无论大小,都是raw

  • lru

    • 记录最后一次被命令访问时间,和回收有关
  • refcount

    • 记录的该对象被引用的次数,类型为整型。初始化为1,decrRefCount incrRefCount,为0的时候,回收内存。
    • Redis的共享对象目前只支持整数值的字符串对象,初始化为0~9999
  • SDS

struct sdshdr { 
    int len;
    int free;
    char buf[];
};
    • buf表示字节数组,用来存储字符串;len表示buf已使用的长度,free表示buf未使用的长度
    • sds与c字符串比较
      • 获取字符串长度 sds是o(1) c是o(n)
      • 修改字符串时内存的重分配
        具体细节,看这篇文章,或者看《redis设计与实现》
-然后你发现一个节点不够,怎么搭建集群呢?事务?
  • 分片 是分群的基础

    • 分区是分割数据到多个Redis实例的处理过程,因此每个实例只保存key的一个子集。
  • 分群

    • 几种实现方式
      • 客户端分片

        《Redis讲解》 分片

      • 基于代理的集群

        • 开源方案 Twemproxy codis
      • 路由查询

        《Redis讲解》 image

  • Redis-cluster

《Redis讲解》 image

特点:

  • 无中心架构。
  • 所有节点彼此互联(PING-PONG机制),6379(默认)用于服务客户端查询,16379(默认服务端口 + 10000)用于集群内部通信。
  • 节点之间通过gossip协议交换状态信息
  • 建议一主多从的机制,这样主机故障之后,会通过投票机制使slave到master
  • 没有用到一致性hash,而是哈希槽
    • 对于每个进入Redis的键值对,根据key进行散列,分配到这16384个slot中的某一个中。
    • 使用的hash算法也比较简单,就是CRC16后16384取模
    • 对于这种是不能批量读取数据的,因为每个key不一定在哪个节点上。两种方法:①放的时候, “a{123}”和”b{123}”是在同一个slot上 ② 读取的时候,先对key进行取模,判断在哪个slot上,还能知道每个节点分管的slot段,然后分开取,最后汇总到一起。
      请求路由方式
      借助客户端进行一次转发。

《Redis讲解》 image

  • codis

框架

《Redis讲解》 image

  • 引入group分组,其指定一个master和多个slave,实现高可用
    • master挂掉之后,不会自动升级slave,涉及数据一致性,需手动调整codis-ha
  • client可以直接访问proxy
  • 采用预分片的形式。启动时创建1024个slot 1个slot只能放在一个group中,一个group可以存放1-1024个

模块简介

  • Server 增加额外数据结构,支持slot有关的操作和数据迁移
  • Dashboard 支持server、proxy的添加删除以及数据迁移等。
  • proxy 客户端链接redis代理服务

另一个角度架构

《Redis讲解》 image

  • 内部主要有三个模块,redis、router和model
  • Router负责将前端请求发送给redis
  • Model负责和ZK交互保持数据一致性。数据:group\proxy\slot的配置
  • Redis 负责和redis交互

可以参考这个文章写的很不错:redis集群

还需要再研究一下~~~~

主从复制 原理

《Redis讲解》 image

  • 当启动一个slave进程,它会向Master发送一个SYNC command,请求同步链接
  • 无论是第一次连接,还是重新连接,都会做两件事
    • Master都会启动一个后台进程,将数据快照保存到数据文件中。
    • 同时Master会记录所有修改数据的命令并缓存到数据文件中
  • 缓存操作完成后,Master就发送数据文件给Slave
    • Slave将数据文件保存到硬盘上,然后将其加载到内存中
    • 接着Master就会将所有修改数据的操作,发给slave

若slave宕机

  • 恢复正常后会重新连接,Master收到Slave的连接后,将其完整的数据文件发送给slave。
  • 如果同时收到多个slave发来的同步请求,Master也只会在后台启动一个进程保存数据文件,然后将其发送给所有的slave

2.8开始支持增量复制(PSYNC命令)

  • Master端为复制流维护一个内存缓冲区,记录最近发送的复制流命令。
  • 同时 Master和Slave之间都维护一个复制偏移量,和当前Master服务器的id,这样slave重连时
    • 如果Master相同,增量复制
    • 否则,依然需要全量复制

来看看master的具体工作

《Redis讲解》 image

两个问题

  1. 为什么要定期发送保活命令?

  2. 为什么在发送rdb文件之后,又发送变更命令?

  3. 因为master会不定时发送变更命令,为了防止slave无意义的等待,以此告诉slave,我还活着,不要中断连接。

  4. master保存rdb文件是一个子进程进行的,所以保存期间依然可以处理客户端请求。因此为了保证数据一致性,会再次发送变更命令

分布式锁

参考这篇

事务

link 不支持回滚 出问题 会继续执行

一些面试题

面试题

    原文作者:小绵羊你毛不多
    原文地址: https://www.jianshu.com/p/0000e64bf5f3
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞