redis整理

redis整理

- redis是啥?

  • redis是一个高性能的key-value数据库(好像很简单的样子)
- 那么问题来了,为什么性能这么高,也就是为啥快?
  1. redis是以内存作为存储介质。

    • 所以读写效率高。以设置和读写一个256字节的字符串来说,读取速度为11万次/s,写的速度是8.1万次/s
  2. 数据结构是kv

    • 查找复杂度是o(1)
  3. 单线程

    • 避免了频繁的上下文切换
  4. 使用多路复用I/O模型,非阻塞Io

    [图片上传失败...(image-1af1b-1536028551371)]

  • 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

[图片上传失败...(image-828cb7-1536028551372)]

  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)

  • [图片上传失败...(image-7bc572-1536028551371)]

    • key(hello)并不是已字符串储存的,而是用sds(simple dynamic string简单动态字符串)。

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

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

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

  • jemalloc划分的内存单元

    [图片上传失败...(image-c0bd46-1536028551371)]

  • 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的一个子集。
  • 分群

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

        [图片上传失败...(image-b14344-1536028551364)]

      • 基于代理的集群

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

        [图片上传失败...(image-f1bab4-1536028551364)]

  • Redis-cluster

[图片上传失败...(image-18e9f4-1536028551371)]

特点:

  • 无中心架构。

  • 所有节点彼此互联(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段,然后分开取,最后汇总到一起。
      请求路由方式
      借助客户端进行一次转发。

[图片上传失败...(image-ac9152-1536028551371)]

  • codis

框架

[图片上传失败...(image-a14c33-1536028551371)]

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

模块简介

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

另一个角度架构

[图片上传失败...(image-3c5e81-1536028551371)]

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

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

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

主从复制 原理

[图片上传失败...(image-f0c8d3-1536028551371)]

  • 当启动一个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的具体工作

[图片上传失败...(image-d5ee6b-1536028551371)]

两个问题

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

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

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

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

分布式锁

参考这篇

事务

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

一些面试题

面试题

转载来源:
作者:小绵羊你毛不多
链接:https://www.jianshu.com/p/0000e64bf5f3

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容