redis和memcached什么区别?为什么高并发下有时单线程的redis比多线程的memcached效率要高?
区别:
1 me可缓存图片和视频。rd支持除k/v更多的数据结构;
2. rd可以使用虚拟内存,rd可持久化和aof灾难恢复,rd通过主从支持数据备份**;**
3. rd可以做消息队列。
原因**:me**多线程模型引入了缓存一致性和锁,加锁带来了性能损耗
Redis为什么那么快
1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);
2、数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;
3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
4、使用多路I/O复用模型,非阻塞IO;
5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
(1)多路 I/O 复用模型
多路I/O复用模型是利用 select、poll、epoll 可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。
**这里“多路”指的是多个网络连接 ,“复用”指的是复用同一个线程。**采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗),且 Redis 在内存中操作数据的速度非常快,也就是说内存内的操作不会成为影响Redis性能的瓶颈,主要由以上几点造就了 Redis 具有很高的吞吐量。
Pipeline有什么好处,为什么要用pipeline?
可以将多次IO往返的时间缩减为一次,前提是pipeline执行的指令之间没有因果相关性。使用redis-benchmark进行压测的时候可以发现影响redis的QPS峰值的一个重要因素是pipeline批次指令的数目。
Redis中的Multi和Pipleline都可以一次性执行多个命令,但是Pipeline只是把多个redis指令一起发出去,redis并没有保证这些指令执行的顺序,且减少了多次网络传递的开销,因而其执行效率很高;Multi相当于一个redis的transaction,保证整个操作的有序性,通过watch这些key,可以避免这些key在事务的执行过程中被其它的命令修改,从而导致得的到结果不是所期望的。
redis主从复制如何实现的?redis的集群模式如何实现?redis的key 是如何寻址的?
主从复制实现**:主节点将自己内存中的数据做一份快照,将快照发给从节点,从节点将数据恢复到内存中。之后再每次增加新数据的时候,主节点以类似于mysql**的二进制日志方式将语句发送给从节点,从节点拿到主节点发送过来的语句进行重放。
分片方式:
客户端分片
基于代理的分片
Twemproxy
codis
路由查询分片
Redis-cluster(本身提供了自动将数据分散到Redis Cluster不同节点的能力,整个数据集合的某个数据子集存储在哪个节点对于用户来说是透明的**) redis-cluster分片原理:Cluster中有一个16384长度的槽(虚拟槽),编号分别为0-16383**。每个Master节点都会负责一部分的槽,当有某个key被 映射到某个Maste「负责的槽,那么这个Maste「负责为这个key提供服 务,至于哪个Master节点负责哪个槽,可以由用户指定,也可以在初始 化的时候自动生成,只有Master才拥有槽的所有权。Master节点维护着 一个16384/8字节的位序列,Master节点用bit来标识对于某个槽自己是 否拥有。比如对于编号为1的槽,Master只要判断序列的第二位(索引从 ◦开始)是不是为1即可。这种结构很容易添加或者删除节点。比如如果我 想新添加个节点D,我需要从节点A、B、C中得部分槽到D上。
集群模式:
- 主从复制模式:在主从复制模式中,一个 Redis 实例充当主节点,其他实例充当从节点。主节点负责写入数据,从节点负责读取数据。主节点发生故障时,从节点可以自动晋升为主节点。
- 哨兵模式:在哨兵模式中,多个哨兵实例共同监控主节点和从节点的状态。当主节点发生故障时,哨兵会自动选举一个从节点为新的主节点。
- 集群模式:在集群模式中,多个 Redis 实例组成一个集群。数据会均匀分布在各个节点上。集群模式可以有效提高 Redis 的性能和可用性。
在单机模式下,Redis 使用哈希表来存储 key-value 数据。哈希表是根据 key 的哈希值来进行寻址的。
使用redis如何设计分布式锁?说一下实现思路?使用zk可以吗?如何实现?这两种有什么区别?
redis:
我们加锁就一行代码:jedis.set(String key, String value, String nxxx, String expx, int time)
,这个set()方法一共有五个形参:
- 第一个为key,我们使用key来当锁,因为key是唯一的。
- 第二个为value,我们传的是requestId,很多童鞋可能不明白,有key作为锁不就够了吗,为什么还要 用到value?原因就是我们在上面讲到可靠性时,分布式锁要满足第四个条件解铃还须系铃人,通过给value赋值为requestId,我们就知道这把锁是哪个请求加的了,在解锁的时候就可以有依据。requestId可以使用
UUID.randomUUID().toString()
方法生成。 - 第三个为nxxx,这个参数我们填的是NX,意思是SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作;
- 第四个为expx,这个参数我们传的是PX,意思是我们要给这个key加一个过期的设置,具体时间由第五个参数决定。
- 第五个为time,与第四个参数相呼应,代表key的过期时间。
总的来说,执行上面的set()方法就只会导致两种结果:1. 当前没有锁(key不存在),那么就进行加锁操作,并对锁设置个有效期,同时value表示加锁的客户端。2. 已有锁存在,不做任何操作。
上面是高版本的redis的功能,如果是低版本的:
1:SETNX value 值=当前时间+过期超时时间,返回1 则获得锁,返回0则没有获得锁。转2。
2:GET 获取 value 的值 。判断锁是否过期超时。如果超时,转3。
3:**GETSET(将给定 key 的值设为 value ,并返回 key 的旧值),**GETSET value 值=当前时间+过期超时时间, 判断得到的value 如果仍然是超时的,那就说明得到锁,否则没有得到锁。
从2并发进到3 的操作,会多次改写超时时间,但这个不会有什么影响。
解锁:
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
可以看到,我们解锁只需要两行代码就搞定了!第一行代码,我们写了一个简单的Lua脚本代码,上一次见到这个编程语言还是在《黑客与画家》里,没想到这次居然用上了。第二行代码,我们将Lua代码传到jedis.eval()
方法里,并使参数KEYS[1]赋值为lockKey,ARGV[1]赋值为requestId。eval()方法是将Lua代码交给Redis服务端执行。
那么这段Lua代码的功能是什么呢?其实很简单,首先获取锁对应的value值,检查是否与requestId相等,如果相等则删除锁(解锁)。那么为什么要使用Lua语言来实现呢?因为要确保上述操作是原子性的。。那么为什么执行eval()方法可以确保原子性,源于Redis的特性,下面是官网对eval命令的部分解释:
简单来说,就是在eval命令执行Lua代码的时候,Lua代码将被当成一个命令去执行,并且直到eval命令执行完成,Redis才会执行其他命令。
zk:
1. 客户端对某个方法加锁时,在zk上的与该方法对应的指定节点的目录 下,生成一个唯一的瞬时有序节点nodel;
2. 客户端获取该路径下所有已经创建的子节点,如果发现自己创建的 nodel的序号是最小的,就认为这个客户端获 得了锁。
3. 如果发现nodel不是最小的,则监听比自己创建节点序号小的最大的节点,进入等待。
4. 获取锁后,处理完逻辑,删除自己创建的nodel即可。区别**:zk**性能差 一些,开销大,实现简单。
知道redis的持久化吗?底层如何实现的?有什么优点缺点?
RDB(Redis DataBase:在不同的时间点将redis的数据生成的快照同步到磁 盘等介质上**):内存到硬盘的快照,定期更新。缺点:耗时,耗性能(fork+io操 作)**,易丟失数据。
AOF(Append Only File:将redis所执行过的所有指令都记录下来,在下次 redis重启时,只需要执行指令就可以了**):**写日志。缺点:体积九恢复速度慢。
bgsave做镜像全量持久化,aof做增量持久化。因为bgsave会消耗比较 长的时间,不够实时,在停机的时候会导致大量的数据丟失,需要aof来配合,在redis实例重启时,优先使用aof来恢复内存的状态,如果没有aof日志,就会使用rdb文件来恢复。Redis会定期做aof重写,压缩aof文件日志大小。Redis4.0之后有了混合持久化的功能,将bgsave