天天看点

品优购第八天Redis理解

  1. 什么是Redis,什么是Jedis,什么是Spring Data Redis?有什么区别?

Redis是一个开源的、使用C语言编写的、支持网络交互的、可基于内存也可持久化的Key-Value数据库。在开发中,多用于实现缓存。

Jedis是Redis官方推荐出来的面向java的客户端,可用于java开发中操作Redis的对象

Spring-data-Redis是spring大家族的一部分,提供了在srping应用中通过简单的配置访问Redis服务,对Redis底层开发包(Jedis, JRedis, and RJC)进行了高度封装,RedisTemplate提供了Redis各种操作、异常处理及序列化,支持发布订阅,并对spring 3.1 cache进行了实现。

spring-data-Redis针对Jedis提供了如下功能:

1.连接池自动管理,提供了一个高度封装的“RedisTemplate”类

2.针对Jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口ValueOperations:简单K-V操作

SetOperations:set类型数据操作

ZSetOperations:zset类型数据操作

HashOperations:针对map类型的数据操作

ListOperations:针对list类型的数据操作

这三个究竟有什么区别呢?可以简单的这么理解,Redis是用C写的一个基于内存的Key-Value数据库,而Jedis是Redis官方推出的面向Java的Client,提供了很多接口和方法,可以让Java操作使用Redis,而Spring Data Redis是对Jedis进行了封装,集成了Jedis的一些命令和方法,可以与Spring整合。在后面的配置文件(redis-context.xml)中可以看到,Spring是通过Jedis类来初始化connectionFactory的。

     2.项目中哪里用到了Redis?为什么用它?

在项目中的网站首页的广告部分,每次刷新都需要进行从数据库中来读取数据,如果是用并发量比较大的时候,会对数据库产生很大的压力,如果用到缓存的话,那么只需要从缓存中读取就可以了,这样就能帮数据库分担部分压力从而达到解决高并发的问题;所以项目中选用Redis来作为网站首页部分的缓存,把商品的信息存入到Redis中,而且因为Redis的基于内存进行存储的特性,从而使得在访问首页的时候也能很快地把商品信息读取出来,提高了用户的体验。

作为缓存的技术有很多,为什么用Redis呢?结合Redis的优点进行回答

    3.关于Redis的一些其他问题

  • Redis的数据类型

Redis有丰富的数据类型,它是以<key,value>的形式进行存储,而他的value又可以存储5种数据类型:

1.字符串(string)

2.字符串列表(list)

3.字符串集合(set)

4.有序字符串集合(sorted set)

5.哈希(hash)

  • Redis的过期机制

Redis中有个设置时间过期的功能,即对存储在Redis数据库中的值可以设置一个过期时间。作为一个缓存数据库,这是非常实用的。这就是Redis过期机制。这个机制运用的场景十分广泛,比如我们一般项目中的token或者一些登录信息,尤其是短信验证码都是有时间限制的,或者是限制请求次数,如果按照传统的数据库处理方式,一般都是自己判断过期,这样无疑会严重影响项目性能。而有了过期机制,可以给他设定一个时间,到了时间就会把对应的缓存清理掉,大大简化了操作。

三种过期策略:

1.定时删除 :

含义:在设置key的过期时间的同时,为该key创建一个定时器,让定时器在key的过期时间来临时,对key进行删除

优点:保证内存被尽快释放

缺点:若过期key很多,删除这些key会占用很多的CPU时间,在CPU时间紧张的情况下,CPU不能把所有的时间用来做要紧的事儿,还需要去花时间删除这些key

定时器的创建耗时,若为每一个设置过期时间的key创建一个定时器(将会有大量的定时器产生),性能影响严重

2.惰性删除:

含义:key过期的时候不删除,每次从数据库获取key的时候去检查是否过期,若过期,则删除,返回null。

优点:删除操作只发生在从数据库取出key的时候发生,而且只删除当前key,所以对CPU时间的占用是比较少的,而且此时的删除是已经到了非做不可的地步(如果此时还不删除的话,我们就会获取到了已经过期的key了)

缺点:若大量的key在超出超时时间后,很久一段时间内,都没有被获取过,那么可能发生内存泄露(无用的垃圾占用了大量的内存)

3.定期删除:

含义:每隔一段时间执行一次删除(在redis.conf配置文件设置hz,1s刷新的频率)过期key操作

优点: 通过限制删除操作的时长和频率,来减少删除操作对CPU时间的占用--处理"定时删除"的缺点;定期删除过期key--处理"惰性删除"的缺点

