Redis

什么是redis?(Remote Dictionary Server(远程数据服务))

非阻塞多路复用:
又被称为事件驱动,reactor模式
下面举一个例子,模拟一个tcp服务器处理30个客户socket。
假设你是一个老师,让30个学生解答一道题目,然后检查学生做的是否正确

你站在讲台上等,谁解答完谁举手。这时C、D举手,表示他们解答问题完毕,你下去依次检查C、D的答案,然后继续回到讲台上等。此时E、A又举手,然后去处理E和A。。。 这种就是IO复用模型,Linux下的select、poll和epoll就是干这个的。将用户socket对应的fd注册进epoll,然后epoll帮你监听哪些socket上有消息到达,这样就避免了大量的无用操作。此时的socket应该采用非阻塞模式。

kv存储系统,value可以是String,Map,list,sets和sorted sets(zset)等类型
String作为value最大不能超过512MB
List是基于LinkedList实现的(不用数组是因为要能非常快的在很大列表上添加元素)(实现生产者/消费者模型)
hash(放入的数量没有限制)
Set(是String的无序排列)(适用于表示对象间的关系)

key都会自动创建和删除

redis有哪几种数据淘汰策略?

(redis中当内存超过限制时,按照配置的策略,淘汰掉相应的kv,使得内存可以继续留有足够的空间保存新的数据。redis 确定驱逐某个键值对后,会删除这个数据并将这个数据变更消息发布到本地(AOF 持久化)和从机(主从连接)。)
1.volatile-lru:从设置了过期时间的数据集中,选择最近最久未使用的数据释放;
2.allkeys-lru:从数据集中(包括设置过期时间以及未设置过期时间的数据集中),选择最近最久未使用的数据释放;
3.volatile-random:从设置了过期时间的数据集中,随机选择一个数据进行释放;
4.allkeys-random:从数据集中(包括了设置过期时间以及未设置过期时间)随机选择一个数据进行释放;
5.volatile-ttl:从设置了过期时间的数据集中,选择马上就要过期的数据进行释放操作;
6.noeviction:不删除任意数据(但redis还会根据引用计数器进行释放),这时如果内存不够时,会直接返回错误。(默认)

redis持久化方法:(AOF与RDB)

RDB的主要原理就是在某个时间点把内存中所有数据保存到磁盘文件中
对于“把内存中的数据转存到磁盘中”这一过程
1.如何落地存储协议?也就是如何设计一个存储协议可以兼容Redis丰富的数据类型(string、hash、list、set、zset)。
2.如何进行数据压缩?通常一个Redis服务器存放了大量数据,如果要将这些数据转存到文件中,如何进行数据压缩,也就是如何尽可能减少文件大小以减轻系统IO压力和节省磁盘空间。
3.如何解决数据转存时服务器阻塞问题?Redis是单线程的,当服务器在进行数据转存操作时就无法对外服务,如何避免这个问题?

save和bgsave
save是在Redis进程中执行的,由于Redis是单线程实现,所以当save命令在执行时会阻塞Redis服务器一直到该命令执行完成为止。

与save命令不同的是,bgsave命令会先fork出一个子进程,然后在子进程中生成RDB文件。由于在子进程中执行IO操作,所以bgsave命令不会阻塞Redis服务器进程,Redis服务器进程在此期间可以继续对外提供服务。
为了提高性能,Redis服务器在bgsave命令执行期间会拒绝执行新到来的其它bgsave命令。
AOF持久化则采用了不同的策略,它将所有写操作相关的命令记录到磁盘文件中。一般对Redis的操作命令可以分为“读命令”和“写命令”两种,只有写命令才会改变数据的状态,所以AOF持久化机制将服务器所执行的写命令记录下来,当系统崩溃时,只要重新执行记录在AOF文件中的写命令就可以将数据库还原成原来的状态。从这个角度看,Redis的AOF机制有点类似于log(记日志)的过程。

AOF缓冲区,也就是说当Redis执行一条写命令后,先将该命令追加到AOF缓冲区中,在以后的某个时刻再将AOF缓冲区中的内容同步到文件中。

Redis服务器在每个事件循环都将AOF缓冲区server.aof_buf中的数据写入AOF文件中,且每秒执行一次AOF文件同步操作。该模式效率和安全性(如果机器崩溃只丢失前一秒处理的新数据)比较适中,是Redis的默认同步策略。(AOF_FSYNC_EVERYSEC)

