前记
1、NoSQL数据库2、Redis采用单线程+多路IO复用技术3、Redis的"两大维度,三大主线" 一、安装配置Redis二、常用五大数据类型
Key1、字符串(String)-- ArrayList2、列表(List)-- quickList3、集合(Set)-- Hash4、哈希(Hash)5、有序集合(Zset) 三、Redis配置文件详解
1、### NETWORK 网络相关配置###2、### SECURITY 安全###3、### GENERAL 通用###4、### LIMITS 限制### 四、Redis的发布与订阅
1、什么是发布与订阅2、流程3、命令行实现 五、Redis新数据类型
1、Bitmaps2、HypeLogLog3、Geospatial 六、Jedis操作
1、测试连接及jedis方法调用2、模拟验证码发送 七、Redis6与SpringBoot整合八、事务_锁机制_秒杀
1、Redis事务定义2、Muti,Exec,discard3、事务的错误处理4、事务冲突的问题
1、悲观锁(效率低)2、乐观锁3、WATCH key [key]4、UNWATCH5、Redis事务三大特性 5、秒杀
1、核心代码演示2、秒杀并发模拟3、连接超时问题4、超卖问题5、库存遗留问题 九、持久化之RDB(Redis Database)默认开启
优点缺点1、是什么2、备份是如何执行的3、Fork4、配置文件(### SNAPSHOT###)5、rdb备份与恢复 十、持久化之AOF(Append only File) 默认不开启
优点缺点1、是什么2、AOF持久化流程3、开启AOF(若RDB同时开启,Redis听AOF的)4、AOF启动/修复/恢复5、AOF同步频率6、Rewrite压缩 十一、RDB和AOF到底如何选择十二、主从复制
1、简介2、演示(一主两从)3、常用三招
1、一主二仆2、薪火相传3、反客为主 4、复制原理5、哨兵模式(sentinel) 十三、集群
前记 1、NoSQL数据库
1、概述
2、NoSQL适用场景
3、NoSQL不适用场景
2、Redis采用单线程+多路IO复用技术
与Memcache的三点不同:支持多数据类型,支持持久化,单线程+多路IO复用
1、多路复用指使用一个线程来检查多个文件描述符(Socket)的就绪状态,
比如调用select和poll函数,传入多个文件描述符,如果有一个文件描述符就位,则返回,否则阻塞直到超时。
得到就绪状态后进行真正的操作可以在同一个线程里执行,也可以启用线程执行(比如使用线程池)、
就是说多路IO复用起到一个监视的效果,就绪后,Redis直接执行,不需要等待
2、串行 vs 采用多线程+锁(Memcached)vs 单线程+多路IO复用的比较
1、串行:阻塞IO,一件事一件事的做,在做当前事时,不能做其他事,有等待 2、采用多线程+锁:非阻塞IO,一直重复做当前的事,没有等待 3、单线程+多路IO复用:做某件事情需要一定的时间,可以监视这件事,我们可以做其他的事。 多路IO复用有select,poll,epoll这些模式。 select监测数量能力有限。poll监测数量没有限制,但是需要一个一个核查。 配epoll监测数量没有限制,也不需要一个一个核查,直接看是否有一个正确的标识。
一、安装配置Redis
redis官网
1、下载最新版本tar.gz
2、安装C语言编译器gcc
yum install gcc 查看gcc版本 gcc --version
3、解压redis-tar.gz(解压到当前目录,本文的目录为/study,解压后为/study/redis-6.2.6)
tar -zxvf redis-6.2.6.tar.gz
4、编译为C文件(进入解压好的redis-6.2.6目录)
1、make 安装bin启动器到/study/redis-6.2.6目录下 2、make PREFIX=/study/redis-6.2.6 install
5、拷贝一份redis.conf到conf目录
cp /study/redis.6.2.6/redis.conf /study/redis.6.2.6/conf/redis.conf vim /study/redis.6.2.6/conf/redis.conf 开启后台运行 daemonize no 改为 yes
6、以配置文件后台启动(进入/study/redis-6.2.6/bin目录),连接客户端
./redis-server ../conf/redis.conf ./redis-cli
7、关闭服务进程
客户端输入 showdown ps -ef|grep redis kill -9 pid
二、常用五大数据类型 Key
keys * 查看所有key exists判断key是否存在 type 查看key是什么类型 del 删除指定key数据 unlink 根据value选择非阻塞删除,仅将key从keyspace元数据删除,真正的删除会在后续异步操作 expire 10 设置10秒过期时间 ttl 查看存活时间 -1永不过期 -2已过期 select ? 切换数据库 默认0-15 dbsize 查看当前数据库key数量 flushdb 清空当前库 flushall 清空所有库
1、字符串(String)-- ArrayList
1、简介
简单动态字符串(Simple Dynamic String SDS)
类似ArrayList,采用预分配冗余空间的方式减少内存的频繁分配
二进制安全,最大存储512M
2、数据结构
3、命令
1、设值
setv 设置单个键值 mset v v 设置多个键值 setex
2、查询
get查询单个键值 mget 查询多个键值 getrange 按下标查询键值 strlen 获取值的长度
3、数字值操作(原子操作:得益于redis单线程,不会被其他线程打断的)
incr k 储存的数字值增 1 incrby k ? 储存的数字值增 ? decr k 储存的数字值减 1 decrby k ? 储存的数字值减 ?
2、列表(List)-- quickList
1、简介
单键多值
2、数据结构:
3、命令
例:从左插入v1 v2 v3,实际从左到右为v3 v2 v1
1、插吐值
lpush/rpush k v1 v2 v3 从左/右插入v1,v2,v3 lpop/rpop k 从左/右吐出一个值,吐完值键消失 rpoplpush k1 k2 从k1右边吐一个值插到k2左边
2、查询(并非吐值)
lrange k s e 按照索引下标范围获得元素s start, e end lindex k i 按照索引下标获得元素 llen l 获取列表长度
3、插值
linsert k before "v1" "new v1" 在v1后面插入newv1
演示
4、特殊删除
lrem k 2 "v" 删除k左边的两个"v"
演示
5、替换
lset k i v 将k下标为i的值换为v
演示
1、简介
自动排重
String类型的无序集合
底层是一个value为null的hash表,增删查复杂度都为O(1)
一个算法,随着数据的增加,执行时间的长短,如果是O(1),数据增加,查找数据的时间不变
2、数据结构
是dict字典,字典使用哈希表实现的
Java中的HashSet的内部实现使用的是HashMap,只不过所有的value都指向同一个对象。
Redis的set结构也一样,内部也使用Hash结构,所有的value指向同一个对象
3、命令
sadd k v1 v2 v3 添加 smembers k 返回集合中所有元素 srandmember k n 随机从集合查询n个值 sismember k v 判断集合中是否有v scard k 查询集合中元素个数 srem k v1 v2 删除集合中的某个元素 spop k 随机从集合k吐出某个值(吐出的值消失) smover k1 k2 v1 把集合k1中的值v1移到k2 sinter k1 k2 返回两个集合的交集元素 sunion k1 k2 返回两个集合的并集元素 sdiff k1 k2 返回两个集合的差集元素(k1中的存在的,k2中不存在的)
4、哈希(Hash)
1、简介
Hash是一个String类型的field和value的映射表,类似于Map
2、数据结构
Hash类型对应数据结构为:ziplist(压缩列表),hashtable(哈希表),
当field-value长度较短且个数较少时,使用ziplist,否则使用hashtable
3、命令
hset k f v 给k中的f赋值v hsetnx k f v 给k中的f赋值v,仅当f不存在时 例 hset user:1001 id 1 设置user1001的id为1 hmest k f1 v1 f2 v2 给k中的多个f赋值v hexist k f 查看k中是否存在f hkeys k 查看k中的所有field hvals k 查看k中的所有value hget k f 从k集合f查询v hincrby k f n 为k中的f的v加上增量n5、有序集合(Zset)
1、简介
2、数据结构
跳跃表实现过程
3、命令
zadd k s v s v 将一个或多个member元素及其score值加入有序集k中 zrange k s e 返回k中下标在s-e中的元素 zrange k s e withscores 返回k中下标在s-e中的元素并带score(按评分从小到大) zrevrange k s e withscores 返回k中下标在s-e中的元素并带score(按评分从大到小) zrangebyscore k min max 返回评分在min max的元素(包含min,max)从小到大 zrevrangebyscore k max min 返回评分在max min的元素(包含min,max)从大到小 zincrby kv 为元素的scrore加上增量 zrem k v 删除k中指定元素 zcount k min max 统计个数,返回个数 zrank k v 返回v的排名,小的是第一名,排名从0开始
三、Redis配置文件详解 1、### NETWORK 网络相关配置###
1、bind
2、protected-mode
默认为yes,远程无法访问
修改为no,远程可以访问
3、Port
默认6379
4、tcp-backlog
5、timeout
1.requirepass
修改配置文件中的 requirepass 参数,
把前面 # 注释去掉,后面 foobared 改成自己想用的密码,
重启redis即可。
进入客户端输入
auth *** 连接3、### GENERAL 通用###
1.daemonize
2.pidfile
3.loglevel
4.logfile
5、database 16
数据库设置默认16个 0-154、### LIMITS 限制###
1.maxclients
2.maxmemory
3.maxmemory-policy
4.maxmemory-samples
四、Redis的发布与订阅 1、什么是发布与订阅 2、流程 3、命令行实现
1.打开一个客户端订阅channel1
subscribe channel1
2.打开另一个客户端,给channe11发布消息hello
publish channel1 hello
3.打开第一个客户端收到hello
五、Redis新数据类型 1、Bitmaps
1.简介
2、命令
汇总
1、setbit设置bitmaps中某个偏移量的值(0或1) *offset:偏移量从0开始 设置成功返回0 2、getbit 获取bitmaps中某个偏移量的值 3、bitcount [start end] 统计字符串从start字节到end字节比特值为1的数量 4、bitop and(or/not/xor) [k1 k2...] 获取多个k中的and(交集),or(并集),not(非),xor(异或) 的操作结果并将结果保存在destkey中 返回值为and(or/not/xor)后的数量
1、设置bitmaps中某个偏移量的值
setbit设置bitmaps中某个偏移量的值(0或1) *offset:偏移量从0开始 设置成功返回0
实例
2、getbit
getbit获取bitmaps中某个偏移量的值
3、bitcount(获取的数量是byte并非bit)
bitcount[start end] 统计字符串从start字节到end字节比特值为1的数量
4、bitop
bitop and(or/not/xor)[k1 k2...] 获取多个k中的and(交集),or(并集),not(非),xor(异或) 的操作结果并将结果保存在destkey中 返回值为and(or/not/xor)后的数量
演示
Bitmaps与set的对比
1、简介
2、命令
1、pfadd
如果执行命令后HLL估计的近似基数发生变化,则返回1,否则返回0
pfadd添加指定元素
2、pfcount
pfcount计算HLL的近似基数,返回数量
3、pfmerge
pfmerge将一个或多个HLL合并后的结果存在
3、Geospatial
1、简介
2、命令
1、geoadd
geoadd添加地理位置(经度,纬度,名称) geoadd city 19.21 32.23 guangzhou
2、geopos
geopos获取指定地区坐标值 geopos city guangzhou
3、geodist
geodist[m|km|ft|mi] 获取两个位置的直线距离 单位m默认值
4、georadius
georadiusradius [m|km|ft|mi] 以给定的经纬度为中心,找出某一半径radius内的元素
六、Jedis操作 1、测试连接及jedis方法调用
1、pom文件
redis.clients jedis 3.2.0
2、开放防火墙
systemctl status firewalld 查看防火墙状态 systemctl stop firewalld 关闭防火墙
3、配置文件
1、bind127.0.0.1 -》 #bind127.0.0.1 注释掉61行 2、protected-mode yes -》 protected-mode no 关闭保护模式
4、重启redis
1、ps -ef|grep redis 查看进程 2、kill -9 pid 杀掉进程
5、测试连接
public class JedisDemo01 {
public static void main(String[] args) {
Jedis jedis = new Jedis("192.168.88.129", 6379);
String value = jedis.ping();
System.out.println(value);
//输出PONG
}
}
6、测试String
public void demo1() {
Jedis jedis = new Jedis("192.168.88.129", 6379);
jedis.set("name", "jiali");
String name = jedis.get("name");
System.out.println(name);
Set keys = jedis.keys("*");
for (String key : keys) {
System.out.println("key:" + key);
}
}
2、模拟验证码发送
1、生成6位随机数
public static String getCode() {
Random random = new Random();
String code = "";
for (int i = 0; i < 6; i++) {
//取10之间的数字
int rand = random.nextInt(10);
code += rand;
}
return code;
}
2、每个手机每天只能发送三次,验证码放到redis中,设置过期时间
public static void sendCode(String phone) {
Jedis jedis = new Jedis("192.168.88.129", 6379);
jedis.auth("****");
//拼接key
//手机发送次数key
String countKey = phone + ":count";
//验证码key
String codeKey = phone + ":code";
//每个手机每天只能发送三次
String count = jedis.get(countKey);
if (count == null) {
//没有发送次数,第一次发送
//设置发送次数是1
jedis.setex(countKey, 24 * 60 * 60, "1");
} else if (Integer.parseInt(count) <= 2) {
//发送次数+1
jedis.incr(countKey);
} else if (Integer.parseInt(count) > 2) {
//发送三次,不能再发送
System.out.println("今天发送次数已经超过三次");
jedis.close();
//中断获取验证码后续操作
return;
}
//发送验证码放到redis
String code = getCode();
jedis.setex(codeKey, 120, code);
jedis.close();
}
3、验证码校验
public static void verifyCode(String phone, String inputCode) {
Jedis jedis = new Jedis("192.168.88.129", 6379);
jedis.auth("****");
String codeKey = phone + ":code";
String redisCode = jedis.get(codeKey);
if (redisCode.equals(inputCode)) {
System.out.println("成功");
} else {
System.out.println("失败");
}
}
4、验证
1、sendCode("10086");
2、进入redis-cli,查看keys *,查看验证码填入下面?
3、verifyCode("10086","?");
4、成功
七、Redis6与SpringBoot整合
1、pom
org.springframework.boot spring-boot-starter-data-redis org.apache.commons commons-pool2
2、配置文件
spring:
redis:
host: 192.168.88.129
port: 6379
password: ****
database: 0
#连接超时时间
timeout: 1800000
lettuce:
pool:
#最大连接数
max-active: 20
#最大阻塞等待时间(负数表示没限制)
max-wait: -1
#最大最小空间连接
max-idle: 5
min-idle: 0
3、RedisConfig
package com.laptoy.redis.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
RedisTemplate template = new RedisTemplate<>();
RedisSerializer redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setConnectionFactory(factory);
//key序列化
template.setKeySerializer(redisSerializer);
//value序列化
template.setValueSerializer(jackson2JsonRedisSerializer);
//value hashmap序列化
template.setHashKeySerializer(jackson2JsonRedisSerializer);
return template;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
//配置序列化(解决乱码问题),过期时间600秒
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(600))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializevaluesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}
}
4、测试Controller
@RestController
public class RedisTestController {
@Autowired
RedisTemplate redisTemplate;
@GetMapping("/test")
public String redisTest() {
redisTemplate.opsForValue().set("name", "jiali");
String name = (String) redisTemplate.opsForValue().get("name");
return name;
}
}
八、事务_锁机制_秒杀
1、Redis事务定义 2、Muti,Exec,discard 3、事务的错误处理
1、组队中某个命令出现报告错误,执行时整个的所有队列都会被回滚
2、执行阶段某个命令报错,则只有报错的不会执行,其他正常执行
4、事务冲突的问题
例子:同一个账户有10000块,三个人登录同一个账户同一时刻购买东西
客户端1
客户端2
被watch的客户端的key成功执行事务后,自动取消watch状态
1.客户端添加秒杀商品 iphone 10件
set sk:iphone:kc 10
2.获取随机用户id
public static String getUid() {
Random random = new Random();
String uid = "";
int rand = random.nextInt(20);
uid += rand;
return uid;
}
3.秒杀过程
public static void doSecKill(String uid, String pid) {
if (uid == null || pid == null) {
System.out.println("用户名或商品名不能为空");
}
Jedis jedis = new Jedis("192.168.88.129", 6379);
jedis.auth("****");
//1、库存key
String kcKey = "sk:" + pid + ":kc";
//2、用户key
String userKey = "sk:" + pid + ":user";
//3、判断秒杀是否开始
String kc = jedis.get(kcKey);
if (kc == null) {
System.out.println("秒杀未开始");
jedis.close();
//中止
return;
}
//4、判断用户是否重复秒杀
if (jedis.sismember(userKey, uid)) {
System.out.println("您已经秒杀成功,无法重复参与");
jedis.close();
//中止
return;
}
//5、判断库存数量
if (Integer.parseInt(kc) <= 0) {
System.out.println("秒杀已结束");
jedis.close();
//中止
return;
}
//6、秒杀过程
//6.1、库存-1
jedis.decr(kcKey);
//6.2、把秒杀用户添加到清单里
jedis.sadd(userKey, uid);
System.out.println("秒杀成功");
jedis.close();
}
4.测试
public static void main(String[] args) {
for (int i = 0; i <20 ; i++) {
doSecKill(getUid(), "iphone");
}
}
2、秒杀并发模拟
自行模拟tomcat测试,此处省略
1.使用工具ab模拟测试
centos6默认安装,centos7需要手动安装
2.安装ab
yum install httpd-tools
3.命令
ab --help ab http://.... -n 请求数量 -c 请求并发量 -p post请求时提交参数 -T 指定参数类型post/put
3、连接超时问题
1.使用数据库连接池解决–单例模式懒加载,双重校验锁
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class JedisPoolUtil{
private static volatile JedisPool jedisPool = null;
private JedisPoolUtil() {
}
public static JedisPool getJedisPoolInstance() {
if (null == jedisPool) {
synchronized (JedisPoolUtil.class) {
if (null == jedisPool) {
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(200);
poolConfig.setMaxIdle(32);
poolConfig.setMaxWaitMillis(100 * 1000);
//超时是否等待
poolConfig.setBlockWhenExhausted(true);
//测试连接状态 ping pong
poolConfig.setTestOnBorrow(true);
jedisPool = new JedisPool(poolConfig, "192.168.88.129", 6379, 60000, "****");
}
}
}
return jedisPool;
}
public static void release(JedisPool jedisPool, Jedis jedis) {
if (null != jedis) {
jedisPool.returnResource(jedis);
}
}
}
2.测试连接
JedisPool jedisPoolInstance = JedisPoolUtil.getJedisPoolInstance(); Jedis jedis = jedisPoolInstance.getResource();4、超卖问题
1.乐观锁解决
代码演示
1.注释秒杀过程
//6、秒杀过程 //6.1、库存-1 //jedis.decr(kcKey); //6.2、把秒杀用户添加到清单里 //jedis.sadd(userKey, uid);
2.加了乐观锁
jedis.watch(kcKey); Transaction multi = jedis.multi(); multi.decr(kcKey); multi.sadd(userKey, uid); List
5、库存遗留问题
1.Lua脚本
测试代码
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.LoggerFactory;
import ch.qos.logback.core.joran.conditional.ElseAction;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.ShardedJedisPool;
import redis.clients.jedis.Transaction;
public class SecKill_redisByscript {
private static final org.slf4j.Logger logger =LoggerFactory.getLogger(SecKill_redisByscript.class) ;
public static void main(String[] args) {
JedisPool jedispool = JedisPoolUtil.getJedisPoolInstance();
Jedis jedis=jedispool.getResource();
System.out.println(jedis.ping());
Set set=new HashSet();
// doSecKill("201","sk:0101");
}
static String secKillscript ="local userid=KEYS[1];rn" +
"local prodid=KEYS[2];rn" +
"local qtkey='sk:'..prodid..":qt";rn" +
"local usersKey='sk:'..prodid..":usr";rn" +
"local userExists=redis.call("sismember",usersKey,userid);rn" +
"if tonumber(userExists)==1 then rn" +
" return 2;rn" +
"endrn" +
"local num= redis.call("get" ,qtkey);rn" +
"if tonumber(num)<=0 then rn" +
" return 0;rn" +
"else rn" +
" redis.call("decr",qtkey);rn" +
" redis.call("sadd",usersKey,userid);rn" +
"endrn" +
"return 1" ;
static String secKillscript2 =
"local userExists=redis.call("sismember","{sk}:0101:usr",userid);rn" +
" return 1";
public static boolean doSecKill(String uid,String prodid) throws IOException {
JedisPool jedispool = JedisPoolUtil.getJedisPoolInstance();
Jedis jedis=jedispool.getResource();
//String sha1= .secKillscript;
String sha1= jedis.scriptLoad(secKillscript);
Object result= jedis.evalsha(sha1, 2, uid,prodid);
String reString=String.valueOf(result);
if ("0".equals( reString ) ) {
System.err.println("已抢空!!");
}else if("1".equals( reString ) ) {
System.out.println("抢购成功!!!!");
}else if("2".equals( reString ) ) {
System.err.println("该用户已抢过!!");
}else{
System.err.println("抢购异常!!");
}
jedis.close();
return true;
}
}
九、持久化之RDB(Redis Database)默认开启 优点
(1)节省磁盘空间(2)恢复速度快 缺点
(1) Fork的时候,内存的数据被克隆了一份,大致2倍的膨胀率需要考虑(2)虽然fork时使用了写时拷贝技术,但是如果数据庞大比较消耗性能(3)在备份周期一定间隔时间做一次备份,但是如果Redis意外down掉的话,就会丢失最后一次修改 1、是什么
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照
Redis将内存存储和持久化存储相结合,即可提供数据访问的高效性,又可保证数据存储的安全性。
当满足条件时,redis需要执行RDB的时候服务器会执行以下操作:
1.redis调用系统的fork()函数创建一个子进程2.子进程将数据集写入一个临时的RDB文件3.当子进程完成对临时的RDB文件的写入时,redis用新的RDB文件来替换原来旧的RDB文件,并将旧的RDB文件删除
redis在进行快照的过程中不会对RDB文件进行修改,只有快照结束后才会将旧快照替换成新快照,也就是说任何时候RDB都是完整的
3、Fork 4、配置文件(### SNAPSHOT###)1.Save
1、默认1分钟内改了1万次,或5分钟内改了100次,或15分钟改了1次执行
2、如何禁用
不设置save指令,或者给save传入空字符串
3、动态禁止
redis-cli config set save ""
2.Save VS bgSave
1、save:只管保存,其他不管,全部阻塞。手动保存,不建议
2、bgsave:Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。
可以通过lastsave命令获取最后一次成功执行快照的时间
3.stop-writes-on-bgsave-error 停止写入
当Redis无法写入磁盘的话,直接关掉Redis的写操作。推荐开启
4.rdbcompression 压缩文件
对于存储到磁盘中的快照,可以设置是否进行压缩存储,redis会采用LZF算法进行压缩。推荐开启
缺点:消耗CPU
5.rdbchecksun 检查完整性
存储快照后,redis可以使用CRC64算法进行数据校验。推荐开启
缺点:消耗CPU
config get dir 查看文件目录(xxx.rdb文件放到该目录自动进行恢复)
十、持久化之AOF(Append only File) 默认不开启 优点
1、备份机制更稳健,丢失数据概率较低2、可读的日志文件,通过操作AOF文件,可以处理误操作3、AOF以appen-only(追加)的模式写入,所以没有任何磁盘寻址的开销,写入性能非常高。 缺点
1、比RDB占用更多磁盘空间2、恢复备份速度慢,不适合做冷备3、每次读写都同步,有一定的性能压力4、存在个别bug,造成恢复不能
bin目录输入 redis-check-aof --fix xxx.aof可以修复一些操作
1、是什么
1、以日志的形式来记录每个写操作2、只许追加文件但不能改写文件3、Redis启动之初会重新构建数据 2、AOF持久化流程
1、客户端的请求命令会被append追加到AOF缓存区中2、AOF缓存区根据AOF持久化策略[always,everysec,no]将操作sync同步到AOF文件3、AOF文件超过重写策略或手动重写时,会对AOF文件进行rewrite重写,压缩AOF文件容量4、Redis服务重启时,重写load加载AOF文件的写操作达到数据恢复的目的 3、开启AOF(若RDB同时开启,Redis听AOF的)
appendonly yes 开启AOF appendfilename "xxx.aof" 自定义文件名称4、AOF启动/修复/恢复
1、恢复
config get dir 查看文件目录(xxx.aof文件放到该目录自动进行恢复)
2、异常修复(bin目录)
redis-check-aof --fix xxx.aof 继续输入y5、AOF同步频率
appendfsync always 始终同步。性能较差但完整性好 appendfsync everysec 每秒同步。如果宕机,本秒的数据可能丢失 appendfsync no 不主动同步,把同步时机交给操作系统6、Rewrite压缩
set k1 v1 + set k2 v2 压缩成 set k1 v1 k2 v2
1、auto-aof-rewrite-percentage:设置重写的基准值,文件达到100%开始重写 2、auto-aof-rewrite-min-size:设置重写的基准值,最小文件64M,达到这个值开始重写
例如:
文件达到70MB开始重写,重写为50MB,下次当100MB开始重写
系统载入时或者上次重写完毕时,Redis会记录此时AOF大小,设为base_size
如果Redis的下一次AOF大小>=base_size+base_size*100%且当前大小大于64MB时开始重写
十一、RDB和AOF到底如何选择
(1)不要仅仅使用RDB这样会丢失很多数据。(2)也不要仅仅使用AOF,因为这一会有两个问题,第一通过AOF做冷备没有RDB做冷备恢复的速度快;第二RDB每次简单粗暴生成数据快照,更加健壮。(3)综合AOF和RDB两种持久化方式,用AOF来保证数据不丢失,作为恢复数据的第一选择;用RDB来做不同程度的冷备,在AOF文件都丢失或损坏不可用的时候,可以使用RDB进行快速的数据恢复。
十二、主从复制 1、简介
读写分离–主机写 从机读容灾快速恢复主机更新后根据配置和策略,自动同步到从机 2、演示(一主两从)
1.创建msconf文件夹存放主从配置文件
2.复制一份redis.conf到msconf
开启守护进程 daemonize yes关闭AOF持久化 appendonly no
3.配置三个配置文件
include /pidfile /portdbfilename
4.开启服务
5.查看服务器状态
info replication
6、配置从机
slaveof 127.0.0.1 port3、常用三招
1、从服务器如果停止了,重启变为master,需要重新执行该命令变为slave2、主服务器如果停止了,重启还是连接着两台从服务器(你大哥还是你大哥) 1、一主二仆
一台Master多台Slave
2、薪火相传Master的slave可以被另一台服务器slaveof
3、反客为主Master挂掉后其中一台slave执行
slaveof no one
成为新的Master
1、slave启动成功连接到master后会发送一个sync同步命令2、当master接到slave发送过来的同步消息,首先把master数据进行持久化成rdb文件,把rdb文件发送到slave,slave拿到rdb文件进行读取3、每次主服务器进行写操作后,和从服务器进行数据同步
1、全量复制:slave接收到数据库文件数据后,将其存盘并加载到内存中
2、增量复制:master继续将新的所有收集到的修改命令依次传给slave
3、但只要时重新连接master,一次完成同步(全量复制)将被自动执行
反客为主的自动版,Master故障自动从Slave选出新的Master
1.新建sentinel.conf文件到/study/redis-6.2.6/msconf/
mymaster为监控对象起的服务器名称 ,1为至少有多少个哨兵同意迁移的数量
sentinel monitor mymaster 127.0.0.1 6379 1
2.启动哨兵
./redis-sentinel /study/redis-6.2.6/msconf/sentinel.conf
3.身份切换
当Master宕机,重新选举一个Slave成为新的Master,旧的Master重启后也成为Slave
4.选举优先级 redis.conf
replica-priority 100 值越小优先级越高
1、选择优先级靠前的2、选择偏移量最大的(获得原主机数据最全的)3、选择runid最小的(每个redis实例启动后随机生成40为runid)
5.复制延迟
由于所有写操作都在Master执行,然后同步更新到Slave,所以从Master同步到Slave机器有一点的延迟,当系统很繁忙的时候,延迟问题更严重,Slave机器的增加也会使这个问题更严重
十三、集群