缺点:在内存友好方面,不如"定时删除";在CPU时间友好方面,不如"惰性删除"

  • Redis的使用场景
    • 共享session
    • 队列
    • 处理过期项目
    • 按照用户投票和时间排序
    • 排行榜应用,取TOP N操作
    • 显示最新的项目列表
    • 存储部分变更数据,如用户信息等。
    • 常规key-value缓存应用。常规计数: 微博数, 粉丝数
  • Redis的持久化方案

RDB持久化:

该机制是指在指定的时间间隔内将内存中的数据集快照写入磁盘。

优点:1.只有一份rdb文件,可随时备份

           2.比AOF文件小,加载效率高

           3.只提供fork子进程,不阻塞主进程,IO操作比较少

AOF持久化:

该机制将以日志的形式记录服务器所处理的每一个写操作,在Redis服务器启动之初会读取该文件来重新构建数据库,以保证启动后数据库中的数据是完整的。

优点:1.每次改动同步数据安全性好

             2.APPEND方式追加日志,不会对旧日志文件产生影响

  • Redis的优缺点

优点:

(1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)

(2) 支持丰富数据类型,支持string,list,set,sorted set,hash

(3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行

(4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除。

缺点:

1 Redis不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。

2 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。

3 Redis的主从复制采用全量复制,复制过程中主机会fork出一个子进程对内存做一份快照,并将子进程的内存快照保存为文件发送给从机,这一过程需要确保主机有足够多的空余内存。若快照文件较大,对集群的服务能力会产生较大的影响,而且复制过程是在从机新加入集群或者从机和主机网络断开重连时都会进行,也就是网络波动都会造成主机和从机间的一次全量的数据复制,这对实际的系统运营造成了不小的麻烦。

4 Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。

  • Redis的集群

1.为什么redis集群中必须至少有三个节点?

一个节点挂了,有一半以上的节点通过ping-pong方式的投票机制认为它挂了,那么这个集群就挂了.但如果只有2个节点,有一个挂了,另一个确定它挂了但百分比只占到了50%,没有超过一半,这个集群也不会挂,所以至少需要3个节点;

2.为什么每个redis集群需要至少6台服务器?

因为redis集群至少需要三个节点,要保证集群的高可用,每个节点都要一个备份机.理论上也需要6台虚拟机

3.目的

高可用、负载均衡、性能提升

  • Redis的淘汰机制

举个例子:MySQL里有2000w数据,Redis中只存20w的数据,如何保证Redis中的数据都是热点数据?Redis内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。Redis提供 6种数据淘汰策略:

voltile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

no-enviction(驱逐):禁止驱逐数据

  • Redis的特点

Redis本质上是一个Key-Value类型的内存数据库,很像memcached,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存。因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB。

Redis的出色之处不仅仅是性能,Redis最大的魅力是支持保存多种数据结构,此外单个value的最大限制是1GB,不像 memcached只能保存1MB的数据,因此Redis可以用来实现很多有用的功能,比方说用他的List来做FIFO双向链表,实现一个轻量级的高性 能消息队列服务,用他的Set可以做高性能的tag系统等等。另外Redis也可以对存入的Key-Value设置expire时间,因此也可以被当作一 个功能加强版的memcached来用。

Redis的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。

  • Redis为什么把数据全部放到内存中

Redis为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘。所以Redis具有快速和数据持久化的特征。如果不将数据放在内存中,磁盘I/O速度会严重影响Redis的性能。在内存越来越便宜的今天,Redis将会越来越受欢迎。

如果设置了最大使用的内存,则数据已有记录数达到内存限值后不能继续插入新值。

  • redis相比memcached有哪些优势?

(1) memcached所有的值均是简单的字符串,Redis作为其替代者,支持更为丰富的数据类型

(2) Redis的速度比memcached快很多

(3) Redis可以持久化其数据

  • Memcache与Redis的区别都有哪些?

1)、存储方式

Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。

Redis有部份存在硬盘上,这样能保证数据的持久性。

2)、数据支持类型

Memcache对数据类型支持相对简单。

Redis有复杂的数据类型。

3)、使用底层模型不同

它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。

Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。

4),value大小

Redis最大可以达到1GB,而memcache只有1MB

  • 在实际使用中Redis可能会遇到的一些问题及解决方法

1 主库宕机

先来看一下主库宕机容灾过程:如下图

品优购第八天Redis理解

在主库宕机的时候,我们最常见的容灾策略为“切主”。具体为从该集群剩余从库中选出一个从库并将其升级为主库,该从库升级为主库后再将剩余从库挂载至其下成为其从库,最终恢复整个主从集群结构。

