Redis
为什么使用Redis
可以做分布式缓存,用户的请求不会直接进入数据库,会先访问Redis,提高了数据的读取性能,提高了吞吐,提高了并发。
什么是nosql
Redis归类为nosql
- Not Only Sql,除了数据的存取之外,还会有其他的功能
- 传统项目使用纯数据库
- 为互联网和大数据而生,普通的数据库就不会把超大的数据提供做一个并发查询,因为数据库的单表数据是有限的,普通数据库不适用进行数据挖掘,大数据分析等等
- 水平(横向)扩展方便高效,比普通的数据库扩容方便很多,存储的形式比较简单,简单key=>value形式,可扩展性比较高
- 高性能读取,Redis可以达到每秒10万次
- 高可用,Redis可以搭建集群
- 存数据,做缓存,高效的访问,缓存是可以做持久化的
nosql的常见分类
- 键值对数据库 Redis、memcache
- 列存储数据库 Hbase
- 文档型数据库 MongoDB CouchDB
- 图形数据库 Neo4j FlockDB
什么是分布式缓存
- 提升读取速度性能,在高并发的场景中,通过分布式缓存提升读取速度的
- 分布式计算领域
- 为数据库降低查询压力
- 跨服务器缓存,任意节点都能够拿到数据,快速的响应请求
- 内存式缓存,内存的存取效率是远远大于硬盘的效率的,可以让大多数的数据命中缓存
什么是Redis
- NoSql
- 分布式缓存中间件
- key-value存储
- 提供海量数据存储访问
- 数据存储在内存中,读取更快
- 非关系型、分布式、开源、水平扩展
分布式缓存方案对比
Ehcache
优点:
1. 基于Java开发,和Java一起整体的健壮性会更好
2. 基于JVM缓存
3. 简单、轻巧、方便
缺点:
1. 集群不支持
2. 分布式不支持
适合单应用的Java语言开发的项目
Memcache
优点:
1. 简单的key-value存储
2. 内存使用率比较高
3. 多核处理,多线程
缺点:
1. 无法容灾
2. 无法持久化
Redis
优点:
1. 丰富的数据类型
2. 持久化
3. 主从同步、故障转移 集群
4. 内存数据库
缺点:
1. 单线程 (6之后多线程),单核去运行,在存储大数据量的时候,性能会有所降低的,但是小数据量比memcache更加好
2. 单核,利用不了全部的CPU,建议多实例去使用Redis
安装配置Redis
-
上传至Linux
-
解压
tar -zxvf redis-5.0.9.tar.gz
3.1 安装gcc编译环境
yum install gcc-c++
-
make && make install
cd redis-5.0.9/ make && make install
-
配置redis.conf
-
配置Redis,在utils目录下将Redis的启动命令设置为开机自启动
cd utils/ cp redis_init_script /etc/init.d/
-
创建 redis,用于存放配置文件
mkdir /usr/local/redis -p
拷贝Redis配置文件
cp redis.conf /usr/local/redis/
-
配置redis.conf
3.1 :目的是为了让Redis启动在Linux后台运行
3.2 修改Redis的工作目录
mkdir working
3.3将 bind 修改为0.0.0.0
3.4 设置密码 requirepass
-
-
修改redis_init_script文件
给执行权限redis_init_scriptchmod 777 redis_init_scrip ./redis_init_script start
-
设置开机自启动
redis_init_scrip文件中#chkconfig: 22345 10 90 #description: Start and Stop redis
将这个文件注册进去chkconfig redis_init_script on
重启服务器这样Redis也就重启了!
Redis的发布与订阅
基于消息进行的发布与订阅
- 发布者 :生产消息 publish
- 订阅者:消费者 subscribe ,需要向发布者做一个订阅的动作,有了订阅了之后,它们之间就相当于是绑定了一层关系,当发布者有些消息要发布的时候,相应的所有的订阅者等能够接收到发布者所发布的消息,消息队列方式和mq是一模一样的
- 订阅频道
SUBSCRIBE p1 p2 p3
然后处于监听模式 - 发布消息
PUBLISH p1 频道1 - 此刻如果订阅了p1 则会接收到订阅的消息
企业中很少用这种方式去代替mq,专人做专事
Redis的持久化-RDB
Redis的缓存数据是放在内存中的,但是一旦服务器发生故障,内存的数据就会消失,但是Redis有持久化机制,会把数据备份到磁盘,所以再次打开Redis,数据会回复的。
RDB:快照模式,每隔一段时间进行备份,保存某个时间点的快照
可以同时使用RDB和AOF
优势:
- RDB是以一个单独的文件进行备份,可以每小时备份一次,也可以每30天备份一次,可以在配置文件进行配置,可以重置不用版本的数据。
- RDB非常适合做一些灾备的方案的,灾难恢复非常的方便,因为仅仅只有一个完整文件。
- RDB在做备份的时候,会有父子进程,父进程fork出一个子进程,当子进程进行备份的时候,父进程不会操作磁盘的I/O的,可以保证备份的完整性
- RDB可以快速重新启动相比于AOF
劣势:
- 发生故障,有可能会丢失最后一次的备份数据
- 子进程所占用的内存比会和父进程一模一样,如会造成CPU负担
- 无法进行实时备份
配置:
save 900 1 after 900 sec (15 min) if at least 1 key changed
save 300 10 after 300 sec (5 min) if at least 10 keys changed
save 60 10000 after 60 sec if at least 10000 keys changed
stop-writes-on-bgsave-error yes
保存过程中发生错误 停止写操作,如果设置为no,在发生错误的时候还是会写,这样可能会造成数据的不一致
rdbcompression yes
压缩,使用LZF压缩模式,如果想节省CPU的开支,就选择no,但是相对的文件变得比较大
rdbchecksum yes
校验的一个规则,数据压缩了之后会对数据进行一个校验,通过CRC64进行校验,差不多会有10%的性能开销
dbfilename dump.rdb
dir /usr/local/redis/working 文件目录
RDB使用大文件备份或者大量数据的恢复,但是对最后一次的保存数据会出现异常,对整体数据的完整性和一致性会造成一定的影响。
Redis的持久化-AOF
要追求数据的完整性、安全性就要考虑使用AOF了。
AOF:以日志的形式来进行记录用户请求的写操作,只有写操作才会记录到日志中,append追加,当日志越写越大,当然也可以进行重写的,把AOF日志文件变得更加的小
优势:
- 健壮和持久,更加的耐用,同步方式有三种,关闭、每秒、每次,默认是every second
- 日志是以append 追加方式,如果磁盘满了,也可以使用redis-check-aof非常简单进行修复
- 当数据太大的时候,Redis可以在后台自动重写
- AOF日志包含的所有写操作,一个接着一个进行叠加式的追加,便于Redis的解析和恢复,可以尝试从AOF文件导出数据,比如执行flashall,进入AOF文件,把最后一行命令删除,重启。
劣势:
- 相同的数据,同一份数据,AOF比RDB大
- 针对不用的同步机制,AOF会比RDB慢,因为AOF每秒都会备份做写操作,这样相对于RDB来说就略低。每秒备份fsync没毛病,但是如果客户端的每次写入就做一次备份fsync的话,尤其是在超高并发情况下,那么Redis的性能就会下降
- bug,就是数据恢复的时候数据不完整,为了防止bug的产生,AOF不会根据旧的指令去重构,而是根据当时缓冲中存在的数据指定去做重构,这样就更加健壮和可靠了。
配置:
appendonly no 默认是关闭的
appendfilename “appendonly.aof”
#appendfsync always
appendfsync everysec 默认是每秒
#appendfsync no
no-appendfsync-on-rewrite no
重写的时候是否要同步,no可以保证数据安全
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
重写机制:避免文件越来越大,自动优化压缩指令,会fork一个新的进程去完成重写动作,新进程里的内存数据会被重写,此时旧的aof文件不会被读取使用,类似rdb
当前AOF文件的大小是上次AOF大小的100% 并且并且文件体积达到64m,满足两者则触发重写
使用RDB还是AOF呢
- 如果能接受一段时间的缓存丢失,那么可以使用RDB
- 如果实时性的数据比较care,那么就用AOF
- 使用RDB和AOF结合一起做持久化,RDB做冷备,可以在不同时期对不用版本做回复,AOF做热备,保证数据仅仅只有1秒的损失。当AOF破损不可用了,那么再用RDB恢复,这样就做到了两者的相互结合,也就是说Redis恢复会先加载AOF,如果AOF有问题会再加载RDB,这样就达到冷热备份的目的了。
主从架构
最简单的Redis扩展模式,水平横向扩展、读写分离架构,master/slave,用户的所有读操作丢给slave去操作,主从复制。
主从原理:
- 首先启动master
- 配置slave,
- 第一次过程,当slave启动的时候,会向master发送一个ping包,master会将数据复制并传给slave,全量的数据复制(RDB),会把RDB全部的数据从内存中拷贝一个新的放在磁盘中,通过内网的网络传输传给slave,slave下载并加载到内存中,初始化的过程。
- 初始化完成之后,master写入一条就会传输一条给slave
- 在这个过程中,不会影响master的写入和slave的读取,因为在这段时间内,请求的是老的数据,只有同步完成之后,就会用新的数据
- 使用主从架构,master必须要开启持久化,如果不开启持久化,一旦master停掉,恢复之后内存中是没有数据的,这个时候会将slave的数据也清空掉
主从模式:
- 一主两从:不会在一台master下面挂多台slave,因为要做主从同步,要将数据同步到其他的slave,一旦slave比较多的时候,就会一直占用内网的带宽,一般使用主从一主两从就可以了
- 两个主从、树状:slave也可以做slave的master,减少同步的压力,让其中一个的slave去做这些事
配置一主两从架构
-
进入客户端,输入密码,查看主从配置信息
[root@localhost ~]# redis-cli 127.0.0.1:6379> auth 你的密码 127.0.0.1:6379> info replication
目前默认可以看到角色是主节点、连接的从节点为0 -
需要修改其余两台slave,master是不需要修改的
[root@localhost ~]# cd /usr/local/redis/ [root@localhost redis]# vim redis.conf
配置Redis.conf文件
replicaof 172.16.139.161 6379 #开启主从设置主服务器IP和端口
masterauth zk #设置主服务器密码
replica-read-only yes #从服务器只读 -
进入work目录,删除从服务器的aof和rdb文件
[root@localhost redis]# cd working/ [root@localhost working]# ll 总用量 4 -rw-r--r--. 1 root root 0 10月 25 10:06 appendonly.aof -rw-r--r--. 1 root root 92 10月 25 10:09 dump.rdb [root@localhost working]# rm appendonly.aof dump.rdb rm:是否删除普通空文件 "appendonly.aof"?y rm:是否删除普通文件 "dump.rdb"?y
-
重启slave服务器,查看主从配置,同时查看work目录
[root@localhost working]# /etc/init.d/redis_init_script stop [root@localhost working]# /etc/init.d/redis_init_script start [root@localhost working]# redis-cli 127.0.0.1:6379> auth zk OK 127.0.0.1:6379> info replication #Replication role:slave master_host:172.16.139.161 #主服务器节点 master_port:6379 #主服务器端口 master_link_status:up
127.0.0.1:6379> exit [root@localhost working]# ll 总用量 8 -rw-r--r--. 1 root root 92 10月 25 10:43 appendonly.aof -rw-r--r--. 1 root root 175 10月 25 10:43 dump.rdb
发现当前从服务器的aof和rdb文件又有了,这就是从主服务器同步过来的文件,当前的状态为一主一从模式了,当前的slave只能读取,不能写入。
-
同步骤配置第二台从服务器
-
查看主服务器的配置信息
127.0.0.1:6379> info replication # Replication role:master connected_slaves:2 slave0:ip=172.16.139.162,port=6379,state=online,offset=997,lag=1 slave1:ip=172.16.139.163,port=6379,state=online,offset=997,lag=1 master_replid:e386d8adeef590a3b9cf235d983cd30cefa8bede master_replid2:0000000000000000000000000000000000000000
配置成功
主从架构如果宕机
- 如果slave宕机,master在此期间进行了写操作,slave恢复之后,也可以全部的同步获取到数据,会把之前同步的数据也同样同步过来。
- 如果master宕机,slave会发现master宕机,但是不会变成master,主从直接本质是通过心跳检测,slave会向master发送ping,如果ping不到,说明master宕机了
127.0.0.1:6379> info replication # Replication role:slave master_host:172.16.139.161 master_port:6379 master_link_status:down master_last_io_seconds_ago:-1
无磁盘化复制
主从复制的原理,是master将数据写入磁盘,在从磁盘传输另外的slave,是通过磁盘之间的传输,随后slave会将数据读取到内存,这是默认的方式,还会有另外一种方式,无磁盘化复制。默认是关闭的
无磁盘化复制,从内存写入到另外一个内存,基于socket方式进行传输,基于网络的,为什么有这种方式,是因为磁盘的类型,如果是最普通的机械磁盘,I/O太慢,提高效率,或者内网传输效率高,使用无磁盘复制。
master会等待一段时间,等待slave全部连接过来,在进行传输,有一个delay的环节,这样就完成了一个传输
# -------------------------------------------------------
# WARNING: DISKLESS REPLICATION IS EXPERIMENTAL CURRENTLY 当前是实验性
# -------------------------------------------------------
repl-diskless-sync no 如果使用改为yes
repl-diskless-sync-delay 5 等待时间
Redis缓存过期处理
引子:
计算机内存有限,越大越贵,Redis的高并发高性能都是基于内存的,用硬盘的话GG
针对设置key的过期时间
已过期的key如何处理?
设置了expire的key缓存过期了,但是服务器的内存还是被占用,这是因为Redis所基于的两种删除策略
Redis有两种策略:
-
(主动)定时删除
Redis会定期、定时的做一个删除,它会抽查随机过期的key,抽查的时间默认是1S10次,这个可以设置的,如果发现抽查的key过期则清理删除。
为什么是随机抽取? 因为如果存储了大量数据,全部遍历一遍是非常影响性能的!# The range is between 1 and 500, however a value over 100 is usually not # a good idea. Most users should use the default of 10 and raise this up to # 100 only in environments where very low latency is required. hz 10
-
(被动)惰性删除
当客户端请求了一个已经过期的key的时候,那么Redis会检测这个key是否过期,如果过期了,则从内存中清除,然后返回一个nil,这种策略对CPU比较友好,不会有太多的损耗,但是内存占用会比较高。
所以,虽然key过期了,但是只要没有被Redis清理,那么其实内存还是会被占用的。
内存淘汰管理机制
如果内存被Redis缓存占用满了怎么办?
内存占满了,可以使用硬盘来保存,但是没意义,因为会影响Redis的性能
所以,当内存占用满了以后,Redis提供了一套缓存淘汰机制:MEMORY MANAGEMENT
############################## MEMORY MANAGEMENT ################################
# Set a memory usage limit to the specified amount of bytes.
# maxmemory <bytes> #当内存已使用率达到,则开始清理缓存
算法:
# volatile-lru -> Evict using approximated LRU among the keys with an expire set.
# allkeys-lru -> Evict any key using approximated LRU. #清除最少用的缓存,然后保存新的缓存(推荐使用)
# volatile-lfu -> Evict using approximated LFU among the keys with an expire set. #v开头针对设置过expire缓存时间的,清除最少用的旧缓存,然后保存新的缓存
# allkeys-lfu -> Evict any key using approximated LFU.
# volatile-random -> Remove a random key among the ones with an expire set.
# allkeys-random -> Remove a random key, any key. #所有的key随机删除,当然也会删除热门的key,所以不推荐使用
# volatile-ttl -> Remove the key with the nearest expire time (minor TTL) #在那些设置了expire过期时间的快要过期的缓存中,随机删除缓存
# noeviction -> Don't evict anything, just return an error on write operations. #旧缓存永不过期,新缓存设置不了,返回错误
# LRU means Least Recently Used #最少被使用 针对于时间的
# LFU means Least Frequently Used #最少被使用 针对于动作的,访问的次数,使用频率
# maxmemory-policy noeviction 设置缓存淘汰的机制 默认值