基本概念
Redis (Remote Dictionary Server),远程字典服务
是一个开源的 使用 ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供了多种语言的API。
Redis 会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了 maser-slave(主从)同步。
Redis 免费 并 开源,是如今最热门的 NoSQL 技术之一,也被称之为结构化数据库。
Redis 的应用
- 内存存储、持久化,(内存中是断电即失,所以持久化很重要)
- 效率高,可以用于高速缓存
- 发布订阅系统
- 地图信息分析
- 计时器、计数器
- ……
使用Redis
网页版 Try Redis
使用Redis客户端进行连接
redis-cli -p 6379
> PING
PONG
redis-cli -p (redis command line interface)
redis命令行接口
连接远程的redis server
-h
参数 输入服务器名称-p
参数 输入端口号
查看Redis 进程是否开启
ps -ef|grep redis
Linux 基本命令中,
ps
:查看当前系统正在执行的进程信息
ps -au:详细显示进程信息
ps -ef:查看父进程的信息
ps -ef|grep 进程名称:过滤进程信息
[ |:管道符;grep:查找文件中符合条件的字符串]
关闭Redis服务
> shutdown
> exit
基础
Redis 默认有16个数据库,默认使用的是第0个,可以使用 select 切换数据库
> select 9 #切换数据库
OK
> DBSIZE #查看数据库大小
查看数据库中所有的key
> key *
清空当前数据库 FLUSHDB
清空全部数据库的内容 FLUSHALL
redis 是将所有的数据全部放在内存中,所以说使用单线程去操作效率是最高的。
多线程(CPU上下文切换 是 很耗时的操作),对于内存系统来说,如果没有上下文切换效率是很高的。
Redis-Key
> EXISTS name # 判断当前key是否存在
> MOVE name # 移除当前key
> EXPIRE name seconds # 设置key的过期时间,单位是秒
> ttl name #查看当前key的剩余时间
> type name #查看当前key的类型
单线程模式
CPU并非瓶颈:多线程模型主要是为了充分利用多核CPU,让线程在IO阻塞时被挂起,让出CPU使用权,交给其他线程,充分提高CPU的使用率。但是这个场景在Rdeis中并不明显,因为CPU并不是Rdeis的瓶颈,Redis的所有操作都是基于内存的,处理事件极快,因此使用多线程 来切换线程提高CPU利用率的需求并不强烈。
内存才是瓶颈:单个Rdeis 实例对单核的利用率已经很好了,但是Redis的瓶颈在于内存。
复杂的Value类型:Redis 有丰富的数据结构,并不是简单的Key-Value型的NoSQL,其中常用的 Hash、Zset、List等结构在value很大时,CURD的操作会很复杂,如果采用多线程模式
在进行相同key操作时就需要加锁来进行同步,这样就可能造成死锁问题。
集群化扩展:目前的机器都是双核的,但是内存一般128GB/64GB ,而Redis 在使用内存60%以上稳定性就不如50%的性能高,因此,在数据较大时,当Redis作为主存,就必须使用多台机器构建集群化的Redis数据库系统,这样以来Redis的单线程模式又被集群化的处理所扩展了。
其它:单线程无论从开发和维护都比多线程要容易很多,并且也能提高服务的稳定性,无锁化处理让单线程的Redis在开发和维护上都具备相当大的优势。
Redis 持久化
持久化就是把内存中的数据写到磁盘中,防止服务宕机导致内存数据丢失。
在服务器发生宕机 时,作为内存数据库Redis里的所有数据将会丢失,因此Redis提供了持久化两个利器:RDB
和 AOF
。
RDB
RDB是Redis默认的持久化方式。按照一定的时间
将内存的数据以快照的形式
保存到磁盘中(Snapshot快照),对应产生的数据文件为 dump.rdb
. 通过配置文件中的save
参数来定义快照的周期。
默认情况:
save 900 1
save 300 10
save 60 10000
一分钟内改了1万次,或5分钟内改了10次,或15分钟内改了1次。
它恢复时是将快照文件直接读到内存里。
Redis 会单独 fork 一个子进程来进行持久化,会先将数据写到一个临时文件中,到持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能。
Fork的作用是复制一个与当前进程一样的进程。新进程的所有数据数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程。
如果需要进行大规模的数据恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更高效。RDB的缺点是最后一次持久化后的数据可能丢失。
RDB配置
Stop-writes-on-bgsave-error (默认yes)
如果配置成no,表示你不在乎数据不一致或者有其他的手段发现和控制。
rdbcompression (yes)
对于存储到磁盘中的快照,可以设置是否进行压缩存储。
如果是的话,redis会采用LZF算法进行压缩;
如果不想消耗CPU来进行压缩的话,可以设置为关闭此功能。
rdbchecksum (yes)
再存储快照后,还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约
10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。
dbfilename
dump.rdb
dir
CONFIG GET dir
命令 save
或者 bgsave
save时只管保存,其它不管,全部阻塞
bgsave redis会在后台异步进行快照操作,快照同时还可以响应客户端请求
可以通过lastsave命令获取最后一次成功执行快照的时间。
执行 flushall
命令,也会产生dump.rdb文件,但里面是空的,没有意义。
如何恢复
将备份文件(dump.rdb)移动到redis安装目录并启动服务即可
CONFIG GET dir获取目录
如何停止
动态所有停止RDB保存规则的方法:
redis-cli config set save ""
内存中的数据对象 rdbSave 磁盘中的RDB文件
------------------->
<-------------------
rdbLoad
已经存在RDB了,为什么还会出现AOF?
要知道新技术的出现,一定是为了弥补老技术的不足。
从上面了解到 在使用rdb进行保存的时候,如果redis服务器发生故障,那么最后一次备份的数据会丢失。所以,AOF出现试着来解决这个问题。
AOF
AOF(Append Only File持久化),则是将Redis 每次执行的写命令
记录到单独的日志文件中,当重启Redis时 就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
AOF保存的是 appendonly.aof
文件。
当两种方式同时开启时,数据恢复 Redis 会优先选择 AOF 恢复。
配置
appendonly no(默认是no,yes是开启aof持久化)
APPEND ONLY MODE
appendonly (no/yes)
appendfilename appendonly.aof
Appendfsync
Always:同步持久化 每次发生数据变更会被立即记录到磁盘,性能较差但数据完整性比较好
Everysec:出厂默认推荐,异步操作,每秒记录 如果一秒内宕机,有数据丢失。(默认)
No-appendfsynce-on-rewrite
重写时是否可以运用Appendfsync,使用默认no即可,保证数据安全性。
Auto-aof-rewrite-min-size
设置重写的基准值
Auto-aof-rewrite-percentage
设置重写的基准值
Rewrite
是什么
AOF采用文件追加方式,文件会越来越大,为了避免出现此种情况,新增了重写机制,
当AOF文件的大小超过锁设置的阈值时,redis就会启动AOF文件的内容压缩,只保留可以
恢复数据的最小指令集,可以使用命令bgrewriteaof。
重写原理
AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件最后再rename),
遍历新进程的内存中数据,每条记录有一条的Set语句。重写aof文件的操作,并没有读取旧的aof文件
而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。
触发机制
redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件
大于64M时触发
劣势
相同数据集的数据,aof文件要远大于rdb文件,恢复速度慢于rdb
aof运行效率要慢于rdb,每秒同步策略效率较好,不同步效率和rdb相同
如果一个系统里面同时存在RDB和AOF,两者是冲突还是协作?
协作(两者可以共存)
先加载的是 appendonly.aof
如果 appendonly.aof 文件损坏,如何修复:
redis-check-aof --fix appendonly.aof
总结
RDB持久化方式 能够在指定的时间间隔 对你的数据进行快照存储
AOF持久化方式 记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,
AOF命令以redis协议追加保存每次写的操作 到文件末尾。
redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大。
只做缓存
如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式
同时开启两种持久化方式
在通常情况下,redis重启时会优先载入AOF文件来恢复原始数据,
因为在通常情况下,AOF文件保存的数据集要比RDB文件保存的数据集完整。
RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。
那要不要只是用AOF?
不建议,因为RDB更适合用于备份数据库(AOF在不断变化不好备份) 留一手哈哈
事务
Redis事务的本质是:一组命令的集合。一个事务中的所有命令都会被序列化
,在事务的执行过程中会按照顺序执行。
Redis事务没有隔离级别的概念。
所有的命令在事务中,并没有直接被执行,只有发起执行命令的时候才会被执行 EXEC
.
Redis 单条命令具有原子性,但是事务不保证原子性。
Redis事务的三个阶段
- 开启事务
MULTI
- 命令入队
- 执行事务
EXEC
Redis事务的相关命令:
- MULTI:开启事务
- EXEC:执行事务
- DISCARD:取消事务
- WATCH:监控,(乐观锁)
- Redis 不支持回滚
- 如果在一个事务中 命令出现错误(编译异常),所有的命令都不会被执行
- 如果在一个事务中 运行错误(语法性错误),那么正确的命令会被执行
SpringBoot整合Redis
在SpringBoot2.x 之后,原来使用的jedis 被替换为了 lettuce
。
- jedis:采用直连,多个线程操作,是不安全的,如果想要避免不安全,使用 jedis pool 连接。
- lettuce:采用netty,实例可以在多个线程中进行共享,不存在线程不安全的情况。
自定义 RedisTemplete
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory
factory) {
// 我们为了自己开发方便,一般直接使用 <String, Object>
RedisTemplate<String, Object> template = new RedisTemplate<String,
Object>();
template.setConnectionFactory(factory);
// Json序列化配置
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new
Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// String 的序列化
StringRedisSerializer stringRedisSerializer = new
StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
集群
随着 Redis 客户端越来越多,一台Redis 不够,那就再多加几台—— 集群
图片来源
客户端的请求会通过 负载均衡算法,分散到各个Redis 服务器上,通过集群,实现了两个特性:
- 扩大缓存容量
- 提升吞吐量
Redis 主从复制
主从复制 是指将一台 Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master/leader),后者称为从节点(slave/follower)。
- 数据的复制是单向的,只能由主节点到从节点。
- Master 以写为主,Slave 以读为主。
- 默认情况下,每台Redis 服务器都是主节点,一个主节点可以有多个从节点,但一个从节点只能有一个主节点。
主从复制的作用:
- 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式
- 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速恢复故障
- 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,从节点提供读服务(即写Redis数据时 应用连接主节点,读Redis数据时 应用连接从节点)
- 高可用(集群)基石:主从复制还是哨兵和集群能够实现的基础
主从复制、读写分离
环境配置
127.0.0.1:6379> info replication # 查看当前库的信息
role:master # 角色
master connected_slaves:0 # 没有从机
开启多个Redis 服务 ,修改配置文件:
- 端口号
- pid 名字
- log文件名
- dump.rdb 名称
默认情况下,每台Redis服务器都是主节点,一般情况下,我们只需要配置从机。
比如,
一主二从 (主:6379)(从:6380、6381)
配置从机:
127.0.0.1:6380> SLAVEOF 127.0.0.1 6379 # SLAVEOF host 6379 找主机
OK
127.0.0.1:6380> info replication # Replication
role:slave # 当前角色是从机
master_host:127.0.0.1 # 可以的看到主机的信息
master_port:6379
学习时,我们使用命令方式配置,是暂时的;而在配置文件中配置,才是永久的。
- 主机可以写,从机只能读不能写
- 主机中的所有信息和数据,都会
自动
被从机保存
复制原理
slave
启动 成功连接 到master 后,会发送一个sync 同步命令;
master
接收到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕后,master 将传送整个数据文件到是slave,并完成一次完全同步。
- slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。
- master 继续将新的所有收集到的修改命令依次传给slave,完成同步
- 当slave 断开连接,只要是重新连接到master,一次全量复制将被自动执行,我们的数据一定可以在从机中看到。
如果master断开连接,我们可以使用 SLAVEOF no one
命令,让slave变为主机,此时其他的从机需要手动连接到这个新的master。
哨兵模式
哨兵模式
是一种特殊的模式,首先 Redis 提供了哨兵(sentinel)的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis 服务器响应,从而监控运行的多个Redis实例。
哨兵是 redis 集群机构中非常重要的一个组件,主要有一下功能:
集群监控
:负责监控 master 和slave 进程是否正常工作消息通知
:如果某个 Redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员故障转移
:如果 master 挂掉了,会自动将 slave 切换成 master ,然后通过发布订阅模式
通知其他 slave 服务器,修改配置文件,让它们切换主机配置中心
:如果故障转移发生了,通知 client 客户端新的master 地址
如果只有一个哨兵进程对Redis 服务器进行监控,可能会出现问题,因此,我们可以使用多个哨兵进行监控。
各个哨兵之间还会进行监控,这样就形成了多哨兵模式。
假设 master服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行 failover(故障切换)过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象称为主观下线。需要大部分的哨兵都同意才行,涉及到了分布式选举的问题。
当故障切换成功之后,就会通过发布订阅模式,让各个哨兵把自己监控的slave服务器 切换主机,这个过程称为客观下线。
哨兵至少需要 3 个实例,来保证自己的健壮性。
哨兵 + redis 主从的部署架构,是不保证数据零丢失的,只能保证 redis 集群的高可用性。
对于哨兵 + redis 主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足的测试和演练。
如果原来的master挂了,哨兵会从slave中投票选举出来一个新的master;需要注意的是,如果原来的master重新启动后,不再是master了,而是成为新的master下的slave。
复制的缺点(主从复制)
复制延迟:由于所有的写操作都是先在Master上操作,然后同步更新到Slave,所以从Msater同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。
文章学习自: