互联网产品正从“满足用户单向浏览的需求”发展为“满足用户个性化信息获取及社交的需求”。随着 5G的到来,会有越来越多“不可思议”的场景被搬到互联网上。这就要求产品做到以用户和关系为基础,对海量数据进行实时分析计算。
这也就意味着,对于用户的每次请求,服务器端都要查询海量数据、多维度数据,还要将这些数据进行聚合、过滤、筛选和排序,最终响应给用户。如果这些数据全部从数据库中加载,则将是一个无法忍受的漫长过程。
1
为什么需要缓存
使用缓存可以提升系统性能,以及改善用户体验。
缓存的意义是:通过开辟一个新的数据交换缓冲区,从而解决了数据获取代价太大的问题,让数据得到更快的访问。
缓存通过“用空间换时间”来达到加速数据获取的目的。
下图展示了引入缓存后的系统架构。通过缓存,可以提升访问性能、降低网络拥堵、减轻服务负载和增强可扩展性。
一般情况下,数据被存放在数据库中,应用程序直接操作数据库。当应用程序访问量达到上万条时,数据库服务器的压力会增大。如果需要减轻数据库服务器的压力,则有以下方法:
- 实现数据库的读写分离。
- 实现数据库的分库分表。
- 使用缓存,并实现读写分离。
缓存的原理是:将应用程序已经访问过的内容或数据存储起来,当应用程序再次访问这些内容或数据时先从缓存中查找;如果缓存命中,则返回数据;如果缓存不命中,则再查找数据库,并将得到的内容或数据保存到缓存中。
缓存具有以下缺点:
- 在系统中引入缓存,会增加系统的复杂度。
- 缓存比数据库的存储成本更高,系统部署及运行的费用也更高。
- 由于一份数据被同时存放在缓存和数据库中,甚至缓存中也会有多个数据副本,所以会存在多份数据不一致的问题。
Redis是完全开源的,并且遵守BSD协议,它具有以下3个特点:
- 支持数据的持久化,可以将内存中的数据保存在磁盘中,重启后可以再次加载这些数据进行使用。
- 不仅支持Key-Value类型的数据,还支持基于list、set、zset、hash等数据结构的存储。
- 支持Master-Slave模式的数据备份。
2
Redis的优势
Redis是基于内存的数据库,读操作和写操作都在内存中完成,速度完全超过磁盘数据库的读写速度。
Redis之所以具有很高的性能,主要得益于以下几点:
(1)纯内存操作。一般都是简单的读写操作,线程占用的时间很少,时间的花费主要集中在I/O上,所以读写速度快。
(2)采用单线程模型。从而保证了每个操作的原子性,也减少了线程的上下文切换和竞争。
(3)使用I/O多路复用模型。非阻塞I/O;使用单线程来轮询描述符;将数据库的开、关、读和写都转换成了事件;Redis采用自己实现的事件分离器,效率比较高。
(4)高效的数据结构。
- 整个 Redis就是一个全局哈希表,其时间复杂度是O(1),而且Redis会执行再哈希操作以防止因哈希冲突导致链表过长。并且,为防止一次性重新映射时数据过大导致线程阻塞,Redis采用了渐进式再哈希,巧妙地将一次性复制分摊到多次操作中,从而避免了阻塞。
- Redis使用哈希结构和有序的数据结构加快了读写速度。
- Redis对数据存储进行了优化,对数据进行压缩存储,还可以根据实际存储的数据类型选择不同编码。
3
Redis与其他Key-Value数据库有何不同
Redis有着更为复杂的数据结构,并且提供了对它们的原子性操作(这是一个不同于其他Key-Value数据库的重点)。Redis的数据类型都是基本数据结构,并且对程序员透明,程序员无须进行额外的抽象。
Redis运行在内存中,但是其数据可以持久化到磁盘中,所以在对不同数据进行高速读写时,数据量不能多于内存的存储空间。
Redis的另一个优点是:对于在磁盘上操作比较复杂的数据结构,在内存中操作它们非常简单,这样Redis可以做很多复杂性很强的事情。同时,在磁盘格式方面,内存数据库以追加方式写入数据,因为它不需要进行随机访问。
4
一个典型的Redis应用案例
下面通过一个典型的应用案例,来演示Redis的强大功能。
假设,某网站现有1亿个注册用户,有经常登录的,也有不经常登录的。
- 需要记录用户的登录次数。
- 需要查询活跃用户,例如查询一周内登录3次的用户。
1. 采用传统的关系型数据库
在关系型数据库中建立一张表,用于存储用户的登录信息,如下图所示。
由于关系型数据库基于行来保存数据,因此,在用户登录网站时会产生1条记录。假设1亿个用户每天都登录网站,这样每天将产生1亿条记录。而一个星期则产生7亿条记录。这对于关系型数据库来说是一个非常大的压力。
2. 采用Redis
一个用户是否登录了网站,只需要用1或0表示即可。例如:1表示该用户登录了网站,而0表示该用户没有登录网站。Redis提供了setbit操作,可以很好地支持这种表示。具体操作如下:
127.0.0.1:6379> setbit monday 123 1 星期一,ID为123的用户登录了网站(integer) 0127.0.0.1:6379> setbit monday 22 1 星期一,ID为22的用户登录了网站(integer) 0127.0.0.1:6379> setbit tuesday 22 1 星期二,ID为22的用户登录了网站(integer) 0127.0.0.1:6379> getbit monday 123 获取星期一ID为123的用户是否登录了网站(integer) 1 返回1表示他登录了网站127.0.0.1:6379> getbit monday 22(integer) 1127.0.0.1:6379> getbit monday 21 获取星期一ID为21的用户是否登录了网站(integer) 0 返回0表示他没有登录网站127.0.0.1:6379>
假设这1亿个用户每天都登录网站,每天最多只会产生1亿个值为1的数据(大概12MB),一个星期大概84MB。这样就减少了数据库的负担。
5
三个重要概念——读写分离、主从架构、复制方式
Redis不能支撑高并发的瓶颈主要在于单节点,因为单节点的Redis不具备自动容错和恢复功能。如果服务器意外宕机,则Redis无法对外提供服务。并且,如果服务器磁盘损坏,则会造成数据丢失。
因为,单节点的Redis是很难实现高并发的,在通常业务中读请求要多于写请求,所以,一般需要采用读写分离架构。
读写分离架构即主从架构:一个主节点(Master)、多个从节点(Slave)。
- 写请求,直接写到Master节点。
- 读请求,直接从Slave节点中读。
如下图所示,Master节点主要负责写请求,并且将数据同步到各个Slave节点中;Slave节点主要负责读请求。
这种主从架构的优点是:Slave节点可以水平扩容。即如果系统QPS持续增加,当前Slave节点无法承受,则直接增加一个或多个Slave节点即可应对持续增加的QPS。
在主从节点建立连接后,就可以开始进行数据同步了,即从节点向主节点发送psync命令(在Redis 2.8版本之前是sync命令)开始同步。
数据同步是主从复制的核心阶段。根据主从节点当前状态的不同,主要分为全量复制和部分复制。
(1)全量复制:用于第一次复制,或者在无法进行部分复制时进行的复制,将主节点中所有数据都发送给从节点。
(2)部分复制:在网络中断等情况后进行的复制,将中断期间主节点写入的数据发送给从节点。如果因为网络中断时间过长而导致主节点没有完整地保存中断期间所执行的写命令,则无法进行部分复制,仍进行全量复制。
6
构建一个高性能、可扩展的Redis集群
上面介绍了通过主从复制能实现集群的横向扩展,以提高Redis读性能N倍。但对于写性能,这种方案还是有问题。
引入Redis集群可以将写性能提升N倍。Redis集群可以提供更大的容量,提升资源系统的可用性。
Redis集群由多个节点组成,数据平均分布在这些节点中。集群中的节点分为主节点和从节点。其中,主节点主要负责读写请求和集群信息的维护;从节点主要负责主节点数据和状态信息的管理。
1. Redis集群主要有如下作用
- 数据分区:集群将数据分布在多个节点上,一方面突破了Redis单机内存容量的限制,使得集群存储容量大大增加;另一方面每个主节点都可以对外提供读服务和写服务,极大地提高了集群的响应能力。
- 高可用:集群支持主从复制和主节点的自动故障转移,当任意一个节点发生故障时,集群仍可以对外提供服务。
搭建Redis集群共有两种方式:
- 手动执行Redis命令完成搭建。
- 使用Ruby脚本完成搭建。
这两种集群搭建方式的原理是一样的,只是Ruby脚本将Redis命令进行了打包封装。
2. Redis集群的原理
Redis 集群最核心的功能是数据分区。一个Redis集群共有16 384个slot。Redis集群就是按照slot来进行数据的读写和管理的。每个Redis分片管理一部分的slot。
(1)Redis集群数据分区
Redis 集群在启动时,会按需要将所有slot分配到Redis的不同节点上。集群在启动后,会按照slot分配策略对访问数据的key进行Hash计算,并将客户端请求路由到对应的节点,如下图所示。
从上图可以看出:
- Redis对访问数据的key进行Hash计算,得到哈希值。
- 依据哈希值,计算数据属于哪个slot。
- 依据slot与节点的映射关系,计算数据属于哪个节点。
在启动集群时,在接入数据读写之前,可以用 Redis 的 cluster addslots命令将 16 384 个 slot 分配给不同的 Redis 分片节点;用 cluster delslots 命令去掉某个节点的 slot;用 cluster flushslots 命令清空某个节点的所有 slot 信息,以完成 slot 的调整。
Redis Cluster 采用去中心化架构,每个节点都记录全部 slot 的拓扑分布。这样,如果 Client把 key 分发给了错误的 Redis 节点,则Redis 会检查请求数据的key 所属的 slot;如果发现 key 属于其他节点的 slot,则会通知 Client 重定向到正确的 Redis 节点进行访问。
(2)Redis集群节点的通信机制。
Redis集群中的不同节点使用Gossip协议进行通信。使用Gossip协议的优点是:它是去中心化的,所以更新不会受中心节点的影响,可以通过任意一个节点来管理通知。其不足之处是:集群的收敛速度较慢,集群操作会在一定的延时后才通知到所有的Redis节点。
在使用Gossip协议进行通信时,Redis集群在进行扩/缩容时,可以向集群内的任何一个节点发送 meet 消息,将新节点加入集群,然后新节点就可以同其他节点进行通信了。
欢迎阅读《NoSQL数据库实战派:Redis + MongoDB + HBase》一书,了解更多相关内容。
抽奖赠书
本次福利将送出《NoSQL数据库实战派:Redis + MongoDB + HBase》* 5本
为避免撸羊毛的用户参与抽奖,让更多真实用户获得社区福利,现在把抽奖方式切换到 spring4all.com 网上通过积分参与。
本次抽奖地址:http://spring4all.com/forum-post/1976.html
还有一大波福利正在路上,一起来参与社区内容的建设,一起学习一起成长吧!如何获取积分?日常登录spring4all.com,参与签到、分享技术经验、好用资源(原创、转载均可)、提问交流、回帖帮助他人等有益内容的,均可获得积分!