什么是redis
是一种基于内存的key-value数据库,用作数据库和缓存。基础的数据类型只有5种;string,list,hash,set和SortedSet(有序集合)。额外还支持HypeLogLog(统计redis的基数,使用很小的内存就可以实现很大数据的统计,会有误差。实际是一种算法的支持),Geo(Redis的地理数据的存储,可以两地的经纬度计算距离和搜索一点范围的其他地名,也是一种算法支持)等。使用场景,缓存,抽奖,发布式session,排行榜,计数,附近地理等。
redis的安装(在线安装)
yum install -y gcc-c++
wget http://download.redis.io/releases/redis-5.0.3.tar.gz
tar -zxvf redis-5.0.3.tar.gz
make install PREFIX=/usr/softOPT/redis
bin/redis-server ../redis.conf
redis的数据结构
redis中的字符串是二进制安全的。使用的是c语言的char类似但是这个char会吧 ‘\0’认为是结束符,所以不能存储比如图形吗,视频等。但是redis使用的是sds(simple dynamic string)。不会将 ‘\0’读作结束符号。
redisTemplate.opsForValue().set("name","小J", (long) Math.ceil(1000L + Math.random() * 1000),TimeUnit.SECONDS);
持久化就是将内存中的数据写入磁盘,如果有aof方式,优先使用aof恢复。
save:是同步执行,会阻塞其他客户端命令,但不会消耗内存资源。bgsave是异步执行,会生成fork子进程器执行持久化,不会阻塞客户端其他命令,但是会消耗内存资源。
根据配置redis.conf的策略进行持久化;如
优点:
1. 文件比较小
2. 对redis的性能影响相对小
3. 恢复快
缺点:
1. 数据不全面
2. 如果生成的快照比较大,会使得客户端稍微有卡顿。因为有fork子进程去做持久化
有三种追加方式
appendfsync always 每次有新命令就追加到aof中,最慢但是最安全。
appendfsync everysec 将在一秒内执行的命令追加到aof文件中。和RDB速度差不多,但是在发生故障后只丢失一秒的数据(默认)
appendfsync no 从不进行fync,交给操作系统,更快但是也更不安全
优点:
1. 数据比较全,最多也就丢失1秒的数据
2. 追加的方式去写,省去磁盘寻址,写入性能高
3. 使用非常可读的形式追加数据,比较适合性数据误删的紧急恢复了
缺点:
1. 文件大
2. 开启aof方式后,redis的QPS要比只RBD支持时要低,因为每秒都要去异步刷新一次日志
由于redis实现的pub/sub模式,所以任何一台从服务器可以订阅任何主服务器的频道,实现同步复制。而且从服务器可以订阅任何从服务器的主服务器所以redis可以实现单层树结构的复制。
全量复制(第一次建立socket连接的时候进行)
1. slave跟master建立socket长连接,并发送psync【见下面的注意点】命令同步数据
2. master收到psync命令后,执行bgsave命令,生产最新的RBD文件,并发送给slave。随之发送replid_id和offset
3. master将生成RDB文件期间的命令写入复制积压缓冲区(replication backlog buffer)并发送给slave
4. slave将master发送过来的buffer和RDB生成新的RDB文件并加载到内存中
5. slave初始化完成
其中 replid是这个 master 的复制 ID,对于master来说和4.0以前的run_id是一样的意义。但对于slave来说,它保存的复制 ID(即 replid) 表示当前正在同步的 master 的复制 ID 。master_replid 2 则表示前一个 master 的复制 ID(如果它之前没复制过其他的 master,那这个字段没用),这个在主从角色发生改变的时候会用到 。而 offset 则是 master 当前的复制偏移量,slave 会将这个值作为自己的初始化偏移量
示意图:
部分复制
部分复制是4.0版本后出现的为了优化slave由于网络等原因断开重新连接之后减轻master复制压力的方案。
在slave重新连接到master之后,将offset和run_id也发送给master,master对比。如果run_id一致说明断开重新连接的slaver,如果offset一致,则进行进行部分复制。只要其中两个一个不一致则进行全量复制。
1. slave跟master建立socket长连接,并发送psync【见下面的注意点】命令同步数据,并发送replid_id和offset
2. master对比replid_id和offset
3. 一致则将rb_buffer中的命令发送给slave。不一致将进行一次全量复制
示意图:参考上图
master的配置文件不需要修改
修改slave的配置文件(4.0以前好像是slaveof)
replicaof 192.168.160.146 6379 #master的ip port
replica-serve-stale-data yes #yes跟master断开后继续响应,no是除去指定命令外返回一个错误信息
replica-read-only yes #设置slave是否只读
直接启动slave和master就可以了
注意点
2.8 版本之前 Redis 复制采用 sync 命令,无论是第一次主从复制还是断线重连后再进行复制都采用全量同步,成本高
2.8 ~ 4.0 之间复制采用 psync 命令,这一特性主要添加了 Redis 在断线重连时候可通过 offset 信息使用部分同步
4.0 版本之后也采用 psync,相比于 2.8 版本的 psync 优化了增量复制,这里我们称为 psync2,2.8 版本的 psync 可以称为 psync1
redis集群是由多个master-slave组成的,具有高可用,复制和分片(数据没有采用一致的hash,而是使用hash slot)等特性。
redisCluster可以挂载n多个master节点,并且每个master节点上可以挂载n多个slave节点的集群模式。
redis实例的分派方法
redis没有采用一致性的hash,而是使用了hash slot的方式。hash slot是有限的在0-16383之间。在一个redis集群中,所有的slot都会完全分派给所有的master集群。比如有3个mastermaster,则每个master节点管理5461个slot。【也可以使用 cluster addslots 0 将0slot分派给当前的实例】。
redis的key映射方式
当一个要被存储在redis集群的时候,先将其发送给任意的master节点,然后使用 HASH_SLOT = CRC16(key) mod 16384算法得到这个slot如果这个master正好在维护这个slot则就地处理这个key,否则返回一个重定向的命令让去正确的实例去操作。
一般情况下,在本地会缓存一个slot的维护列表,直接在本地进行计算然后直接去正确的redis实例操
作。如果还是错误会重新计算并且刷新本地缓存。
数据分片
redis会将所有的数据分派不同的redis上,即只保留一份数据。
高可用
使用主从复制模型保证master节点不可用的时候,可以快速将从节点升级为主节点,保证系统的高可用。换句话说,redisCluster集成了主从复制和哨兵模式
Slot机制还有一个很明显的优势,就是在处理并发的场景,因为它将数据集进行了分割,实际上减小了锁的粒度,从而扩大了并发度。Java中的ConcurrentHashMap容器是应用这种机制来实现并发的典型的例子
修改下面那个配置内容
cluster-enabled yes
cluster-node-timeout 15000 #集群超时时间
cluster-config-file nodes-port.conf 【port是这个实例对应的端口号。如果配置文件中没有这个选项可以不加,可以自己生成好像】
使用任意实例的bin/redis-cli命令执行: redis-cli --cluster create 192.168.43.129:6380 192.168.43.129:6381 192.168.43.129:6382 192.168.43.129:63800 192.168.43.129:63801 192.168.43.129:6382 --cluster-replicas 1
具体的redis-cli的命令参考文章redis-cli命令参考
6. 验证cluster
进入任意一个redis实例
cluster info(查看集群信息)
cluster nodes(查看节点列表)
(3)进行数据操作验证
redisCluseter01:0>set name 小J
OK
redisCluseter02:0>get name
小J
(4)关闭集群则需要逐个进行关闭,使用命令: /usr/local/redis-5.0.2/bin/redis-cli -c -h 192.168.43.129 -p 638* shutdown
在创建集群的时候不要使用127.0.0.1这个ip否则在外部访问不了
很显然,单个哨兵会存在自己挂掉而无法监控整个集群的问题,所以哨兵也是支持集群的,我们通常用三台哨兵机器来监控一组redis集群。
sentinel的组件功能
集群监控:负责监控 Redis master 和 slave 进程是否正常工作。
消息通知:如果某个 Redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员。
故障转移:如果 master node 挂掉了,会自动转移到 slave node 上。
配置中心:如果故障转移发生了,通知 client 客户端新的 master 地址。
搭建redis-sentinel(经典的三个哨兵)
#sentinel监控的IP 端口号 sentinel通过投票后认为mater宕机的数量,此处为至少2个
sentinel monitor mymaster 192.168.132.128 7000 2
#20秒ping不通主节点的信息,主观认为master宕机
sentinel down-after-milliseconds mymaster 30000
#故障转移后重新主从复制,1表示串行,>1并行
sentinel parallel-syncs mymaster 1
#故障转移开始,60秒内没有完成,则认为转移失败
sentinel failover-timeout mymaster 600000
redis-sentinel redis.conf
在springboot2.x中集成了jedis,不需要大量的配置。对jedis进行了大量的封装形成RedisTemplate类,使我们更加分别的操作redis。
加入pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
spring.redis.host=192.168.43.129
spring.redis.port=6379
spring.redis.client-name=testRedis
spring.redis.password=''"
spring.redis.timeout=5000
spring.redis.jedis.pool.max-wait=0
spring.redis.jedis.pool.max-active=8
spring.redis.database=1
@Autowired
private RedisTemplate<String, Object> redisTemplate;
spring:
redis:
timeout: 6000ms
password:
cluster:
max-redirects: 3 # 获取失败 最大重定向次数
nodes: 192.168.43.129:6380,192.168.43.129:6381, 192.168.43.129:6382
lettuce:
pool:
max-active: 1000 #连接池最大连接数(使用负值表示没有限制)
max-idle: 10 # 连接池中的最大空闲连接
min-idle: 5 # 连接池中的最小空闲连接
max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)
package com.wdg.redis;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* @author wdg
* @date 2020/5/18 16:43
* @Description
*/
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
@Autowired
private RedisTemplate<String, Object> redisTemplate;
redis由于直接在内存中运行,所以其处理速度是特别快的,和memcached相比,redis丰富的数据类型和可持久化的模式都大大的增加了redis的实用性,作为传统关系型数据库补充很好的解决了现代互联网的巨大流量的对系统的冲击, 理论上导致redis性能瓶颈不在是硬件而是网络IO。
在然后选择集群还是主从复制的模式的时候,一般如果数据量只有几十G的时候,搭建master就可以了,因为一般服务器的内存大概在32G。如果数据量超出32G则考虑搭建Cluster,如有100G的数据量可以搭建5台master。
未完待续。。。