Redis 是一个使用 C 语言编写的,开源的(BSD许可)高性能非关系型(NoSQL)的键值对数据库。前端
Redis 能够存储键和五种不一样类型的值之间的映射。键的类型只能为字符串,值支持五种数据类型:字符串、列表、集合、散列表、有序集合。node
与传统数据库不一样的是 Redis 的数据是存在内存中的,因此读写速度很是快,所以 redis 被普遍应用于缓存方向,每秒能够处理超过 10万次读写操做,是已知性能最快的Key-Value DB。另外,Redis 也常常用来作分布式锁。除此以外,Redis 支持事务 、持久化、LUA脚本、LRU驱动事件、多种集群方案(Redis 6.0 集群搭建实践)。面试
从2010年3月15日起,Redis的开发工做由VMware主持。从2013年5月开始,Redis的开发由Pivotal赞助。redis
Redis主要有5种数据类型,包括String,List,Set,Zset,Hash,知足大部分的使用要求。详细的可参考:Redis 的 8 大数据类型,写得很是好!算法
因为Redis优异的读写性能,持久化支持等优点,Redis的使用场景很是多,主要包括计数器,缓存,消息队列,分布式锁等,具体使用场景以下:数据库
计数器vim
缓存后端
会话缓存缓存
全页缓存(FPC)安全
查找表
消息队列(发布/订阅功能)
分布式锁实现
其它
Redis 是内存型数据库,为了以后重用数据(好比重启机器、机器故障以后回复数据),或者是为了防止系统故障而将数据备份到一个远程位置,须要将内存中的数据持久化到硬盘上。
Redis 提供了RDB和AOF两种持久化方式。默认是只开启RDB,当Redis重启时,它会优先使用AOF文件来还原数据集。
Redis持久化详解能够参考:Redis持久化
Redis中有个设置时间过时的功能,即对存储在 redis 数据库中的值能够设置一个过时时间。做为一个缓存数据库,这是很是实用的。如咱们通常项目中的 token 或者一些登陆信息,尤为是短信验证码都是有时间限制的,按照传统的数据库处理方式,通常都是本身判断过时,这样无疑会严重影响项目性能。
Redis有三种不一样的删除策略:当即删除,惰性删除,定时删除
能够看到,第二种为被动删除,第一种和第三种为主动删除,且第一种实时性更高。下面对这三种删除策略进行具体分析。
当即删除能保证内存中数据的最大新鲜度,由于它保证过时键值会在过时后立刻被删除,其所占用的内存也会随之释放。可是当即删除对cpu是最不友好的。由于删除操做会占用cpu的时间,若是恰好碰上了cpu很忙的时候,好比正在作交集或排序等计算的时候,就会给cpu形成额外的压力。
并且目前redis事件处理器对时间事件的处理方式–无序链表,查找一个key的时间复杂度为O(n),因此并不适合用来处理大量的时间事件。
惰性删除是指,某个键值过时后,此键值不会立刻被删除,而是等到下次被使用的时候,才会被检查到过时,此时才能获得删除。因此惰性删除的缺点很明显:浪费内存。dict字典和expires字典都要保存这个键值的信息。
举个例子,对于一些按时间点来更新的数据,好比log日志,过时后在很长的一段时间内可能都得不到访问,这样在这段时间内就要拜拜浪费这么多内存来存log。这对于性能很是依赖于内存大小的redis来讲,是比较致命的。
从上面分析来看,当即删除会短期内占用大量cpu,惰性删除会在一段时间内浪费内存,因此定时删除是一个折中的办法。
定时删除是:每隔一段时间执行一次删除操做,并经过限制删除操做执行的时长和频率,来减小删除操做对cpu的影响。另外一方面定时删除也有效的减小了因惰性删除带来的内存浪费。
redis使用的过时键值删除策略是:惰性删除加上按期删除,二者配合使用。
能够设置内存最大使用量,当内存使用量超出时,会施行数据淘汰策略。
Redis 具体有 6 种淘汰策略:
做为内存数据库,出于对性能和内存消耗的考虑,Redis 的淘汰算法实际实现上并不是针对全部 key,而是抽样一小部分而且从中选出被淘汰的 key。
Redis 4.0 引入了 volatile-lfu 和 allkeys-lfu 淘汰策略,LFU 策略经过统计访问频率,将访问频率最少的键值对淘汰。
您须要根据系统的特征,来选择合适的淘汰策略。 固然,在运行过程当中也能够经过命令动态设置淘汰策略,并经过 INFO 命令监控缓存的 miss 和 hit,来进行调优。
淘汰策略的内部实现
在这个过程当中,内存使用量会不断地达到 limit 值,而后超过,而后删除部分 key,使用量又降低到 limit 值之下。
若是某个命令致使大量内存占用(好比经过新key保存一个很大的set),在一段时间内,可能内存的使用量会明显超过 maxmemory 限制。
二者都是非关系型内存键值数据库,如今公司通常都是用 Redis 来实现缓存,并且 Redis 自身也愈来愈强大了!Redis 与 Memcached 的区别请参考:Redis与Memcached的区别
Redis 经过 MULTI、EXEC、WATCH 等命令来实现事务(transaction)功能。事务提供了一种将多个命令请求打包,而后一次性、按顺序地执行多个命令的机制,而且在事务执行期间,服务器不会中断事务而改去执行其余客户端的命令请求,它会将事务中的全部命令都执行完毕,而后才去处理其余客户端的命令请求。
事务中的多个命令被一次性发送给服务器,而不是一条一条发送,这种方式被称为流水线,能够减小客户端与服务器之间的网络通讯次数从而提高性能。
在传统的关系式数据库中,经常使用 ACID 性质来检验事务功能的可靠性和安全性。在 Redis 中,事务老是具备原子性(Atomicity)、一致性(Consistency)和隔离性(Isolation),而且当 Redis 运行在某种特定的持久化模式下时,事务也具备持久性(Durability)。
Redis 服务器是一个事件驱动程序。
服务器经过套接字与客户端或者其它服务器进行通讯,文件事件就是对套接字操做的抽象。
Redis 基于 Reactor 模式开发了本身的网络事件处理器,使用 I/O 多路复用程序来同时监听多个套接字,并将到达的事件传送给文件事件分派器,分派器会根据套接字产生的事件类型调用相应的事件处理器。
服务器有一些操做须要在给定的时间点执行,时间事件是对这类定时操做的抽象。
时间事件又分为:
目前Redis只使用周期性事件,而没有使用定时事件。 一个事件时间主要由三个属性组成:
实现服务器将全部时间事件都放在一个无序链表中,每当时间事件执行器运行时,遍历整个链表,查找全部已到达的时间事件,并调用相应的事件处理器。(该链表为无序链表,不按when属性的大小排序)
服务器须要不断监听文件事件的套接字才能获得待处理的文件事件,可是不能一直监听,不然时间事件没法在规定的时间内执行,所以监听时间应该根据距离如今最近的时间事件来决定。
Sentinel(哨兵)能够监听集群中的服务器,并在主服务器进入下线状态时,自动从从服务器中选举出新的主服务器。
分片是将数据划分为多个部分的方法,能够将数据存储到多台机器里面,这种方法在解决某些问题时能够得到线性级别的性能提高。
假设有 4 个 Redis 实例 R0,R1,R2,R3,还有不少表示用户的键 user:1,user:2,… ,有不一样的方式来选择一个指定的键存储在哪一个实例中。
最简单的方式是范围分片,例如用户 id 从 0~1000 的存储到实例 R0 中,用户 id 从 1001~2000 的存储到实例 R1 中,等等。可是这样须要维护一张映射范围表,维护操做代价很高。
还有一种方式是哈希分片,使用 CRC32 哈希函数将键转换为一个数字,再对实例数量求模就能知道应该存储的实例。
根据执行分片的位置,能够分为三种分片方式:
经过使用 slaveof host port 命令来让一个服务器成为另外一个服务器的从服务器。
一个从服务器只能有一个主服务器,而且不支持主主复制。
链接过程
随着负载不断上升,主服务器可能没法很快地更新全部从服务器,或者从新链接和从新同步从服务器将致使系统超载。为了解决这个问题,能够建立一个中间层来分担主服务器的复制工做。中间层的服务器是最上层服务器的从服务器,又是最下层服务器的主服务器。
缓存雪崩是指缓存同一时间大面积的失效,因此,后面的请求都会落到数据库上,形成数据库短期内承受大量请求而崩掉。
缓存穿透是指缓存和数据库中都没有的数据,致使全部的请求都落到数据库上,形成数据库短期内承受大量请求而崩掉。
附加
布隆过滤器(推荐)
缓存击穿是指缓存中没有但数据库中有的数据(通常是缓存时间到期),这时因为并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引发数据库压力瞬间增大,形成过大压力。和缓存雪崩不一样的是,缓存击穿指并发查同一条数据,缓存雪崩是不一样数据都过时了,不少数据都查不到从而查数据库。
缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就能够避免在用户请求的时候,先查询数据库,而后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然须要保证服务仍是可用的,即便是有损服务。系统能够根据一些关键数据进行自动降级,也能够配置开关实现人工降级。
缓存降级的最终目的是保证核心服务可用,即便是有损的。并且有些服务是没法降级的(如加入购物车、结算)。
在进行降级以前要对系统进行梳理,看看系统是否是能够丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;好比能够参考日志级别设置预案:
通常:好比有些服务偶尔由于网络抖动或者服务正在上线而超时,能够自动降级;
警告:有些服务在一段时间内成功率有波动(如在95~100%之间),能够自动降级或人工降级,并发送告警;
错误:好比可用率低于90%,或者数据库链接池被打爆了,或者访问量忽然猛增到系统能承受的最大阀值,此时能够根据状况自动降级或者人工降级;
严重错误:好比由于特殊缘由数据错误了,此时须要紧急人工降级。
服务降级的目的,是为了防止Redis服务故障,致使数据库跟着一块儿发生雪崩问题。所以,对于不重要的缓存数据,能够采起服务降级策略,例如一个比较常见的作法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。
热点数据,缓存才有价值
对于冷数据而言,大部分数据可能尚未再次访问到就已经被挤出内存,不只占用内存,并且价值不大。频繁修改的数据,看状况考虑使用缓存
对于热点数据,好比咱们的某IM产品,生日祝福模块,当天的寿星列表,缓存之后可能读取数十万次。再举个例子,某导航产品,咱们将导航信息,缓存之后可能读取数百万次。
数据更新前至少读取两次,缓存才有意义。这个是最基本的策略,若是缓存尚未起做用就失效了,那就没有太大价值了。
那存不存在,修改频率很高,可是又不得不考虑缓存的场景呢?有!好比,这个读取接口对数据库的压力很大,可是又是热点数据,这个时候就须要考虑经过缓存手段,减小数据库的压力,好比咱们的某助手产品的,点赞数,收藏数,分享数等是很是典型的热点数据,可是又不断变化,此时就须要将数据同步保存到Redis缓存,减小数据库压力。
缓存中的一个Key(好比一个促销商品),在某个时间点过时的时候,刚好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过时通常都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
对缓存查询加锁,若是KEY不存在,就加锁,而后查DB入缓存,而后解锁;其余进程若是发现有锁就等待,而后等解锁后返回数据或者进入DB查询
以上内容参考连接:https://blog.csdn.net/ThinkWo...
咱们就来总结一下,在使用Redis时的最佳实践方式,主要包含两个层面:业务层面、运维层面。
因为我以前写过不少UGC后端服务,在大量场景下用到了Redis,这个过程当中也踩过不少坑,因此在使用过程当中也总结了一套合理的使用方法。
后来作基础架构,开发Codis、Redis相关的中间件,在这个阶段关注领域从使用层面下沉到Redis的开发和运维,更多聚焦在Redis的内部实现和运维过程当中产生的各类问题,在这块也积累了一些经验。
下面就针对这两块,分享一下我认为比较合理的Redis使用和运维方法,不必定最全面,也可能与你使用Redis的方法不一样,但如下这些方法都是我在踩坑以后总结的实际经验,供你参考。关注公众号Java技术栈回复redis获取系列Redis教程。
业务层面主要是开发人员须要关注,也就是开发人员在写业务代码时,如何合理地使用Redis。开发人员须要对Redis有基本的了解,才能在合适的业务场景使用Redis,从而避免业务层面致使的延迟问题。
写请求量很大时,推荐使用集群,部署多个实例分摊写压力
目的是合理规划Redis的部署和保障Redis的稳定运行,主要优化以下:
以上就是我在使用Redis和开发Redis相关中间件时,总结出来Redis推荐的实践方法,以上提出的这些方面,都或多或少在实际使用中遇到过。
可见,要想稳定发挥Redis的高性能,须要在各个方面作好工做,但凡某一个方面出现问题,必然会影响到Redis的性能,这对咱们使用和运维提出了更高的要求。
若是你在使用Redis过程当中,遇到更多的问题或者有更好的使用经验,能够留言一块儿探讨!
出处:kaito-kidd.com/2020/07/04/redis-best-practices/
集群中每一个主节点将承担一部分槽点的维护,而槽点中存储着数据,每一个主节点都有至少一个从节点用于高可用。
redis cluster是去中心化的,集群中的每一个节点都是平等的关系,每一个节点都保存各自的数据和整个集群的状态。每一个节点都和其余全部节点链接,并且这些链接保持活跃。
搭建集群时,会为每个分片的主节点,对应一个从节点。实现slaveof功能,同时当主节点down,实现sentinel哨兵的自动failover切换功能
端口号:7000-7005
本次分布式分片集群在一台LInux系统便可,只须要安装多个实例做为集群配置。
yum -y install ruby rubygems
yum安装2.0.0版本,可是gem须要2.2.2版本以上,因此选择编译
wget https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.1.tar.gz tar xf ruby-2.6.1.tar.gz && cd ruby-2.6.1/ ./configure --prefix=/usr/local/ruby make && make install && echo $? echo 'export PATH=$PATH:/usr/local/ruby/bin' >> /etc/profile source /etc/profile
# 查看gem工具源地址 gem sources -l # 添加一个阿里云的gem工具源 gem sources -a http://mirrors.aliyun.com/rubygems/ # 删除gem工具默认国外源 gem sources --remove https://rubygems.org/ # 下载当前最新版本集群插件 gem install redis -v 4.1.0
mkdir /data/700{0..5}
vim /data/7000/redis.conf port 7000 daemonize yes pidfile /data/7000/redis.pid loglevel notice logfile "/data/7000/redis.log" dbfilename dump.rdb dir /data/7000 protected-mode no cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes
# 拷贝配置文件 cp /data/7000/redis.conf /data/7001/ cp /data/7000/redis.conf /data/7002/ cp /data/7000/redis.conf /data/7003/ cp /data/7000/redis.conf /data/7004/ cp /data/7000/redis.conf /data/7005/ # 修改配置文件内容 sed -i 's#7000#7001#g' /data/7001/redis.conf sed -i 's#7000#7002#g' /data/7002/redis.conf sed -i 's#7000#7003#g' /data/7003/redis.conf sed -i 's#7000#7004#g' /data/7004/redis.conf sed -i 's#7000#7005#g' /data/7005/redis.conf
redis-server /data/7000/redis.conf redis-server /data/7001/redis.conf redis-server /data/7002/redis.conf redis-server /data/7003/redis.conf redis-server /data/7004/redis.conf redis-server /data/7005/redis.conf
ln -s /usr/local/redis-5.0.2/src/redis-trib.rb /usr/sbin/
ps -ef |grep redis-server
# --replicas 1",1是表明每个主有一个从,后面的是全部节点的地址与端口信息 redis-cli --cluster create --cluster-replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005
分布式主从规则为,前三个实例节点是主,对应的后面三个实例节点为从节点,若是replicas 2,那就多加3个实例节点
redis-cli -p 7000 cluster nodes|grep master
redis-cli -p 7000 cluster nodes|grep slave
mkdir /data/700{6..7} 拷贝其余端口实例: # 拷贝配置文件 cp /data/7000/redis.conf /data/7006/ cp /data/7000/redis.conf /data/7007/ # 修改配置文件内容 sed -i 's#7000#7006#g' /data/7006/redis.conf sed -i 's#7000#7007#g' /data/7007/redis.conf
启动新节点实例:
redis-server /data/7006/redis.conf redis-server /data/7007/redis.conf
ps -ef |grep redis-server
#'把7006实例添加到7000实例这个主节点所在集群内(此时已经有了4个主节点) redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000
redis-cli -p 7000 cluster nodes|grep master
#'操做集群管理节点重新分配,并在交互界面指定分片大小、选择接收分片的节点ID redis-cli --cluster reshard 127.0.0.1:7000 How many slots do you want to move (from 1 to 16384)? 4096 #经过人工手动计算数据分片总大小除以主节点后的数字 What is the receiving node ID? 2129d28f0a86fc89571e49a59a0739812cff7953 #选择接收数据分片的节点ID,(这是新增节点7006实例的ID号) Source node #1: all #选择从哪些源主节点从新分片给新主节点)(all是全部节点) Do you want to proceed with the proposed reshard plan (yes/no)? yes #确认修改以上的操做
redis-cli -p 7000 cluster nodes|grep master
#'把7007实例节点添加到7006实例主节点内,并指定对应7006实例主节点坐在集群的管理节点 redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7000 --cluster-slave --cluster-master-id 2129d28f0a86fc89571e49a59a0739812cff7953
redis-cli -p 7000 cluster nodes|grep slave
#'操做集群管理节点重新分配,并在交互界面指定分片大小、选择接收分片的节点ID redis-cli --cluster reshard 127.0.0.1:7000 #方法是根据要删除master节点的分片位置,而后一个组分一个节点 , 也能够直接移动全部数据片到一个节点 How many slots do you want to move (from 1 to 16384)? 1365 #经过人工手动查看数据分片总大小 What is the receiving node ID? e64f9074a3733fff7baa9a4848190e56831d5447 #选择接收数据分片的节点ID,(这是新增节点7006实例的ID号) Source node #1: 2129d28f0a86fc89571e49a59a0739812cff7953 #选择从哪些源主节点从新分片给新主节点(这是要删除的主节点的ID号) Source node #2: done #这是结束命令 Do you want to proceed with the proposed reshard plan (yes/no)? yes #确认修改以上的操做
redis-cli -p 7000 cluster nodes|grep master
#'操做集群管理节点重新分配,并在交互界面指定分片大小、选择接收分片的节点ID redis-cli --cluster reshard 127.0.0.1:7000 # 方法是根据要删除master节点的分片位置,而后一个组分一个节点 , 也能够直接移动全部数据片到一个节点 How many slots do you want to move (from 1 to 16384)? 1366 #经过人工手动查看数据分片总大小 What is the receiving node ID? f6c1aaea3a8c56e0c7dee8ad7ae17e26dd04244c #选择接收数据分片的节点ID,(这是新增节点7006实例的ID号) Source node #1: 2129d28f0a86fc89571e49a59a0739812cff7953 #选择从哪些源主节点从新分片给新主节点(这是要删除的主节点的ID号) Source node #2: done #这是结束命令 Do you want to proceed with the proposed reshard plan (yes/no)? yes #确认修改以上的操做
redis-cli -p 7000 cluster nodes|grep master
#'操做集群管理节点重新分配,并在交互界面指定分片大小、选择接收分片的节点ID redis-cli --cluster reshard 127.0.0.1:7000 #方法是根据要删除master节点的分片位置,而后一个组分一个节点 , 也能够直接移动全部数据片到一个节点 How many slots do you want to move (from 1 to 16384)? 1365 #经过人工手动查看数据分片总大小 What is the receiving node ID? 5a0df4ea0af5f35c1248e45e88d44c3f2e10169f Source node #1: 2129d28f0a86fc89571e49a59a0739812cff7953 Source node #2: done
redis-cli -p 7000 cluster nodes|grep master
#'删除已经清空数据的7006实例 redis-cli --cluster del-node 127.0.0.1:7006 2129d28f0a86fc89571e49a59a0739812cff7953 #删除没有主库的7007实例 redis-cli --cluster del-node 127.0.0.1:7007 821bcf78c5e4c0b08aa7a5d514214b657ebec4ab
#内存信息查看 redis-cli -p 6382 -a redhat info memory #设置最大只能使用100MB的内存 redis-cli -p 6382 -a redhat config set maxmemory 102400000