Redis服务器在每个事件循环都将AOF缓冲区server.aof_buf中的数据写入AOF文件中,且执行一次AOF文件同步操作。该模式速度最慢(每个事件循环都要执行同步操作)但也最安全(如果机器崩溃只丢失当前事件循环中处理的新数据)。(AOF_FSYNC_ALWAYS)

Redis服务器在每个事件循环都将AOF缓冲区server.aof_buf中的数据写入AOF文件中,但不执行同步fsync方法,由操作系统决定何时同步。该模式速度最快(无需执行同步操作)但也最不安全(如果机器崩溃将丢失上次同步后的所有数据)。(AOF_FSYNC_NO)

redis集群

投票机制:
投票过程是集群中所有master参与,如果半数以上master节点与master节点通信超时(cluster-node-timeout),认为当前master节点挂掉.

1.Redis官方集群方案 Redis Cluster
主从节点,一个主节点,n个从节点,如果主节点失效,会从slave节点中选择一个升级为主节点
对于客户端来说,整个Cluster被看作是一个整体,客户端可以链接任意一个node进行操作,就像操作单一Redis实例一样,当客户端操作的key没有分配到该node时,Redis会返回转向指令,指向另一个可用的node

2.Redis Sharding集群
采用哈希算法将Redis数据的key进行散列,通过hash函数,特定的key会映射到特定的redis节点上

3.利用代理中间件实现大规模Redis集群(twemproxy也叫nutcracker)
降低客户端直接连接后对服务器的链接数量,但性能降低20%

4.Redis Sentinel哨兵模式(监控master)
当redis在做master-slave的高可用方案时,假如master宕机了,redis本身(以及其很多客户端)都没有实现自动进行主备切换,而redis-sentinel本身也是独立运行的进程,可以部署在其他与redis集群可通讯的机器中监控redis集群。
1、不时地监控redis是否按照预期良好地运行;
2、如果发现某个redis节点运行出现状况,能够通知另外一个进程(例如它的客户端);
3、能够进行自动切换。当一个master节点不可用时,能够选举出master的多个slave(如果有超过一个slave的话)中的一个来作为新的master,其它的slave节点会将它所追随的master的地址改为被提升为master的slave的新地址。
4、哨兵为客户端提供服务发现,客户端链接哨兵,哨兵提供当前master的地址然后提供服务,如果出现切换,也就是master挂了,哨兵会提供客户端一个新地址。
单个哨兵会存在自己挂掉而无法监控整个集群的问题,所以哨兵也是支持集群的,我们通常用三台哨兵机器来监控一组redis集群。

RDB的优缺点
优:不会进行任何IO操作,保证了Redis的高性能
缺:间隔一段时间进行持久化,持久化时,产生故障,造成数据丢失

AOF的优缺点
优:保证数据的完整性
缺:AOF文件比RDB文件大,且恢复速度慢

主从复制模式
存RDB快照和在RDB快照期间产生的命令缓存起来一起发给从数据库
主从复制是异步的,保证的是最终一致性

写操作丢失
当一个写到达master,master已经回复client,但是master还没来的及复制到slave就宕机了,那么这个写操作就会丢失。

修改配置不重启Redis会实时生效吗?实时生效
redis 127.0.0.1:6379> config get maxmemory
1) “maxmemory”
2) “3221225472”
redis 127.0.0.1:6379> config set maxmemory 4294967296
OK

redis与memcached的区别????

redis可扩展1000个节点

redis常见性能问题和解决方案:

