1.什么是redis
redis,即远程字典服务,是一个高性能的key-value数据库,也是非关系型数据库。
redis是一个开源(BSD许可)的,内存中的数据结构存储系统,他可以用作数据库、缓存和消息中间件。它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
2.redis都可以干什么
- 计算器:利用redis中原子性的自增操作,我们可以统计类似用户点赞数、用户访问数等,这类操作如果使用MySQL,频繁的读写会带来相当大的压力。
- 限速器:限速器比较典型的使用场景是限制某个用户访问某个api的频率,常用的有,抢购时,防止用户疯狂点击带来的不必要的压力。
- 好友关系:利用set集合的一些命令,比如求交集、并集、差集等。可以方便搞定一些共同好友、共同爱好之类的功能
- 简单消息队列:除了redis自身的发布/订阅模式,我们也可以利用list来实现一个队列机制。比如,到货通知、邮件发送之类的需求,不需要高可靠,但是会带来非常大的DB压力,完全可以用list来完成一部解耦。
- session共享:以PHP为例,默认session是保存在服务器的文件中,如果是集群服务,同一个用户过来可能落在不同机器上,这就会导致用户频繁登录;采用redis保存session后,无论用户落在那台机器上都能获取到对应的session信息。
3.redis的特点及优势
特点:
- redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加在进行使用。
- redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的储存
- redis支持数据的备份,即master-slave模式的数据备份
优势:
- 性能极高——redis能读得的速度是110000次/s,写的速度是81000次/s。
- 丰富的数据类型——redis支持二进制案例的Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
- 原子——redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过multi和exec指令包起来。
- 丰富的特性-redis还支持publish/subscribe,通知,key过期等等特性
4.redis为什么这么块
- 基于内存:(拿MySQL与redis做下对比)
MySQL是持久化存储的,数据存放在磁盘里面,检索的话,会涉及到一定的IO,为了解决这个瓶颈,于是出现了缓存,加快检索速度
(ps IO瓶颈:
关于IO的几个指标解释:
- bi:读磁盘的速度,单位KB/秒
- bo:写磁盘的速度
- wa:IO的时间
瓶颈分析:
1:wa并不能反应磁盘的瓶颈,实际反应的是cpu的io等待时间
2:bi+bo 参考值为1000,如果超过1000,而且wa值比较大,标示系统磁盘IO存在瓶颈
)
内存和硬盘的关系,硬盘放置主体数据用于持久化存储,而内存则是当前运行的那部分数据,CPU访问内存而不是磁盘,这大大提升了运行的速度,当然这是基于程序的局部化访问原理。
推理到redis+MySQL,他是内存+磁盘关系的一个映射,MySQL放在磁盘,redis放在内存,这样的话,web应用每次只访问redis,如果没有找到数据,才回去访问MySQL
2. 单线程:
多线程的问题:
- 锁机制造成性能降低
Java中与hash相关的集合有HashMap,HashTable,ConcurrentHashMap等,其中HashMap是线程不安全的,就是如果多线程环境中有多个线程同时操作该集合的话,可能导致数据污染的问题,因而有了HashTable的出现。但是在实际操作中,发现HashTable的性能比较低,因为HashTable靠一把锁来维护全部的数据,所有的线程在操作数据时只能去竞争那一把锁,造成线程等待,故而新能降低。为了克服这个问题,在JDK1.5~1.7版本,Java使用了分锁机制实现ConcurrenHashMap。这就是多线程中锁机制造成的性能降低。
- 上下文切换造成时间消耗
CPU的每个核在一个时刻只能运行一个线程,当在运行一个线程的过程中转去运行另外一个线程,这个叫做线程上下文切换。由于可能当前线程的任务并没有执行完毕,所以在切换时需要保存线程的运行状态,以便下次重新切换回来时能够继续切换之前的状态运行。每一次切换都是需要花时间的,所以在计算密集型的任务中,多线程的数量不宜超过CPU的核心数,吵过之后CPU只是会徒劳的花时间在上下文切换上。
而redis使用单线程,就不需要考虑上述问题了。
3. IO多路复用
其实单线程也是存在问题的,由于线程的执行过程是顺序执行的,加上读写操作等待用户输入或输出都是阻塞的,所以IO操作一般情况下不能直接返回,这会导致某个文件的IO阻塞,导致整个进程无法对其他客户提供服务。为了解决这个问题,因此有了IO多路复用
IO多路复用的核心是用户进程调用select函数被阻塞,同时内核轮询监听所有select负责的socket,当任何一个socket有数据准备好了,select就将控制权返回给用户进程。
多路复用的具体实现方式有select/poll/epoll等,redis内部实现采用了epoll+自己实现的简单的事件框架。
相比select和poll,epoll有以下几个优势:
- epoll没有最大连接数限制(select的最大限制为1024)
- epoll最大的有点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,epoll的效率就远远高于select和poll
- 内存拷贝,select和poll在数据准备好之后,需要把数据从内核拷贝到用户进程,epoll使用了“共享内存”,避免了内存拷贝。
- epoll线程安全
总结:redis使用了IO多路复用,保证了redis在进行IO操作是依然能处理socket请求,不会再IO上浪费时间;单线程机制也避免了不必要的上下文切换和锁机制;而redis的每一次IO操作都是基于内存的,非常高效。可以说,redis的这三个特性相辅相成,共同造就了redis的高并发。
5.kafka与redis区别
举一个例子,来说:
老师有个好消息要告诉同学们,有两种方式。
1.去到每个人的座位上,挨个说。(此时,小明不在座位上,就只能错过这个好消息了)
2.老师把好消息写到黑板上,三天后就会擦掉,谁想知道谁来看。(此时,小明请假一周,他就会错过这个好消息)
redis用第一种办法,kafka用第二种办法