1、redis是单线程的,为什么还这么快(除了内存)?
1)完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,
HashMap的优势就是查找和操作的时间复杂度都是O(1);
2)数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;
3)采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗
CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
4)使用多路I/O复用模型,非阻塞IO:
1.1什么是非阻塞IO:
1) 一个服务器进程和一个客户端进程通信,服 务器端read(sockfd1,bud,bufsize),此时客户
端进程没有发送数据,那么read(阻塞调用)将阻塞直到客户端调用write(sockfd,but,size)发来数据.
在一个客户和服务器通信时这没什么问题,当多个客户与服务器通信时,若服务器阻塞于其中一个客户sockfd1,当另一个
客户的数据到达套接字sockfd2时,服务器不能处理,仍然阻塞在read(sockfd1,...)上;此时
问题就出现了,不能及时处理另一个客户的服务
while ((n = read(fd_read, buf, SIZE)) > 0) if ( write(fd_write, buf, SIZE) != n) echo("write error") 2)IO多路转接技术的内容就是:线构造一张或多张描述符的表,然后调用一个函数,知道有表中的描述符准备好可以进行IO操作时,该函数才返回,在返回时告诉进程哪一个描述符已经准备好了,可以进行IO操作,对该描述符进行IO操作就不会阻塞.
2、redis分布式锁的问题?
SET my_key my_value NX PX milliseconds。
加锁代码满足我们可靠性里描述的三个条件:
首先,set()加入了NX参数,可以保证如果已有key存在,则函数不会调用成功,也就是只有一个客户端能持有锁,满足互斥性。
其次,由于我们对锁设置了过期时间,即使锁的持有者后续发生崩溃而没有解锁,锁也会因为到了过期时间而自动解锁(即key被删除),不会发生死锁。
最后,因为我们将value赋值为requestId,代表加锁的客户端请求标识,那么在客户端在解锁的时候就可以进行校验是否是同一个客户端。
===================解读========
SET my_key my_value NX PX milliseconds。
加锁:setNx 方法,如果key存在,则不会拿到锁。并且设置所得过期时间,不会发生死锁。
解锁:redis中key对应的value与之前设置的是否一致,防止解别人的锁;使用lua代码保证操作的原子性;
3、redis的五种数据类型,hash底层的数据结构是怎么样的?String在底层存储的是什么?
1)五种数据类型:String、List、Set、Hash、ZSet
2)Hash底层数据结构类型:
Redis的Hash实际是内部存储的Value为一个HashMap,并提供了直接存取这个Map成员的接口。
也就是说,Key仍然是用户ID, value是一个Map,这个Map的key是成员的属性名,value是属性值;
这样对数据的修改和存取都可以直接通过其内部Map的 Key(Redis里称内部Map的key为field),
也就是通过 key(用户ID) + field(属性标签) 就可以操作对应属性数据了,既不需要重复存储数据,
也不会带来序列化和并发修改控制的问题。
3)redis的String类型底层为二进制数据,因为任何类型数据都可以表示成二进制,redis的字符串可以存储任何类型的数据;
4、redis的持久化策略?
1)、快照(snapshots)
当符合一定条件时Redis会自动将内存中的数据进行快照并持久化到硬盘。n秒内m个key被修改。
快照模式并不十分健壮,当系统停止,或者无意中Redis被kill掉,最后写入Redis的数据就会丢失。
2)、aof(append only file)appendonly yes
①appendfsync no 由操作系统决定时机,一般30s一次;
②appendfsync everysec每秒扫描一次,有改变就持久化
③appednfsync always每个操作持久化
5、redis内存不够的策略有哪些?lru有哪几种方法? LRU驱动事件(LRU eviction)
volatile-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(驱逐):禁止驱逐数据
使用策略规则:
1)、如果数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率低,则使用allkeys-lru
2)、如果数据呈现平等分布,也就是所有的数据访问频率都相同,则使用allkeys-random
6、redis如何找到近期的key?
KEYS pattern 列出匹配的keys
scan pattern 不会导致线程的阻塞,
TTL key 当 key 不存在时,返回 -2;当 key 存在但没有设置剩余生存时间时,返回 -1;否则,以秒为单位,返回 key 的剩余生存时间
7、redis的集群结构有哪几种?哪个地方去记录了哪个hash槽的位置(cluster,管理集群的插件)?
1)哨兵:高可用特性都会受到Master主节点内存的限制。
哨兵的作用就是监控Redis系统的运行状况。它的功能包括以下两个。
①监控主数据库和从数据库是否正常运行。
②主数据库出现故障时自动将从数据库转换为主数据库。
哨兵模式的优缺点
优点:
主从可以切换,故障可以转移,系统可用性更好。哨兵模式是主从模式的升级,系统更健壮,可用性更高。
缺点:
高可用特性都会受到Master主节点内存的限制,Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。
2)集群
在redis官方给出的集群方案中,数据的分配是按照槽位来进行分配的,每一个数据的键被哈希函数映射到一个槽位,redis-3.0.0规定一共有16384个槽位,当然这个可以根据用户的喜好进行配置(slot-add)。当用户put或者是get一个数据的时候,首先会查找这个数据对应的槽位是多少,然后查找对应的节点,然后才把数据放入这个节点。这样就做到了把数据均匀的分配到集群中的每一个节点上,从而做到了每一个节点的负载均衡,充分发挥了集群的威力。
当其中一个节点node1执行set a a 命令时,redis 先对 key 使用 crc16 算法算出一个结果,
然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽(但一个hash槽会对应许多keys),redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。
set的步骤如下所示:①node1实行set key value ②cluster对key进行算法计算,并对16384取余数,得到插槽位置,比如node2 ③在node2上 set key value;
get的步骤如下①计算key值,得到槽值位于node2 ②跳转至node2 ③在node2上执行get key命令
优点:使用哈希槽的好处就在于可以方便的添加或移除节点。
当需要增加节点时,只需要把其他节点的某些哈希槽挪到新节点就可以了;
当需要移除节点时,只需要把移除节点上的哈希槽挪到其他节点就行了;在这一点上,我们以后新增或移除节点的时候不用先停掉所有的 redis 服务。
8、redis性能优化问题
(1)Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件;(Master写内存快照,save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以Master最好不要写内存快照;AOF文件过大会影响Master重启的恢复速度)
(2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次
(3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内
(4) 尽量避免在压力很大的主库上增加从库
(5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3...;这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。
9、
redis.conf中设置:
端口号port:
后台启动daemonize:yes
主从启动:slaveof
10、缓存穿透是什么,你认为该如何解决这个问题?
缓存穿透,是指查询一个数据库一定不存在的数据。
缺点:假如有恶意攻击,就可以利用这个漏洞,对数据库造成压力,甚至压垮数据库。
解决:缓存空值;
缓存雪崩,是指在某一个时间段,缓存集中过期失效。
缺点:对于数据库而言,就会产生周期性的压力波峰。
解决:不同的分类设置不同的过期时间;
缓存击穿,是指某一个非常热点的key瞬间失效,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
缺点:主打的商品永不过期
11、Redis客户端
Redis Client
12、LPOP和BLPOP的区别
LBPOP支持多个key,也就是说可以同时监听多个list,并按照key的
LPOP 删除并返回删除值
13、Redis中的lua用过没,可以用来做什么,为什么可以这么用?
注意一点 lua脚本是一个原子性的操作,只有当lua脚本执行完毕之后,redis的其他操作才能正常执行。
可以用来分布式事务中解锁操作;加锁过程中setnx操作,保证key已经存在,那么函数不会调用成功;
解锁呢,需要两步来实现:首先判断key对应的value与之前是否一致,然后一致解锁;用lua可以保证操作的原子性,防止解了别人的锁;
14、Redis的pipeline用来做什么?
pipeline就是把一组命令进行打包,然后一次性通过网络发送到Redis。同时将执行的结果批量的返回回来;节约发送的往返时间;
15、redis MemCached CasSandra的区别
(1) memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型
(2) redis的速度比memcached快很多
(3) redis可以持久化其数据