Redis
Posted baoziy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis相关的知识,希望对你有一定的参考价值。
- 为什么使用Redis?
Redis是一个高性能基于key/value存储数据的分布式缓存数据库,基于内存运行并且支持持久化的NoSQL数据库。
根据CAP理论(强一致性、可用性和分区容错性),一个分布式系统不可能同时很好的满足这三个要求,最多只能较好的满足其中两个要求。那么分布式系统中分区容错性是必不可少的,而且结合当下用户体验至上的理念,系统可用性是任何一个系统都必须要满足的,所以只能让系统放松对某一时刻数据一致性要求来换取系统整体伸缩性和性能上的改观。所以就需要使用redis这样的NOSQL数据库来实现对数据的保存和操作。
- Redis的三个重要特点:
l Redis支出数据的持久化,可以将内存中的数据写进磁盘,还可以再次加在进磁盘继续使用
l Redis不仅仅支持简单的Key-value类型的数据,还支持list、set、哈希、以及zset等数据结构的存储
l Redis支持Master-Slaver模式的数据备份
- Redis的优点与缺点
l 读写速度快,因为数据都是在内存中存放的;
l 支持丰富的数据类型;
l 所有的操作都是原子操作,部分支持事务
l 丰富的特性:可用于缓存、消息或者按Key设置过期时间等
- Redis的数据类型、底层实现以及各种数据类型的使用场景
Redis的数据类型一共有五种:
数据类型 |
底层实现 |
特点 |
String |
就是String类型的底层实现 |
String是Redis中最基本的数据类型,单值单value的模式使用,Redis中的String可以包含任何数据类型 |
List |
底层实现是一个链表 |
List的单值多value的模式,其实就是一个简单的字符串列表 |
Set |
底层是通过HashTable实现了的 |
Set也是单值多value的模式,只是里边存的是String的无序集合 |
Hash |
就类似于Java中的map集合的底层实现 |
Hash也是kv的模式,只是这里的v也是以键值对形式存在的,Hash类型特别适合存储对象,类似于Java中的Map<String,Object> |
Sort set(Zset) |
|
这是一个有序集合,它和Set一样也是存的String类型的集合且不允许有重复元素,不同的是每个元素都会关联一个分数(可以重复),Zset就是通过这个分数来对数据进行排序 |
- Redis为什么这么快?
(1)完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。
(2)数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;
(3)采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
(4)使用多路I/O复用模型,非阻塞IO;
(5)使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
- Redis是单线程吗?
Redis是一个分布式缓存服务器基于内存运行,如果是多线程的话,在操作数据的时候涉及到数据安全问题就要为数据进行加锁。虽然使用多线程可以在一定程度上提高系统的性能,但是每一个线程的执行效率都会下降,而且会导致程序的逻辑严重复杂化,并且Redis数据库不单单支持Key-value类型的数据,还有list、set等复杂的数据结构,这些数据结构有可能会进行更细粒度的操作,这样的操作就需要加更多的锁,导致的结果就是系统开销大大增加。
Redis在权衡各方面的利弊之后选择使用单线程,这样就能突出自己功能的灵活性,单线程中任何复杂的数据操作都可以被准确高效的执行。
- Redis持久化的方式
Redis有两种持久化策略,分别是RDB和AOF。
RDB:是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。这个过程主进程是不参与的,这样就确保了Redis有一个极高的性能。(RDB适合大规模数据恢复并且对数据的完整性和一致性要求不高的情况)
AOF:是以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。
如果同时开启两种持久化方式:
AOF采用的是文件追加方式,这样就导致文件会越来越大,那么当文件的大小超过了设定的阈值的时候,redis就会启动文件压缩,只保留可以恢复数据的最小指令集。这个就是Redis中针对AOF的文件重写机制。
- Redis的事务
Redis对事务的支持是部分支持。如果在写批处理命令的时候系统显示error错误,那么这一批命令都不会被执行,但是如果批处理命令只有在执行的时候才判断出来错误,那么这时候剩余的命令会正常执行,只有那个错误的命令不会被执行。
- 谈谈Redis的过期策略以及内存淘汰机制
- Redis的复制问题(Master/Salver)?
Redis的主从复制问题主要包括两部分:Master主要负责写、Salver主要负责读。具体在使用的时候主要有一些几点需要注意:
三种方法的详细介绍:
(1) 其中主从复制是最常用的一种,一个主机,若干个从机,刚开始的时候redis默认都是master,但是可以通过命令把某一个redis变成另一个redis 的一个从机,redis只要是变成从机,那么第一次从机会把主机中的全部内容都复制一份到自己的数据库中;因为是读写分离,主机负责写,从机负责读,如果从机写一条在主机中已经存在的数据,那么是写不进去的会报错无异常。主机如果挂掉,从机会原地等待,继续保持是从机的角色,只是和主机的连接状态发生变化。如果是从机挂掉,那么如果该从机的配置没有写进配置文件中,机器重新启动之后就断开了与主机的关系。
这样做的缺点就是中心化太严重,主机的抗干扰能力不够强 。
(2)去中心化减轻master的负担压力:薪火相传
这种情况是一个一个串下去,当前redis是前一个的slaver,同时也是后一个的master。
(3)反客为主:一主二仆的情况下主机挂掉,那么可以再两个从机中使用命令把其中一个主机变成master,另一个从机变成当前主机的从机,但是原先挂掉的主机在启动之后 就被独立出来了,和这两个redis没有任何关系了
Redis的复制原理:
目前比较高级的用法:哨兵模式:其实就是反客为主的自动版,能够自动的监控后台的主库是否发生了故障,如果发生故障就通过投票的方式自动选举出一个从库升级为主库。如果之前的master回来了,但是他以前是主机,但它变成从机了,因为其他的从机已经自动的自己通过投票重新选出了master。因为哨兵模式是一直在监视着当前的主机,并且关注着主机下边的从机,只要主机挂掉立马通过投票选出新的master。
- 如何应对缓存穿透与缓存雪崩的问题?
|
定义 |
解决办法 |
缓存穿透 |
缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时才会写入缓存,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。 |
是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力; 另外一种方法,如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。 |
缓存雪崩 |
缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。 |
缓存失效时的雪崩效应对底层系统的冲击非常可怕。大多数系统设计者考虑用加锁或者队列的方式保证缓存的单线程操作,从而避免失效时大量的并发请求落到底层存储系统上。其实可以在原有的失效时间基础上增加一个随机值,这样缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。 |
缓存击穿 |
对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题,这个和缓存雪崩的区别在于这里针对某一key缓存,前者则是很多key。 缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。 |
1.使用互斥锁(mutex key):就是在缓存失效的时候,不是立即去底层数据库取值,而是先使用缓存工具的某些带成功操作返回值的操作去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。加锁排队只是为了减轻数据库的压力,并没有提高系统吞吐量; 2、在value内部设置1个超时值(timeout1), timeout1比实际的memcache timeout(timeout2)小。当从cache读取到timeout1发现它已经过期时候,马上延长timeout1并重新设置到cache。然后再从数据库加载数据并设置到cache中 |
- 常见的缓存算法:
FIFO算法:First in First out,先进先出。原则:当缓存满的时候一个数据最先进入缓存中,则应该最早淘汰掉。
LFU算法:Least Frequently Used,最不经常使用算法。
LRU算法:Least Recently Used,最近最少使用算法;
LRU和LFU的区别。LFU算法是根据在一段时间里数据项被使用的次数选择出最少使用的数据项,即根据使用次数的差异来决定。而LRU是根据使用时间的差异来决定的
- 如何解决Redis的并发竞争问题
Redis的并发竞争问题其实主要发生在写并发竞争,解决办法主要包括:
1、使用redis自带的自增操作命令
2、使用独占锁或者乐观锁的方法进行并发控制
4、在客户端进行控制,当针对同一个Key进行操作的时候进行加锁
以上是关于Redis的主要内容,如果未能解决你的问题,请参考以下文章