这篇博客的内容主要包括:
1,redis的线程模型是什么?
Redis确实是单进程单线程的模型,因为Redis完全是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章的采用单线程的方案了(毕竟采用多线程会有很多麻烦)
==>Redis是单线程的,为什么还能这么快吗?
第一:Redis完全基于内存,绝大部分请求是纯粹的内存操作,非常迅速,数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度是O(1)。
==>Redis将所有数据放在内存中,非数据同步正常工作中,是不需要从磁盘读取数据的,0次IO。内存响应时间大约为100纳秒,这是Redis速度快的重要基础
第二:数据结构简单,对数据操作也简单。
第三:采用单线程,避免了不必要的上下文切换和竞争条件,不存在多线程导致的CPU切换,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有死锁问题导致的性能消耗。
第四:使用多路复用IO模型,非阻塞IO
–>I/O多路复用实际上是指**多个连接的管理可以在同一进程。多路是指网络连接,复用只是同一个线程**。在网络服务中,I/O多路复用起的作用是一次性把多个连接的事件通知业务代码处理,处理的方式由业务代码来决定。
在I/O多路复用模型中,最重要的函数调用就是I/O 多路复用函数,该方法能同时监控多个文件描述符(fd)的读写情况,当其中的某些fd可读/写时,该方法就会返回可读/写的fd个数。
Redis使用epoll作为I/O多路复用技术的实现,再加上Redis自身的事件处理模型将epoll的read、write、close等都转换成事件,不在网络I/O上浪费过多的时间。实现对多个FD读写的监控,提高性能。
举个形象的例子吧。比如一个tcp服务器处理20个客户端socket。
A方案:顺序处理,如果第一个socket因为网卡读数据处理慢了,一阻塞后面的都完了。。。
B方案:每个socket请求都创建一个分身子进程来处理,不说每个进程消耗大量系统资源,光是进程切换就够操作系统累的了。
C方案(I/O复用模型,epoll):将用户socket对应的fd注册进epoll(实际上服务器和操作系统之间传递的不是socket的fd而是fd_set的数据结构),然后epoll只告诉哪些需要读/写的socket,只需要处理那些活跃的、有变化的socket fd的就好了。这样,整个过程只在调用epoll的时候才会阻塞,收发客户消息是不会阻塞的。
2,memcached的相关知识:
(1)memcached的介绍:
memcached是一个自由、源代码开放,高性能分布式的内存对象缓存系统,用于动态web应用以减轻数据库的负载。她通过在内存中缓存数据和对象来减少数据库的次数,从而提高了网站访问速度。
memcached是一个存储键值对的hashmap,在内存中对任意的数据(比如字符串,对象等)所使用的key-value存储,数据可以来自数据库调用,API调用,或者页面渲染的结果。
(2)memcached的工作原理:
由于它的工作机制是在**内存中开辟一块空间,然后建立一个hashtable**,memcached管理这些hashtable,所以,速度非常快。
(3)memcached的作用:
使用memcached的网站一般流量是比较大的,为了缓解数据库的压力,让memcached**作为一个缓存区域,把部分信息保存在内存中,在前端能够迅速的进行读取**。那么一般的焦点就是集中在如何分担数据库压力和进行分布式,毕竟单机的memcached的内存容量是有限的。
(4)memcached的设计思想:
==>缓存与数据库如何保持同步:
在service层新增,修改和删除操作时,将更改的数据写入数据库,并将操作的数据在缓存中标记,等待下次读取时再从数据库中加载到缓存中。
(5)memcached的使用场景:
在动态系统中减少数据库负载,提升性能,做缓存,适合多读少写,大数据量的情况(如大量查询用户信息、好友信息、文章信息等)。它的一个**总原则是将经常需要从数据库读取的数据缓存在memcaced中**,这些数据也分为几类:
(6)不适用场景:
3,那你为什么选择Redis的缓存方案而不用memcached呢(memcached与redis有什么区别)
4,为什么单线程的redis比多线程的memcached效率要高?
5,使用过Redis分布式锁么,它是怎么实现的?
先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。
==>问?如果在setnx之后执行expire之前进程意外crash或者要重启维护了,那会怎么样?
这样的话,锁就得不到释放了,但是好像set指令有非常复杂的参数,这个应该是可以同时把setnx和expire合成一条指令来用的!
==>下面是比较详细的解释:
redis实现分布式锁基于setnx命令,因为在redis中key是保证唯一的,所以当多个线程同时创建setnx时,只要谁能够创建成功谁就能获取到锁。
6.使用过Redis做异步队列么,你是怎么用的?有什么缺点?
一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。
==> 问?可不可以不用sleep呢?
答:list还有个指令叫blpop,在没有消息的时候,他会阻塞住直到消息到来
==>问?能不能生产一次消费多次呢?
答:使用pub/sub主题订阅者模式,可以实现1:N的消息队列。
==>问?pub/sub有什么缺点呢?
答:在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。
==>问?redis如何实现延时队列?
答:使用sorted set(也就是有序集合),拿时间戳作为score,消息内容作为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒前的数据轮询进行处理
缺点:
在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。
7.Redis的并发竞争key问题如何解决?
这个问题的意思是大概是:同时有多个子系统去set一个key。
答案:
(1)若对这个key操作,不要求顺序。这种情况下,准备一个分布式锁,大家去抢锁,抢到锁就做set操作即可,比较简单。
(2)若对这个key操作,要求顺序。如:key1-value1,key2-value2,key3-value3,期望按照这个顺序key1-value1,key2-value2,key3-value3变化,这种时候我们在数据写入的数据库的时候,需要保持一个时间戳,假设时间戳如下:
系统A key1{value1 3:00}
系统B key1{value2 3:05}
系统C key1{value3 3:10}
若系统B先抢到锁,将key1设为{value2 3:05},接下来系统A抢到锁,发现自己的value1的时间戳早于缓存中的时间戳,那就不做set操作了,以此类推。
(3)其他方法,比如利用队列,将set方法变成串行访问也可以
8,redis的并发上限?
上限来自于你的 redis可以使用的内存大小(假如redis有三个节点,每个节点可使用的最大1G内存,那你的缓存的上限小于3G。)
9,redis官方为什么不提供windows版本?
因为目前linux版本已经相当稳定,而且用户量很大,无需开发windows版本,反而会带来兼容性问题。
10.redis如何做内存优化?
尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以,你应该尽可能的将你的数据模型抽象到一个散列表里。比如你的web系统中有一个用户对象,不要为这个用户名称,姓氏,邮箱,密码设置单独的key,而是应该把这个用户的所有信息存储到一张散列表里面。
11,若redis正在给线上的业务提供服务,那使用keys指令会有什么问题?
因为redis是单线程的,keys命令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕服务才能恢复。这时可以使用scan指令,scan指令可以无阻塞的提供出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接使用keys指令长。
12,单线程的redis如何利用多核的CPU?
因为redis是单线程,所以同一时刻只有一个操作在进行。所以耗时的命令会导致并发的下降。==>可以启动多个实例,组成master-master或master-slave或cluster,这样的话就可以使用多核的CPU。
13,为什么redis需要把所有数据放入到内存中
redis为了达到最快的读写速度将数据都读入内存中,并通过异步的方式将数据写入磁盘。所以redis具有快速和数据持久化的特征。若不将数据放入内存中,磁盘IO速度会严重影响redis性能。