Redis采用的是基于内存的采用的是单进程 单线程模型 的KV数据库,由C语言编写。官方提供的数据是可以达到100000+的qps
应用场景:
- 令牌(Token)生成
- 短信验证码
- 发布订阅
- 分布式锁
- 计数器
- 缓存(热点数据)
获取Redis安装包官网地址:https://redis.io
# 使用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
官网下载地址,及安装启动教程:https://redis.io/download
安装gcc/g++编译器(要大于>4.8.5版本),及make编译通过官网下载指定版本,然后使用传输文件工具上传到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 #successRedis可视化工具 Redis Desktop Manager
使用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-serverRedis的6种数据类型
String(字符串)
- string在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr,decr等 *** 作时会转成数值型进行计算,此时redisObject的encoding字段为int。
- 类似java中的Map
- 应用场景:可以做验证码
# 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(列表)
- list的实现为一个双向链表,即可以支持反向查找和遍历,更方便 *** 作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用的这个数据结构。
- 类似Java Map
> - 应用场景非常多:如twitter的关注列表、粉丝列表等都可以用Redis的list结构来实现;再如有的应用使用Redis的list类型实现一个简单的轻量级消息队列,生产者push,消费者pop/bpop;
# 从头部开始添加 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(字典)
- hash对应Value内部实际就是一个HashMap,实际这里会有2种不同实现,这个Hash的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,对应的value redisObject的encoding为zipmap,当成员数量增大时会自动转成真正的HashMap,此时encoding为ht。
- 类似Java Map
>
# 添加或修改指定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(集合)
- set是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
- 时间复杂度的优劣对比常见的数量级大小:越小表示算法的执行时间频度越短,则越优;
- O(1)
# 添加删除元素 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(有序集合)
- 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。
- 不同的是每个元素都会关联整数值或双精度浮点数double类型的分数。redis正是通过整数或浮点数来为集合中的成员进行从小到大的排序。
- 有序集合的成员是唯一的,但整数值或双精度浮点数(score)却可以重复。
- 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
- 应用场景:热点排行榜
#添加元素 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(日志数据结构)
- stream是Redis5版本引入的一个新的数据类型,提供了一组允许消费者以阻塞的方式等待生产者向stream中发送的新消息,此外还有一个名为消费者组的概念。
- 消费者组最早是由名为Kafka的流行消息系统引入的。Redis用完全不同的术语重新实现了一个相似的概念,但目标是相同的:允许一组客户端相互配合来消费同一个stream的不同部分的消息。
- 应用场景:适用于消息队列和时间序列存储
# 追加消息 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 $Spring Boot 集成 Redis
Maven依赖:
org.springframework.boot spring-boot-starter-data-redis
Spring Boot 2 的 spring-boot-starter-data-redis 中,默认使用的是lettuce作为redis客户端,它与jedis的主要区别如下:
- Jedis是同步的,不支持异步,Jedis客户端实例不是线程安全的,需要每个线程一个Jedis实例,所以一般通过连接池来使用Jedis
- Lettuce是基于Netty框架的事件驱动的Redis客户端,其方法调用是异步的,Lettuce的API是线程安全的,所以多个线程可以 *** 作单个Lettuce连接来完成各种 *** 作,同时Lettuce也支持连接池
如果不使用默认的lettuce,使用jedis的话,可以排除lettuce的依赖,手动加入jedis依赖,配置如下:
排除lettuce的依赖:
org.springframework.boot spring-boot-starter-data-redisio.lettuce lettuce-coreredis.clients jedis3.1.0
#配置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
StringRedisTemplate:
- 继承自RedisTemplate,重新实现了序列化策略,使用StringRedisSerialier类来序列化key-value,包括List、Hash、Set等数据结构。
- *** 作String类型数据(不是说只能 *** 作redis中的string类型,而是存储数据是string,key也为string的数据)
StringRedisTemplate stringRedisTemplate; String key = "key"; //stringRedisTemplate.opsForValue().set(string,user对象); //error 放不了对象 stringRedisTemplate.opsForValue().set(string,string); //获取Map里的单个值 StringRedisTemplate stringRedisTemplate; Mapstu = 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 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 objects = stringRedisTemplate.opsForHash().multiGet(key, list); //objects.forEach(System.out::println); //遍历出 黄秀珠、在深圳还好吗 objects.forEach(temp-> System.out.println(temp));
修改RedisTemplate序列化规则:
Maven依赖:
com.fasterxml.jackson.core jackson-databind
- 使用Jackson2JsonRedisSerialize 替换 默认的jdkSerializeable序列化:
@Configuration @EnableCaching public class RedisConfig { @Bean public RedisTemplateredisTemplate(RedisConnectionFactory factory) { RedisTemplate template = new RedisTemplate<>(); template.setConnectionFactory(factory); // 使用Jackson2JsonRedisSerialize 替换默认的jdkSerializeable序列化 Jackson2JsonRedisSerializer 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; } }
- Jackson2JsonRedisSerialize 序列化后使用栗子:
@Autowired RedisTemplateredisTemplate; @Autowired StringRedisTemplate stringRedisTemplate; //使用Jackson2JsonRedisSerialize 替换默认的jdkSerializeable序列化之后 @Autowired //上两个error错误无法运行org.springframework.beans.factory.UnsatisfiedDependencyException RedisTemplate 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 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 list = new ArrayList<>(); Iterator it = stu.keySet().iterator(); while(it.hasNext()){ Object next = it.next(); System.out.println(next); list.add(next); } List list1 = redisTemplateJackson.opsForHash().multiGet("ket", list); list1.forEach(System.out::println);
Redis 常用注解:
- @EnableCaching 表示启用缓存支持
@EnableCaching public class RedisConfig {...}
- @Cacheable 查询
- 可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存
@service // service层 @Cacheable(value = "users", key = "#pageIndex", condition = "#pageIndex < 3") @Override public ListlistAll(int pageIndex) {} //value: 类似Java Map > 外面一层key //key: 内面一层key //condition: 判断是否进行缓存,"#pageIndex < 3" //首先进行 内面一层key(#pageIndex先查,里没有执行method,里有了key再按缓存) and condition = "#pageIndex < 3"
- @CachePut 更新
- 标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中
@service // service层 @CachePut(value = "users", key = "#pageIndex", condition = "#pageIndex < 3") @Override public ListlistAllPut(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日志
- @CacheEvict 清除
- 清除缓存元素的方法或类上的。类上时表示其中所有的方法的执行都会触发缓存的清除 *** 作
@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,再清除缓存);
- @CacheConfig 全局配置名称
- 注解到类上面,表示本类的全局配置
@Service // service层 @CacheConfig(cacheNames = "users") public class UserServiceImpl implements UserService { //查询 @Cacheable(key = "#pageIndex", condition = "#pageIndex < 3") @Override public ListlistAll(int pageIndex) {} //更新 @CachePut(key = "#pageIndex", condition = "#pageIndex < 4") @Override public List listAllPut(int pageIndex) {} //清除 @CacheEvict(key = "#pageIndex", condition = "#pageIndex < 3", allEntries = true, beforeInvocation = true) @Override public boolean update(int pageIndex){} }
- @Caching 组合配置
- @Caching注解可以让我们在一个 方法 或者 类上 同时指定多个Spring Cache相关的注解
- 其拥有三个属性:cacheable、put和evict,分别用于指定@Cacheable、@CachePut和@CacheEvict
@Caching( cacheable = @Cacheable("users"), evict = { @CacheEvict("cache2"), @CacheEvict(value = "cache3", allEntries = true) } )Redis持久化 (RDB 与 AOF) 模式
- 就是将内存数据保存到硬盘,Redis持久化存储 (RDB 与 AOF 两种模式)
RDB持久化:
- RDB 是以二进制文件,是在某个时间点将数据写入一个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件,达到数据恢复
- 优点:使用单独子进程来进行持久化,主进程不会进行任何 IO *** 作,保证了 redis 的高性能
- 缺点:
1> 子进程需要开销和主进程相同的内存完成数据保存,可能会导致内存溢出
2> RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失 - 所以这种方式更适合数据要求不严谨的时候
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持久化:
- Append-only file,将“ *** 作 + 数据”以格式化指令的方式追加到 *** 作日志文件的尾部,“日志文件”保存了历史所有的 *** 作过程;当 server 需要数据恢复时,可以直接 replay 此日志文件,即可还原所有的 *** 作过程
- 优点:
1> 可以保持更高的数据完整性,如果设置追加 file 的时间是 1s,redis 发生故障,最多会丢失 1s 的数据;
2> 且如果日志写入不完整支持 redis-check-aof 来进行日志修复;AOF 文件没被 rewrite 之前(文件过大时会对命令进行合并重写),可以删除其中的某些命令; - 缺点:AOF 文件比 RDB 文件大,且恢复速度慢
- AOF 记录同步选项appendfsync everysec 最多会丢失1s的数据,相对可靠
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
- AOF rewrite 过程并不阻塞客户端请求。系统会开启一个子进程来完成
总结:
- AOF rewrite *** 作 /aof 记录同步 /RDB snapshot(快照) 都消耗磁盘IO,Redis 采取了“schedule”策略:
Redis主从复制无论是“人工干预”还是系统触发,RDB snapshot(快照) 和 AOF rewrite(通过配置文件来声明-appendfsync everysec同步) 需要逐个被执行
- 通过redis的复制功能可以很好的实现数据库的读写分离,提高服务器的负载能力
- redis的复制功能是支持多个数据库之间的数据同步。一类是主数据库(master)一类是从数据库(slave)
- 主数据库(master):主要进行读写 *** 作,当发生写 *** 作的时候自动将数据同步到从数据库
- 从数据库(slave):负责一般是只读,并接收主数据库同步过来的数据
一个主数据库可以有多个从数据库,而一个从数据库只能有一个主数据库
执行过程:
- :当一个从数据库启动时,会向主数据库发送sync命令,
- :主数据库接收到sync命令后会开始在后台保存快照(执行rdb *** 作),并将保存期间接收到的命令缓存起来
- :当快照完成后,redis会将快照文件和所有缓存的命令发送给从数据库
- :从数据库收到后,会载入快照文件并执行收到的缓存的命令
修改redis.conf从配置文件:
# 设置访问主服务器地址和端口 # slaveof3.x版本使用指令(slaveof 是mysql的版权) replicaof # 主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. #你不能对只读副本进行写入Redis哨兵机制
- Redis的哨兵(sentinel) 系统用于管理多个 Redis 服务器(主从服务器),该系统执行以下三个任务:
- 1.监控(Monitoring): 哨兵(sentinel) 会不断地检查你的Master和Slave是否运作正常
- 2.提醒(Notification):当被监控的某个 Redis出现问题时, 哨兵(sentinel) 可以通过 API 向管理员或者其他应用程序发送通知
- 3.自动故障迁移(Automatic failover):当一个Master不能正常工作时,哨兵(sentinel) 会开始一次自动故障迁移 *** 作,它会将失效主服务器Master的其中一个从服务器Slave升级为新的Master
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>Spring Boot 集成主从复制 sentinel机制
#配置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.4Redis事务
- redis事务可以一次执行多个命令,事务是一个单独的隔离 *** 作:事务中的所有命令都会序列化、按顺序地执行(以数据结构 队列 的形式压入)
- 事务在执行的过程中,不会被其他客户端发送来的命令请求所打断
- 一个事务从开始到执行会经历以下三个阶段:
- 开始事务
- 命令入队
- 执行事务
它先以 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事务 不支持回滚 为了提升效率,加了回滚需要处理锁的问题,所以不加,锁是影响效率的
- 只做了批量处理,可以达到检查指令是否是错误
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)