Redis采用的是基于内存的单进程 单线程模型 的KV数据库,由C语言编写。官方提供的数据是可以达到100000+的qps
应用场景:
# 使用wget命令下载安装包,安装wget命令下载工具
yum -y install wget
# 创建redis目录
mkdir /usr/local/redis -p
cd /usr/local/redis
# 下载安装包
wget http://download.redis.io/releases/redis-6.2.5.tar.gz
通过官网下载指定版本,然后使用传输文件工具上传到Linux服务器的/usr/local/redis 路径上(没有创建mkdir /usr/local/redis -p)
https://download.redis.io/releases
#使用命令查看gcc/g++版本
gcc -v
g++ -v
#gcc 版本 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC)
# 先进入此目录
cd /etc/scl/conf
# 使用SCL管理, Red Hat 软件包源
yum -y install centos-release-scl
# 安装devtoolset-9依赖包
yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils
# 临时有效,退出 shell 或重启会恢复原 gcc 版本
scl enable devtoolset-9 bash
# 永久有效
# echo "source /opt/rh/devtoolset-9/enable" >>/etc/profile
# 查看gcc/g++版本
gcc -v
g++ -v
#gcc version 9.3.1 20200408 (Red Hat 9.3.1-2) (GCC)
# 回到redis目录
cd /usr/local/redis
# 解压redis安装包
tar -zxvf redis-6.2.5.tar.gz
# make编译
cd redis-6.2.5
make
# 编译成功
#Hint: It's a good idea to run 'make test' ;)
# 当前终端运行redis服务进程
./src/redis-server
#29211:C 23 Dec 2021 13:58:49.608 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
#29211:C 23 Dec 2021 13:58:49.608 # Redis version=6.2.5, bits=64, commit=00000000, modified=0, pid=29211, just started
#29211:C 23 Dec 2021 13:58:49.608 # Warning: no config file specified, using the default config. In order to specify a config file use ./src/redis-server /path/to/redis.conf
#29211:M 23 Dec 2021 13:58:49.613 * Increased maximum number of open files to 10032 (it was originally set to 1024).
#29211:M 23 Dec 2021 13:58:49.613 * monotonic clock: POSIX clock_gettime
# _._
# _.-``__ ''-._
# _.-`` `. `_. ''-._ Redis 6.2.5 (00000000/0) 64 bit
# .-`` .-```. ```\/ _.,_ ''-._
# ( ' , .-` | `, ) Running in standalone mode
# |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379
# | `-._ `._ / _.-' | PID: 29211
# `-._ `-._ `-./ _.-' _.-'
# |`-._`-._ `-.__.-' _.-'_.-'|
# | `-._`-._ _.-'_.-' | https://redis.io
# `-._ `-._`-.__.-'_.-' _.-'
# |`-._`-._ `-.__.-' _.-'_.-'|
# | `-._`-._ _.-'_.-' |
# `-._ `-._`-.__.-'_.-' _.-'
# `-._ `-.__.-' _.-'
# `-._ _.-'
# `-.__.-'
#
#29211:M 23 Dec 2021 13:58:49.616 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
#29211:M 23 Dec 2021 13:58:49.616 # Server initialized
#29211:M 23 Dec 2021 13:58:49.616 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
#29211:M 23 Dec 2021 13:58:49.616 * Ready to accept connections
# 按键Ctrl + C 终止Redis服务进程(先不要按)
#Redis is now ready to exit, bye bye...
打开另外一个终端
# 打开另外一个终端
# 查看进程信息,一行color=auto 6379(没有运行),两行才算运行
ps aux | grep redis
#root 29211 0.1 0.2 162480 10256 pts/0 Sl+ 13:58 0:02 ./src/redis-server *:6379
#root 29536 0.0 0.0 112724 984 pts/1 S+ 14:21 0:00 grep --color=auto redis
# 查看端口是否在运行,显示行数是在运行,没显示(没运行)
netstat -tunlp | grep 6379
#tcp 0 0 0.0.0.0:6379 0.0.0.0:* LISTEN 29211/./src/redis-s
#tcp6 0 0 :::6379 :::* LISTEN 29211/./src/redis-s
回到原来的终端
# 回到原来的终端,终止redis服务进程
按键Ctrl + C
#Redis is now ready to exit, bye bye...
# 修改redis.conf文件,按键/ 输入需要查看修改的内容,然后回车,如:/protected-mode
vim redis.conf
#运行远程连接需要调整,注释掉#bind 127.0.0.1 -::1
#bind 127.0.0.1 -::1
#关闭密码验证,yes 修改为 no
protected-mode no
#设置后端启动 no 修改为 yes
daemonize yes
# 占时不设置密码
#(无需)关闭redis设置密码(默认是注释掉的,就是关闭)
# requirepass foobared
#(需要)开启redis设置密码,还要开启密码验证
requirepass 123
#开启密码验证,no 修改为 yes
protected-mode yes
# 运行redis服务 加上redis.conf配置文件
./src/redis-server redis.conf
# 查看redis服务进程信息,以及 端口是否在运行
ps aux | grep redis && netstat -tunlp | grep 6379
#root 32082 0.2 0.2 162480 9900 ? Ssl 15:22 0:00 ./src/redis-server *:6379
#root 32114 0.0 0.0 112724 984 pts/1 S+ 15:23 0:00 grep --color=auto redis
#tcp 0 0 0.0.0.0:6379 0.0.0.0:* LISTEN 32082/./src/redis-s
#tcp6 0 0 :::6379 :::* LISTEN 32082/./src/redis-s
# 关闭防火墙,自己玩没必要开启防火墙
# 查看防火状态(Active: inactive (dead)无效; Active: failed关闭; Active: active (running)运行)
systemctl status firewalld
# 关闭防火墙
systemctl stop firewalld
# 禁用开机启动
systemctl disable firewalld
# 启动防火墙
#systemctl start firewalld
# 开启开机启动
#systemctl enable firewalld
# 重启防火墙
#systemctl restart firewalld
# 要开启防火墙远程访问,需要开放redis端口 6379
# 查看是否开放端口
firewall-cmd --list-port
# 开放6379端口
firewall-cmd --add-port=6379/tcp --permanent
#success
# 关闭6379端口
firewall-cmd --remove-port=6379/tcp --permanent
# 重新加载信息
firewall-cmd --reload
#success
使用Redis可视化工具 Redis Desktop Manager v.0.8.8.384,网上找一个多的很,使用的版本号是v.0.8.8.384
# 先关闭redis服务 pwd /usr/local/redis/redis-6.2.5
./src/redis-cli -p 6379 shutdown
# 查看redis服务是否关闭
ps aux | grep redis && netstat -tunlp | grep 6379
#已关闭
#root 35336 0.0 0.0 112724 988 pts/1 S+ 17:35 0:00 grep --color=auto redis
# 设置开机自动启动Redis服务(systemd对应的进程管理命令是systemctl)
vim /etc/systemd/system/redis-server.service
# 输入内容:
[Unit]
Description=redis-server
After=network.tartget
[Service]
Type=forking
ExecStart=/usr/local/redis/redis-6.2.5/src/redis-server /usr/local/redis/redis-6.2.5/redis.conf
PrivateTmp=true
[Install]
WantedBy=multi-user.target
# 开机自动启动redis-server 与 redis-server重启
systemctl daemon-reload
systemctl enable redis-server
systemctl restart redis-server
systemctl status redis-server
#● redis-server.service - redis-server
# Loaded: loaded (/etc/systemd/system/redis-server.service; enabled; vendor preset: disabled)
# Active: active (running) since 四 2021-12-23 17:45:09 CST; 2s ago
# Process: 35518 ExecStart=/usr/local/redis/redis-6.2.5/src/redis-server /usr/local/redis/redis-6.2.5/redis.conf (code=exited, status=0/SUCCESS)
# Main PID: 35521 (redis-server)
# Tasks: 5
# Memory: 8.3M
# CGroup: /system.slice/redis-server.service
# └─35521 /usr/local/redis/redis-6.2.5/src/redis-server *:6379
#
#12月 23 17:45:09 localhost.localdomain systemd[1]: Starting redis-server...
#12月 23 17:45:09 localhost.localdomain systemd[1]: Started redis-server.
# 启动服务 与 查看状态
systemctl start redis-server
systemctl status redis-server
# 停止 与 重启
systemctl stop redis-server
systemctl restart redis-server
# 开启开机启动 与 禁用开机启动
systemctl enable redis-server
systemctl disable redis-server
String(字符串)
# redis-cli客户端连接redis-server服务器
/usr/local/redis/redis-6.2.5/src/redis-cli -p 6379
# 新增或修改指定key的值
set key val
# 获取指定key的值
get key
# 删除指定key的值
del key
List(列表)
# 从头部开始添加
lpush key v1 v2 ....
# 从尾部添加
rpush key v1 v2 ...
# 查看列表 lrange key start end (start从0开始,负数表示到链表尾部的位置,-1链表尾部,-2尾部倒数第二)
lrange key 0 -1
# 删除链表第一个元素
lpop key
# 删除链表倒数第一个元素
rpop key
# 获取链表元素个数
llen key
# 扩展命令(lpushx头部/rpushx) 仅当参数中指定的key存在时,向关联的list的头部或尾部插入value。如果不存在,将不进行插入
lpushx key value
rpushx key value
Hash(字典)
# 添加或修改指定key
hset key field val
# 设置多个key
hmset key f1 v1 f2 v2
# 获取值
hget key field
# 获取多个值
hmget key f1 f2
# 删除指定值
hdel key f1 f2
# 删除整个列表
del key
Set(集合)
# 添加删除元素
sadd k m1 m2
srem k m1 m2
# 获取set中所有成员
smembers k
# 求差集合(A、B两个集合,获取属于A但是B中没有的元素)
sdiff A B
# 求交集(A、B两个集合,AB两个集合都有的元素)
sinter A B
# 求并集
sunion A B
Sorted Set(有序集合)
#添加元素
zadd ke 10 m1 9 m2 8 m3
#获得元素,
#获取指定成员对应的分数
zscore ke m1
#获取集合元素个数
zcard ke
#删除元素
zrem ke m1
#范围查询
#zrange key start stop [withscores],加withscores参数表明返回的成员包含其分数。
zrange ke 0 -1
zrange ke 0 -1 withscores
#照元素分数从大到小的顺序 zrevrange key start stop [withscores] 返回索引从start到stop之间的所有元素(包含两端的元素)
zrevrange ke 0 -1 withscores
#按照排名范围删除元素zremrangebyrank key start stop,移除下标 0 至 1 区间内的成员
zremrangebyrank ke 0 1
#按照数值范围删除元素zremrangebyscore key min max,移除所有数值在 1 到 10 内的成员
zremrangebyscore ke 1 10
Steam(日志数据结构)
# 追加消息 XADD key ID field string [field string ...] ID可以自己指定,例如0-1, 0-2等等,*表示使用时间戳做ID
xadd ke * m1 f1 f2 f3 f4 f5
xadd ke * m2 f1 f2 f3 f4 f5
# 消息长度 XLEN key
xlen ke
# 范围查找的命令 XRANGE key start end [COUNT count]
#start和end
#`-`和`+` 分别代表最小和最大,或者说最老和最新的消息
#start和end也可以使用时间戳,而不加上后面的自增ID部分来查询,
#COUNT是用来达到多少数量消息之后就停止查找
xrange ke - +
xrange ke 1640516765000 1640516772621
xrange ke - + count 1
# 相反范围查找的命令 XREVRANGE key end start [COUNT count]
xrevrange ke + -
# 删除消息 xdel key ID [ID ...] 这里的删除仅仅是设置了标志位,不影响消息总长度
xdel ke 1640516772621-0
# 删除整个stream
del ke
# 读取消息 XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] ID [ID ...]
# BLOCK是阻塞读,如果填0,则是一直到有消息,否则都是阻塞。
XREAD BLOCK 0 STREAMS ke 1640516772621-0
# STREAMS后原本写ID的地方,如果使用 $ 则是代表最新的消息的ID
#设想,如果你加入一个群聊,不看历史消息,但是从你加入之后的消息都能读到,该怎么做?这种时候就可以用这个了。
XREAD COUNT 2 STREAMS ke $
Maven依赖:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
Spring Boot 2 的 spring-boot-starter-data-redis 中,默认使用的是lettuce作为redis客户端,它与jedis的主要区别如下:
如果不使用默认的lettuce,使用jedis的话,可以排除lettuce的依赖,手动加入jedis依赖,配置如下:
排除lettuce的依赖:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
<exclusions>
<exclusion>
<groupId>io.lettucegroupId>
<artifactId>lettuce-coreartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>3.1.0version>
dependency>
#配置redis连接
#选择数据库,默认值为0
spring.redis.database=0
#主机地址,默认localhost
spring.redis.host=localhost
#端口号,默认6379
spring.redis.port=6379
#密码
spring.redis.password=
#请求超时时间 0表示只要没拿到数据就一直请求
spring.redis.timeout=0
#配置lettuce连接池
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.lettuce.pool.max-active=8
# 连接池分配连接最大阻塞等待时间(阻塞时间到,抛出异常。使用负值表示无限期阻塞)
spring.redis.lettuce.pool.max-wait=-1
# 连接池中的最大空闲连接数
spring.redis.lettuce.pool.max-idle=8
# 连接池中的最小空闲连接数
spring.redis.lettuce.pool.min-idle=0
#配置jedis连接池
#spring.redis.jedis.pool.max-active=8
#spring.redis.jedis.pool.max-wait=-1
#spring.redis.jedis.pool.max-idle=8
#spring.redis.jedis.pool.min-idle=0
Spring Boot 内置实现的Redis API:
RedisTemplate:
/**
* RedisTemplate
*/
RedisTemplate<Object,Object> redisTemplate;
String key = "key";
//redis-String类型
redisTemplate.opsForValue().set(key, "redisTemplate");
redisTemplate.delete(key);
//相同key会进行覆盖, 原来是字符串,给user对象覆盖了 ——可以的
redisTemplate.opsForValue().set(key, user对象);
Object o = redisTemplate.opsForValue().get(key);
if(o != null){
user = (User)o;
System.out.println(user);
}
//redis-Hash类型
Map<String, Object> stu = new HashMap<>();
stu.put("1", "山东威");
stu.put("2", "山西洲");
redisTemplate.opsForHash().putAll("ket", stu);
Object o2 = redisTemplate.opsForHash().get("ket", "2");
System.out.println(o2);
List<Object> list = new ArrayList<>();
Iterator<String> it = stu.keySet().iterator();
while(it.hasNext()){
String next = it.next();
System.out.println(next);
list.add(next);
}
List<Object> list1 = redisTemplate.opsForHash().multiGet("ket", list);
//list1.forEach(System.out::println); //遍历出 山东威、山西洲
list1.forEach(temp-> System.out.println(temp));
StringRedisTemplate:
/**
* StringRedisTemplate
*/
StringRedisTemplate stringRedisTemplate;
String key = "key";
//stringRedisTemplate.opsForValue().set(string,user对象); //error 放不了对象
stringRedisTemplate.opsForValue().set(string,string);
//获取Map里的单个值
StringRedisTemplate stringRedisTemplate;
Map<String, String> stu = new HashMap<>();
stu.put("1", "黄秀珠");
stu.put("2", "在深圳还好吗");
stringRedisTemplate.opsForHash().putAll(key, stu);
Object o1 = stringRedisTemplate.opsForHash().get(key,"1");
System.out.println(o1); //输出黄秀珠
//获取所有Map里的值
List<Object> list = new ArrayList<>();
Iterator it = stu.keySet().iterator();
while(it.hasNext()){
String obj = (String)it.next();
System.out.println(obj);
list.add(obj);
}
//获取出所有Map里放进的redis里的Hash
List<Object> objects = stringRedisTemplate.opsForHash().multiGet(key, list);
//objects.forEach(System.out::println); //遍历出 黄秀珠、在深圳还好吗
objects.forEach(temp-> System.out.println(temp));
修改RedisTemplate序列化规则:
Maven依赖:
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
dependency>
@Configuration
@EnableCaching
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// 使用Jackson2JsonRedisSerialize 替换默认的jdkSerializeable序列化
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.activateDefaultTyping(om.getPolymorphicTypeValidator(),ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 字符串序列器
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;
}
}
@Autowired
RedisTemplate<Object, Object> redisTemplate;
@Autowired
StringRedisTemplate stringRedisTemplate;
//使用Jackson2JsonRedisSerialize 替换默认的jdkSerializeable序列化之后
@Autowired //上两个error错误无法运行org.springframework.beans.factory.UnsatisfiedDependencyException
RedisTemplate<String, Object> redisTemplateJackson;
//JsonSerialize会覆盖StringRedisTemplate(原有redis存储) ——redis-String类型 key="key"
String key = "key";
redisTemplateJackson.opsForValue().set(key, "redisTemplateJacksonSerialize");
Object o = redisTemplateJackson.opsForValue().get(key);
System.out.println(o);
User user = new User(2, "黄秀珠", 22);
redisTemplateJackson.opsForValue().set(key, user);
Object o1 = redisTemplateJackson.opsForValue().get(key);
System.out.println(o1);
//JsonSerialize会覆盖StringRedisTemplate(原有redis存储) ——redis-Hash类型 key="ket"
Map<String, Object> stu = new HashMap<>();
stu.put("1", "山东威");
stu.put("2", "山西洲");
redisTemplateJackson.opsForHash().putAll("ket", stu);
Object o2 = redisTemplateJackson.opsForHash().get("ket", "1");
System.out.println(o2);
List<Object> list = new ArrayList<>();
Iterator it = stu.keySet().iterator();
while(it.hasNext()){
Object next = it.next();
System.out.println(next);
list.add(next);
}
List<Object> list1 = redisTemplateJackson.opsForHash().multiGet("ket", list);
list1.forEach(System.out::println);
Redis 常用注解:
@EnableCaching
public class RedisConfig {...}
@service // service层
@Cacheable(value = "users", key = "#pageIndex", condition = "#pageIndex < 3")
@Override
public List<User> listAll(int pageIndex) {}
//value: 类似Java Map> 外面一层key
//key: 内面一层key
//condition: 判断是否进行缓存,"#pageIndex < 3"
//首先进行 内面一层key(#pageIndex先查,里没有执行method,里有了key再按缓存) and condition = "#pageIndex < 3"
@service // service层
@CachePut(value = "users", key = "#pageIndex", condition = "#pageIndex < 3")
@Override
public List<User> listAllPut(int pageIndex) {}
//value: 类似Java Map> 外面一层key
//key: 内面一层key
//condition: 判断是否存入缓存,"#pageIndex < 3"
//首先每次都会执行method 内面一层key(#pageIndex先查,里有key更新缓存,里没有key,要看#pageIndex(key) 是否小于< 3 小于存入/不小于不存入) and condition = "#pageIndex < 3"
//错误问题!在idea测试出来,无法更新存入缓存redis
MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk. Commands that may modify the data set are disabled, because this instance is configured to report errors during writes if RDB snapshotting fails (stop-writes-on-bgsave-error option). Please check the Redis logs for details about the RDB error.
Redis配置为保存RDB快照,但它目前无法在磁盘上持久化。可能修改数据集的命令被禁用,因为此实例被配置为在RDB快照失败时报告写入期间的错误(在bgsave error选项上停止写入)。有关RDB错误的详细信息,请查看Redis日志
@service // service层
@CacheEvict(value = "users", key = "#pageIndex", condition = "#pageIndex < 3", allEntries = true, beforeInvocation = true)
@Override
public boolean update(int pageIndex){return true;}
//其中value、key和condition的语义与@Cacheable对应的属性类似。
//allEntries = true:(清除所有value = "users"里的 外内key); allEntries = 默认false:不清除所有,只清除内层key"#pageIndex";
//清除beforeInvocation = true:(先清除外层key"users"里的 内层key"#pageIndex"缓存,再执行method,然后不会放入缓存); beforeInvocation = 默认false:(执行method,再清除缓存);
@Service // service层
@CacheConfig(cacheNames = "users")
public class UserServiceImpl implements UserService {
//查询
@Cacheable(key = "#pageIndex", condition = "#pageIndex < 3")
@Override
public List<User> listAll(int pageIndex) {}
//更新
@CachePut(key = "#pageIndex", condition = "#pageIndex < 4")
@Override
public List<User> listAllPut(int pageIndex) {}
//清除
@CacheEvict(key = "#pageIndex", condition = "#pageIndex < 3", allEntries = true, beforeInvocation = true)
@Override
public boolean update(int pageIndex){}
}
@Caching(
cacheable = @Cacheable("users"),
evict = {
@CacheEvict("cache2"),
@CacheEvict(value = "cache3", allEntries = true)
}
)
RDB持久化:
RDB默认开启,redis.conf 中的具体配置参数; 持久化数据存储在本地的文件dump.rdb快照(snapshots)
#dbfilename:持久化数据存储在本地的文件
dbfilename dump.rdb
#dir:持久化数据存储在本地的路径,如果是在/redis/redis-3.0.6/myRedis下启动的redis-cli,则数据会存储在当前myRedis目录下
dir ./
#save时间,以下分别表示更改了1个key时间隔900s进行持久化存储;更改了10个key300s进行存储;更改10000个key60s进行存储
#可以通过 注释#save 900 1 来关闭snapshot功能
##对于此值的设置,需要谨慎,评估系统的变更操作密集程度
save 900 1
save 300 10
save 60 10000
##当snapshot时出现错误无法继续时,是否阻塞客户端“变更操作”,“错误”可能因为磁盘已满/磁盘故障/OS级别异常等
stop-writes-on-bgsave-error yes
##是否启用rdb文件压缩,默认为“yes”,压缩往往意味着“额外的cpu消耗”,同时也意味这较小的文件尺寸以及较短的网络传输时间
rdbcompression yes
AOF持久化:
AOF 默认关闭,redis.conf 中的具体配置参数; 持久化数据存储在本地的文件appendonly.aof
##aof功能的开关,默认为“no”,只有在“yes”下,aof重写/文件同步等特性才会生效
appendonly yes
##指定aof文件名称
appendfilename appendonly.aof
#指定aof操作中文件同步策略,有三个合法值:always everysec no,默认为everysec
#always:每一条 aof 记录都立即同步到文件,这是最安全的方式,也以为更多的磁盘操作和阻塞延迟,是 IO 开支较大
#everysec:每秒同步一次,性能和安全都比较中庸的方式,也是 redis 推荐的方式。如果遇到物理服务器故障,有可能导致最近一秒内 aof 记录丢失(可能为部分丢失)
#no:redis并不直接调用文件同步,而是交给操作系统来处理,操作系统可以根据 buffer 填充情况 / 通道空闲时间等择机触发同步;这是一种普通的文件操作方式。性能较好,在物理服务器故障时,数据丢失量会因 OS 配置有关。
appendfsync everysec
#在aof-rewrite期间,appendfsync是否暂缓文件同步,“no”表示“不暂缓”,“yes”表示“暂缓”,默认为“no”
no-appendfsync-on-rewrite no
#aof文件rewrite触发的最小文件尺寸(mb,gb),只有大于此aof文件大于此尺寸是才会触发rewrite,默认“64mb”,建议“512mb”
auto-aof-rewrite-min-size 64mb
#相对于“上一次”rewrite,本次rewrite触发时aof文件应该增长的百分比;
#每一次rewrite之后,redis都会记录下此时“新aof”文件的大小(例如A),那么当aof文件增长到A*(1 + p)之后, 触发下一次rewrite,每一次aof记录的添加,都会检测当前aof文件的尺寸
auto-aof-rewrite-percentage 100
AOF rewrite:一条数据经过多次变更,将会产生多条 AOF 记录,其实只要保存当前的状态,历史的操作记录是可以抛弃的——作用是由 AOF 持久化模式“AOF rewrite”完成
触发 rewrite 的时机可以通过配置文件来声明-appendfsync everysec同步,同时 redis 中可以通过 bgrewriteaof 指令人工干预:
redis-cli -h ip -p port bgrewriteaof
总结:
无论是“人工干预”还是系统触发,RDB snapshot(快照) 和 AOF rewrite(通过配置文件来声明-appendfsync everysec同步) 需要逐个被执行
一个主数据库可以有多个从数据库,而一个从数据库只能有一个主数据库
执行过程:
修改redis.conf从配置文件:
# 设置访问主服务器地址和端口
# slaveof 3.x版本使用指令(slaveof 是mysql的版权)
replicaof <masterip> <masterport>
# 主Redis配置了密码,则需要配置
# masterauth 123456
# 可以使用info命令查看主从信息info replication
ip:6378> info replication
ip:6378> info
ip:6380> info replication
ip:6381> info replication
从数据库终端slave:
pwd /usr/local/redis/redis-6.2.5
mkdir slave
cp redis.conf slave/redis80.conf
cp redis.conf slave/redis81.conf
成都 slave
# 进程终端1:
vi redis80.conf
port 6380
dbfilename dump80.rdb
pidfile /var/run/redis_6380.pid
replicaof 主服务器ip 6378
# 启动redis80 从服务器
../src/redis-server redis80.conf
# 进程终端2:
vi redis81.conf
port 6381
dbfilename dump81.rdb
pidfile /var/run/redis_6381.pid
replicaof 主服务器ip 6378
# 启动redis81 从服务器
../src/redis-server redis81.conf
主数据库终端master:
../src/redis-cli -p 6378
127.0.0.1:6378> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=434,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=434,lag=0
master_replid:0f8f58d3c56b6f77e20cb3c7c71fbefaf5a64be8
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:434
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:434
127.0.0.1:6378> keys *
(empty array)
127.0.0.1:6378> set test 123
OK
127.0.0.1:6378> set test abc
OK
查看从服务器终端slave:
../src/redis-cli -p 6380
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6378
master_link_status:up
master_last_io_seconds_ago:8
master_sync_in_progress:0
slave_repl_offset:784
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:0f8f58d3c56b6f77e20cb3c7c71fbefaf5a64be8
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:784
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:784
127.0.0.1:6380> keys *
1) "test"
127.0.0.1:6380> get test
"123"
127.0.0.1:6380> set test abc
(error) READONLY You can't write against a read only replica.
#你不能对只读副本进行写入
sentinel 是一个分布式系统,你可以在一个架构中运行多个哨兵(sentinel) 进程,这些进程使用流言协议(gossipprotocols)定时发送消息来接收关于Master是否下线的信息,并使用投票协议(agreement protocols)来决定是否执行自动故障迁移,以及选择哪个Slave作为新的Master
哨兵模式,拷贝一份sentinel.conf到slave目录下,修改sentinel.conf配置文件:
pwd /usr/local/redis/redis-6.2.5/slave
cp ../sentinel.conf slave/sentinel-1.conf
vim sentinel-1.conf
#端口
port 26379
#日志文件配置
logfile "/usr/local/redis/redis-6.2.5/slave/sentinel-1.log"
#进程号文件配置
pidfile /var/run/redis-sentinel-1.pid
#工作目录
dir /usr/local/redis/redis-6.2.5/slave/
#后台启动
daemonize yes
#主节点 名称(mymaster只要是字符串即可) IP(master的IP) 端口号(master的端口) 选举次数(需要几个哨兵同意 则认为主服务器失效)
sentinel monitor mymaster 127.0.0.1 6378 1
#主节点密码
#sentinel auth-pass mymaster 123456
#修改心跳检测5000毫秒,意思就是在多少毫秒内主节点连接无响应,那么认定为主节点已经宕机 //默认30 seconds秒 30000
sentinel down-after-milliseconds mymaster 5000
#在执行故障转移时, 最多可以有多少个从服务器同时对新的主服务器进行同步
sentinel parallel-syncs mymaster 2
#进行故障转移时如果超过了配置的时间就表示故障转移超时失败 //默认3 minutes分钟 180000毫秒
sentinel failover-timeout mymaster 15000
# 启动哨兵模式
../src/redis-sentinel sentinel-1.conf
# 关闭哨兵模式,shutdown关闭 服务端、客户端、哨兵端 都可以
#../src/redis-cli -h 127.0.0.1 -p 26379 shutdown
主服务器master:
../src/redis-cli -p 6378
127.0.0.1:6378> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=24623,lag=0
slave1:ip=127.0.0.1,port=6381,state=online,offset=24623,lag=1
master_replid:a1b761a06f89998271bd78c756564ac9046976e5
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:24623
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:24623
# 模拟6378 宕机(挂掉); save保存快照; 6378服务端和6378客户端都会关了
127.0.0.1:6378> shutdown [NOSAVE|SAVE]
从服务器slave(redis80.conf会给修改,经过哨兵选举成为了主服务master):
#终端1:
vi redis80.conf
port 6380
dbfilename dump80.rdb
pidfile /var/run/redis_6380.pid
##replicaof 127.0.0.1 6378
#终端2:
vi redis81.conf
port 6381
dbfilename dump81.rdb
pidfile /var/run/redis_6381.pid
replicaof 127.0.0.1 6378 变成 6380
#6378主服务终端关闭了还没有启动:
vi redis78.conf
# replicaof
#slave 6380变成master:
[root@localhost slave]# ../src/redis-cli -p 6380
127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6381,state=online,offset=96937,lag=0
master_replid:e765df828618ebb7dfe7f9a929c399d20653c0aa
master_replid2:a1b761a06f89998271bd78c756564ac9046976e5
master_repl_offset:96937
second_repl_offset:76513
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:96937
127.0.0.1:6380> keys *
1) "test"
127.0.0.1:6380> get test
"ggggg"
127.0.0.1:6380> set test HHHHH
OK
127.0.0.1:6380> exit
重启6378主服务终端,变成了从服务slave:
# 6378服务端启动重启后
vi redis78.conf
# Generated by CONFIG REWRITE 由配置重写生成
replicaof 127.0.0.1 6380 // 最后面会添加,主服务器 master 6380
#slave 6380变成master:
[root@localhost slave]# ../src/redis-cli -p 6380
127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6381,state=online,offset=299218,lag=1
slave1:ip=127.0.0.1,port=6378,state=online,offset=299218,lag=0
master_replid:e765df828618ebb7dfe7f9a929c399d20653c0aa
master_replid2:a1b761a06f89998271bd78c756564ac9046976e5
master_repl_offset:299218
second_repl_offset:76513
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:299218
127.0.0.1:6380>
#配置redis连接
#选择数据库,默认值为0
spring.redis.database=0
#密码
spring.redis.password=
#请求超时时间
spring.redis.timeout=0
#配置jedis连接池
spring.redis.jedis.pool.max-active=8
spring.redis.jedis.pool.max-wait=-1
spring.redis.jedis.pool.max-idle=8
spring.redis.jedis.pool.min-idle=0
#设置哨兵监听主服务器
spring.redis.sentinel.master=mymaster
#设置哨兵群,多个哨兵使用,号分割
spring.redis.sentinel.nodes=访问运行哨兵服务器ip:26379
配置application.yml:
spring:
redis:
# host: redis服务器ip
# port: 6379
database: 1
sentinel:
master: mymaster
nodes: 访问运行哨兵服务器ip:26379
#主节点 master主服务器 主机IP 要外放出去给访问
#vim sentinel-1.conf
#sentinel monitor mymaster redis服务器ip 6378 1
遇到的error version
// error,SpringBoot2.4.2无法启动!可能是版本不能兼容的问题JedisSentinelPool;
Correct the classpath of your application so that it contains a single, compatible version of redis.clients.jedis.JedisSentinelPool
compatible version of // 兼容版本
Spring Boot:
Default:https://start.spring.io //2.3.8可以解决
Custom:http://start.aliyun.com/ //SpringBoot2.3.4
它先以 MULTI 开始一个事务,然后将多个命令入队到事务中,最后由 EXEC 命令触发事务,一并执行事务中的所有命令:
# 终端启动在主服务器master:
127.0.0.1:6380> multi
OK
127.0.0.1:6380> set u1 123
QUEUED
127.0.0.1:6380> get u1
QUEUED
127.0.0.1:6380> exec
1) OK
2) "123"
127.0.0.1:6380> multi
OK
127.0.0.1:6380> set u2 11111 2222
QUEUED
127.0.0.1:6380> get u1
QUEUED
127.0.0.1:6380> set u5 555
QUEUED
127.0.0.1:6380> exec
1) (error) ERR syntax error
2) "123"
3) OK //存进去了 redis 没有回滚
- redis事务 不支持回滚 为了提升效率,加了回滚需要处理锁的问题,所以不加,锁是影响效率的
- 只做了批量处理,可以达到检查指令是否是错误