以上是一个完整的容灾过程,而代价最大的过程为从库的重新挂载,而非主库的切换。

这是因为Redis无法像mysql、mongodb那样基于同步的点位在主库发生变化后从新的主库继续同步数据。在Redis集群中一旦从库换主,Redis的做法是将更换主库的从库清空然后从新主库完整同步一份数据再进行续传。

整个从库重做流程是这样的:

1.主库bgsave自身数据到磁盘

2.主库发送rdb文件到从库

3.从库开始加载

4.加载完毕开始续传,同时开始提供服务

很明显,在这个过程中Redis的内存体积越大以上每一个步骤的时间都会被拉长,实际测试的数据如下:

品优购第八天Redis理解

可以看到,当数据达到20G的时候,一个从库的恢复时间已经被拉长到了将近20分钟,如果有10个从库那么如果依次恢复则共需200分钟,而如果此时该从库承担着大量的读取请求你能够忍受这么长的恢复时间吗?

看到这里你肯定会问:为什么不能同时重做所有从库?这是因为所有从库如果同时向主库请求rdb文件那么主库的网卡则立即跑满从而进入一个无法正常提供服务的状态,此时主库又死了,简直是雪上加霜。

当然,我们可以批量恢复从库,例如两两一组,那么全部从库的恢复时间也仅仅从200分钟降低到了100分钟,这不是五十步笑百步吗?

另一个重要问题在于第四点中的标红位置,续传可以理解为一个简化的mongodb的oplog,它是一个体积固定的内存空间,我们称之为“同步缓冲区”。

Redis主库的写入操作都会在该区域存放一份然后发送给从库,而如果在上文中1,2,3步耗时太久那么很可能这个同步缓冲区就被重写,此时从库无法找到对应的续传位置它会怎么办?答案是重做1,2,3步!

但因为我们无法解决1,2,3步的耗时因此该从库会永远的进入恶性循环:不停的向主库请求完整数据,结果对主库的网卡造成严重影响。

2. 扩容问题

很多时候会出现流量的突发性增长,通常在找到原因之前我们的应急做法就是扩容了。

而根据场景一中的表格,一个20G的Redis扩容一个从库需要将近20分钟,在这个紧急的时刻20分钟业务能够容忍吗?可能还没扩好就死翘翘了。

3 网络不好导致从库重做最终引发雪崩

该场景的最大问题是主库与从库的同步中断,而此时很可能从库仍然在接受写入请求,那么一旦中断时间过长同步缓冲区就很可能被复写。此时从库上一次的同步位置已丢失,在网络恢复后虽然主库没有发生变化但由于从库的同步位置丢失了从库必须进行重做,也就是问题一中的1,2,3,4步。如果此时主库内存体积过大那么从库重做速度就会很慢,而发送到从库的读请求就会受到严重影响,同时由于传输的rdb文件的体积过大,主库的网卡在相当长的一段时间内都会受到严重影响。

4 内存越大,触发持久化的操作阻塞主线程的时间越长

Redis是单线程的内存数据库,在Redis需要执行耗时的操作时,会fork一个新进程来做,比如bgsave,bgrewriteaof。 Fork新进程时,虽然可共享的数据内容不需要复制,但会复制之前进程空间的内存页表,这个复制是主线程来做的,会阻塞所有的读写操作,并且随着内存使用量越大耗时越长。例如:内存20G的Redis,bgsave复制内存页表耗时约为750ms,Redis主线程也会因为它阻塞750ms。

解决办法:

解决办法当然就是极力减少内存的使用了,一般情况下都是这么做的:

1 设置过期时间

对具有时效性的key设置过期时间,通过Redis自身的过期key清理策略来降低过期key对于内存的占用,同时也能够减少业务的麻烦,不需要定期清理了

2 不存放垃圾到Redis中

3 及时清理无用数据

例如一个Redis承载了3个业务的数据,一段时间后有2个业务下线了,那你就把这两个业务的相关数据清理了

4 尽量对数据进行压缩

例如一些长文本形式的数据,压缩能够大幅度降低内存占用

5 关注内存增长并定位大容量key

不管是DBA还是开发人员使用Redis就必须关注内存,这里可以分析Redis实例中哪些key比较大从而帮助业务快速定位异常key(非预期增长的key,往往是问题之源)

6 pika

如果实在不想搞的那么累,那就把业务迁移到新开源的pika上面,这样就不用太关注内存了,Redis内存太大引发的问题,那也都不是问题了。