(1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件

(2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次

(3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内

(4) 尽量避免在压力很大的主库上增加从库

(5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3…

这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。

redis适用场景:

1.少量数据存储,高速读写访问,它使用内存提供主存储支持,而仅使用硬盘做持久性的存储
2.那些你现有的数据库处理起来感到缓慢的任务

BSD协议:给与使用者很大自由的协议,可以自由的使用,修改源代码,也可以将修改后的代码作为开源或者专有软件再发布

用作存储用还是缓存:
尽管数据分区对于Redis来说无论是数据持久化存储还是缓存,在概念上都是一样的,然而对于数据持久化存储还是有一个很大的限制。当我们使用Redis来作为持久化存储的时候,每一个key必须一直被映射到同一个Redis实例。
而当Redis被当做缓存使用的时候,对于这个key,如果一个实例不能用了,这个key还可以被映射到其他的实例中。

缓存穿透

查询一个不存在的数据,导致这个不存在的数据每次都要求存储层去查询,失去了缓存的意义,流量大时,可能DB就挂了
如果有人利用这个不存在的key频繁攻击,这就是漏洞
解决方案:
1.将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据就会被这个bitmap拦截掉,从而避免了对底层存储的查询压力
2.简单粗暴,如果一个查询返回的数据为空,仍然把这个结果缓存,但是它的过期时间设置的比较短

缓存雪崩
设置缓存时间采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬间压力过重雪崩
解决方案:
1.将缓存的失效时间分散开,失效时间基础上加上一个随机值,使重复率降低

缓存击穿
一些设置过期时间的key,恰好在这个时间点对这个key有大量请求,这些请求发现缓存过期一般会从DB加载数据并设回到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮
解决方案:
1.使用互斥锁(存在死锁风险,线程池阻塞)
简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。
public String get(key) {
String value = redis.get(key);
if (value == null) { //代表缓存值过期
//设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db
if (redis.setnx(key_mutex, 1, 3 * 60) == 1) { //代表设置成功
value = db.get(key);
redis.set(key, value, expire_secs);
redis.del(key_mutex);
} else { //这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
sleep(50);
get(key); //重试
}
} else {
return value;
}
}
2.提前使用互斥锁
在value内部设置1个超时值(timeout1), timeout1比实际的memcache timeout(timeout2)小。当从cache读取到timeout1发现它已经过期时候,马上延长timeout1并重新设置到cache。然后再从数据库加载数据并设置到cache中。

3.永远不过期(缺点:构建缓存的时候,其余线程可能访问的是老数据)
从功能上看,如果不过期,那不就成静态的了吗?所以我们把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建,也就是“逻辑”过期

4.资源保护????
采用netflix的hystrix,可以做资源的隔离保护主线程池

Redis是单线程的,如何提高多核CPU的利用率?
要发挥多核CPU性能,可以通过在单机开多个Redis core实例来完善,一样实现分布式;
实现读写分离
组成master-master或者master-slave。

Consistent hashing实现通常使得当一个key被映射到的实例不能用的时候将这个key映射到其他实例成为可能。类似,如果增加了一台机器,一部分的key将会被映射到这台新的机器上,我们需要了解的两点如下:

如果Redis被用来当做缓存,且要求容易增加或删除机器,使用consistent hashing是非常简单的。
如果Redis被用来当做(持久)存储,一个固定的key到实例的映射是需要的,因此我们不能够再灵活的添加或删除机器。否则,我们需要在增加或删除机器的时候系统能够rebalace,当前Redis Cluster已经支持。

Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
Redis支持数据的备份,即master-slave模式的数据备份。

所有操作都是原子性,支持事务

redis与其他kv存储有什么不同?
redis的数据类型基于基本类型结构,无需进行额外的抽象
运行在内存中,但也可以持久化到磁盘中
发布/订阅(pub/sub)
发布者发送消息,订阅者接受消息
redis客户端可以订阅任意数量的频道

redis事务:

3个阶段(不支持回滚,事务执行时会阻塞其他客户端的请求执行)
1.事务开始
2.命令入队
3.事务执行

原子性
分2种情况:
1.当执行的指令被服务器拒绝的时候,所以事务中的所有命令都不会执行,因为
前面我们有介绍到,redis的事务命令是统一先放到事务队列里,在用户输入EXEC命令的时候再统一执行。
但是我们错误的使用”GET”命令,在命令
放入事务队列的时候被检测到事务,这时候还没有接收到EXEC命令,所以全部不执行
redis > MULTI
OK

redis > SET pwd 123456
QUEUED

redis > GET
(error) ERR wrong number of arguments for ‘get’ command

redis > GET username
QUEUED

redis > EXEC
(error) EXECABORT Transaction discarded because of previous errors
2.与传统型数据库的事务不同的是:不支持回滚事务,即使队列中的操作发生错误,也会继续执行后续的队列中的命令

一致性:
事务具有一致性指的是,如果数据库在执行事务之前是一致的,那么在事务执行之后,无论事务是否执行成功,数据库也应该仍然一致的。
”一致“指的是数据符合数据库本身的定义和要求,没有包含非法或者无效的错误数据。redis通过谨慎的错误检测和简单的设计来保证事务一致性。

隔离性:(因为redis是单线程)
事务的隔离性指的是,即使数据库中有多个事务并发在执行,各个事务之间也不会互相影响,并且在并发状态下执行的事务和串行执行的事务产生的结果完全
相同。
因为redis使用单线程的方式来执行事务(以及事务队列中的命令),并且服务器保证,在执行事务期间不会对事物进行中断,因此,redis的事务总是以串行
的方式运行的,并且事务也总是具有隔离性的
持久性:
事务的耐久性指的是,当一个事务执行完毕时,执行这个事务所得的结果已经被保持到永久存储介质里面。
因为redis事务不过是简单的用队列包裹起来一组redis命令,redis并没有为事务提供任何额外的持久化功能,所以redis事务的耐久性由redis使用的模式决定
当服务器在无持久化的内存模式下运行时,事务不具有耐久性,一旦服务器停机,包括事务数据在内的所有服务器数据都将丢失
当服务器在RDB持久化模式下运作的时候,服务器只会在特定的保存条件满足的时候才会执行BGSAVE命令,对数据库进行保存操作,并且异步执行的BGSAVE不
能保证事务数据被第一时间保存到硬盘里面,因此RDB持久化模式下的事务也不具有耐久性
当服务器运行在AOF持久化模式下,并且appedfsync的选项的值为always时,程序总会在执行命令之后调用同步函数,将命令数据真正的保存到硬盘里面,因此这种配置下的事务是具有耐久性的。
当服务器运行在AOF持久化模式下,并且appedfsync的选项的值为everysec时,程序会每秒同步一次命令数据到磁盘因为停机可能会恰好发生在等待同步的那一秒内,这种可能造成事务数据丢失,所以这种配置下的事务不具有耐久性

redis客户端连接
1.客户端 socket 会被设置为非阻塞模式,因为 Redis 在网络事件处理上采用的是非阻塞多路复用模型。
2.为这个 socket 设置 TCP_NODELAY 属性,禁用 Nagle 算法
3.创建一个可读的文件事件用于监听这个客户端 socket 的数据发送

通常情况下的客户端/服务端
客户端向服务端发送一个查询请求,并监听Socket返回,通常是以阻塞模式,等待服务端响应。
服务端处理命令,并将结果返回给客户端。
但是redis管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应

分区:(水平分区与垂直分区)
分区是分割数据到多个Redis实例的处理过程,因此每个实例只保存key的一个子集。
水平分区:通过某个属性进行分区,譬如按年份
垂直分区:把不经常使用的列,划分到另一个分区

分割数据到多个Redis实例的过程,每个实例只保存key的一个子集

redis分区:
优点:1.通过利用多台计算机内存的和值,允许我们构造更大的数据库。
2.通过多核和多台计算机,允许我们扩展计算能力;通过多台计算机和网络适配器,允许我们扩展网络带宽
缺点:1.涉及多个key的redis事务不能使用
2.涉及多个key的操作通常是不被支持的。举例来说,当两个set映射到不同的redis实例上时,你就不能对这两个set执行交集操作。
3.当使用分区时,数据处理较为复杂,比如你需要处理多个rdb/aof文件,并且从多个实例和主机备份持久化文件
4.增加或删除容量也比较复杂。redis集群大多数支持在运行时增加、删除节点的透明数据平衡的能力,但是类似于客户端分区、代理等其他系统则不支持这项特性。然而,一种叫做presharding的技术对此是有帮助的。

Redis 提供了接口(hgetall)可以直接取到全部的属性数据,但是如果内部 Map 的成员很多,那么涉及到遍历整个内部 Map 的操作,由于 Redis 单线程模型的缘故,这个遍历操作可能会比较耗时,而另其它客户端的请求完全不响应,这点需要格外注意。 Redis服务端是个单线程的架构,不同的Client虽然看似可以同时保持连接,但发出去的命令是序列化执行的,这在通常的数据库理论下是最高级别的隔离

发布了125 篇原创文章 · 获赞 36 · 访问量 10万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章