## Redis集群搭建 ### 一、主从模式 #### 1. 什么是主从复制 主机数据更新后根据配置和策略,自动同步到备机的master/slave机制,Master以写为主,Slave以读为主。 这样就可以起到读写分离、性能扩展,容灾快速恢复的目的。 #### 2. 配置主从复制 ##### 2.1 准备工作 ~~~bash mkdir /myredis ~~~ 进入myredis,复制redis.conf配置文件到myredis文件夹中 ~~~bash cp /opt/redis-6.2.5/redis.conf /myredis/redis.conf ~~~ 打开redis.conf 找到appendonly配置为no ~~~sh appendonly no ~~~ 密码设置为: ~~~ requirepass 8a6b67qa@WSDE# ~~~ ##### 2.2 配置一主两从 创建一个配置文件 ~~~bash touch redis6379.conf ~~~ 在三个配置文件中写入内容 ~~~sh include /myredis/redis.conf #引入配置文件的公共部分,其他的单独进行配置 pidfile /var/run/redis6379.pid #写入pid的文件位置 port 6379 #设置端口为6379 dbfilename dump6379.rdb #设置redis数据库文件名字 masterauth "8a6b67qa@WSDE#" #连接主机的密码 ~~~ ##### 2.3 复制其他两个从机的配置 ~~~bash cp redis6379.conf redis6380.conf cp redis6379.conf redis6381.conf ~~~ 打开文件进行第3步的操作修改 ##### 2.4 启动三台服务器 ~~~bash redis-server redis6379.conf redis-server redis6380.conf redis-server redis6381.conf ~~~ 查看redis启动进程 ~~~ ps -ef | grep redis ~~~ 分别连接三台服务器,查看三台主机的运行情况 info replication 打印主从复制的相关信息 ##### 2.5 配置从机 配置从机成为指定实例主服务器的从服务器,主机上不要设置 在6380和6381两台从机上执行: ~~~ slaveof 127.0.0.1 6379 ~~~ 如果一台从机挂掉了,那么启动后它就是一台独立的服务器,需要重新配置为从机,然后就可以正常的共享数据了 如果主机挂掉了,从机还是从机并没有改变主从关系,当主机重启后,不需要重新配置 #### 3. 主从复制的实现原理 1、 当从机连接上主机后,从服务器向主服务器发送请求,进行数据同步消息 2、 主服务接收到从服务器发送过来同步消息,把主服务器数据先进行持久化,放入到rdb文件中,把rdb文件发送到从服务器,从服务器拿到rdb文件,进行读取 3、每次主服务器进行写操作之后,主动和从服务器进行数据同步 #### 4. 实战中的三种配置模式 ##### 4.1 一主多仆 刚才配置的主从复制就是一主多仆模式,主机shutdown后,从机还是原地待命,并不会上位成为主机。 ##### 4.2 薪火相传 就是把一台从机设置为另一台从机的主机,形成一个链条 。 缺点:链条断了就不能再继续进行 在6381下进行如下操作,就可以实现 ~~~bash slaveof 127.0.0.1 6380 ~~~ 上一个Slave可以是下一个Slave的Master,Slave同样可以接收其他Slaves的连接和同步请求,那么该Slave做为链条中的下一个Master,可以有效减轻Master的复制从机数据的压力,去中心化的思想,降低风险。 风险是:一旦某个Slave宕机,后面的Slave都没法备份。主机一旦挂了,从机依然还是从机,无法写数据了。 ##### 4.3 反客为主 当一个主机master宕机后,后面的slave从机立即升级为master,其后面的slave不用做任何修改 使用命令 ~~~bash slaveof no one #将从机变成主机 ~~~ 停掉6379,设置6380 缺点:需要手动的进行设置 ### 二、哨兵模式 反客为主的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库 ![无标题2](Redis-3.assets/无标题2.png) #### 1. 先配置成一主多仆模式 把刚才设置成的薪火相传,重新设置主机 ~~~shell slaveof 127.0.0.1 6379 ~~~ #### 2. 准备工作 在myredis下新建sentinel.conf文件,名字不能写错 #### 3. 配置哨兵 ~~~sh sentinel monitor mymaster 127.0.0.1 6379 1 sentinel auth-pass mymaster "8a%&^AeE6b67qa@WSDE#" ~~~ 其中mymaster为监控对象起的服务器名称,1为至少有多少个哨兵同意迁移的数量。 #### 4. 启动哨兵 ~~~bash redis-sentinel /myredis/sentinel.conf ~~~ 当主机挂掉,从机选举中产生新的主机 哪个从机会被选为主机呢?根据优先级:slave-priority,在这种模式下,原来的主机重启后,会变成从机。 优先级在redis.conf中默认:slave-priority 100 ,值越小优先级越高。 注意:redis新版本中的名称叫:replica-priority。 大概10-20秒钟左右,可以看到哨兵窗口日志的输出,切换了新的主机 选举条件依次为: - 选择优先级靠前的 - 选择偏移量最大的 偏移量指的是获得原主机数据最全的 - 选择runid最小的从服务器 每个redis实例启动后,都会随机生成一个40位的runid,运行id由40位字符组成,是一个随机的十六进制字符,例如: 7cfbf53e4901277d7247db9fac7945af44dcc666 运行id在每台服务器启动时自动生成的,master在首次连接slave时,会将自己的运行ID发送给slave,slave保存此ID,通过info Server命令,可以查看节点的runid ~~~shell redis-cli -p 6379 info server | grep run ~~~ #### 5. 复制延时 由于所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。 ### 三、阿里云搭建Redis哨兵模式 > 一主两从三哨兵集群配置方案: 当master节点宕机时,通过哨兵(sentinel)重新推选出新的master节点,保证集群的可用性。 哨兵的主要功能: 1.集群监控:负责监控 Redis master 和 slave 进程是否正常工作。 2.消息通知:如果某个 Redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员。 3.故障转移:如果 master node 挂掉了,会自动转移到 slave node 上。 4.配置中心:如果故障转移发生了,通知 client 客户端新的 master 地址。 ![2021062109150312](Redis-3.assets/2021062109150312.png) #### 1. 下载安装Redis到服务器 此处步骤略 #### 2. 配置Redis步骤 ##### 2.1 在根目录下创建文件夹 ~~~bash mkdir redis cd redis mkdir data #准备存放数据的文件夹 mkdir log #存放日志的文件夹 ~~~ ##### 2.2 拷贝文件到根目录 ~~~bash cp /opt/redis-6.2.5/redis.conf /redis ~~~ ##### 2.3 修改配置文件(说明) ~~~bash vim redis.conf ~~~ ~~~shell #注释掉bind,允许远程访问 #bind 127.0.0.1 -::1 #表示Redis可以接受任意ip的连接 bind 0.0.0.0 #打开后台启动 daemonize yes #设置权限密码 requirepass 8a%&^AeE6b67qa@WSDE# #设置从节点连接主节点密码 masterauth 8a%&^AeE6b67qa@WSDE# #关闭保护模式 protected-mode no #注释端口号 #port 6379 #注释pid文件 #pidfile /var/run/redis_6379.pid #注释日志文件 #logfile "" #开启默认的rdb文件 save 3600 1 save 300 100 save 60 10000 #每一个端口都会有对应的rdb文件,这个需要注释 #dbfilename dump.rdb #存放在根目录的data文件目录下 dir /redis/data #从服务器默认只读 replica-read-only yes ~~~ ##### 2.4 创建三个配置文件 在redis的跟目录下,创建三个配置文件,6379做为主机,6380与6381做为从机 ~~~properties touch redis6379.conf touch redis6380.conf touch redis6381.conf ~~~ 在6379配置文件中写入内容: ~~~properties #引入公共的redis.conf文件配置 include /redis/redis.conf # 进程编号记录文件 pidfile /var/run/redis-6379.pid # 进程端口号 port 6379 # 日志记录文件 logfile "/redis/log/redis-6379.log" # 数据记录文件名称 dbfilename dump-6379.rdb # 追加文件名称 appendfilename "appendonly-6379.aof" ~~~ ~~~sh include /redis/redis.conf pidfile /var/run/redis-6379.pid port 6379 logfile "/redis/log/redis-6379.log" dbfilename dump-6379.rdb appendfilename "appendonly-6379.aof" ~~~ 在6380配置文件中写入内容 ~~~properties #引入公共的redis.conf文件配置 include /redis/redis.conf # 进程编号记录文件 pidfile "/var/run/redis-6380.pid" # 进程端口号 port 6380 # 日志记录文件 logfile "/redis/log/redis-6380.log" # 数据记录文件 dbfilename "dump-6380.rdb" # 追加文件名称 appendfilename "appendonly-6380.aof" # 下面的配置无需在6379里配置 # # 备份服务器从属于6379推荐配置局域网ip replicaof 116.62.32.11 6379 ~~~ ~~~sh include /redis/redis.conf pidfile "/var/run/redis-6380.pid" port 6380 logfile "/redis/log/redis-6380.log" dbfilename "dump-6380.rdb" appendfilename "appendonly-6380.aof" replicaof 116.62.32.11 6379 ~~~ 在6381配置文件中写入内容 ~~~properties #引入公共的redis.conf文件配置 include /redis/redis.conf # 进程编号记录文件 pidfile "/var/run/redis-6381.pid" # 进程端口号 port 6381 # 日志记录文件 logfile "/redis/log/redis-6381.log" # 数据记录文件 dbfilename "dump-6381.rdb" # 追加文件名称 appendfilename "appendonly-6381.aof" # 下面的配置无需在6379里配置 # # 备份服务器从属于6379推荐配置局域网ip replicaof 116.62.32.11 6379 ~~~ ~~~sh include /redis/redis.conf pidfile "/var/run/redis-6381.pid" port 6381 logfile "/redis/log/redis-6381.log" dbfilename "dump-6381.rdb" appendfilename "appendonly-6381.aof" replicaof 116.62.32.11 6379 ~~~ ##### 2.5 启动服务 启动三个redis服务 ~~~properties redis-server redis6379.conf redis-server redis6380.conf redis-server redis6381.conf ~~~ 查看redis启动进程 ~~~shell ps -ef | grep redis ~~~ 通过redis-cli指令分别连接三台服务器,查看三台主机的运行情况 ~~~shell info replication ~~~ 打印主从复制的相关信息 ##### 2.6 配置哨兵模式 在根目录的redis中,创建sentinel.conf文件 ~~~shell touch sentinel.conf ~~~ 配置内容如下 ~~~properties daemonize yes sentinel monitor mymaster 116.62.32.11 6379 2 sentinel auth-pass mymaster 123456 ~~~ > 哨兵监控主从 这个2 是这边开启三台服务器 如果要选举服务器就需要大于百分之50才能选举成功 3台服务器如果要选一台就需要2台的投票数 ~~~properties #是否要用守护线程的方式启动,采用yes时,redis会在后台运行 daemonize yes # 表示接收所有ip的请求 bind 0.0.0.0 #告诉redis监控,ip为39.105.163.81,端口号为6379,名为mymaster的主节点,quorum设为2。 #quorum的含义: #确认主节点不可达需要经过多少哨兵同意 #(从而将该主机标记为失效,并触发故障转移机制) #quorum只用于监测失效的主节点。当某个哨兵被选为执行故障转移的首领后,才能由其进行具体的转移操作。 sentinel monitor mymaster 39.105.163.81 6379 2 # 密码 sentinel auth-pass mymaster 20020929_wskE@ # 主服务器超时时间,秒为单位 sentinel down-after-milliseconds mymaster 30000 # 最多可以有多少个从服务器同时对新的主服务器进行同步, 这个数字越小, 完成故障转移所需的时间就越长,但越大就意味着越多的从服务器因为复制而不可用。可以通过将这个值设为1来保证每次只有一个从服务器处于不能处理命令请求的状态。 sentinel parallel-syncs mymaster 1 # 选举新主服务器的最小超时时间 sentinel failover-timeout mymaster 180000 #sentinel的节点ip地址 sentinel announce-ip 39.105.163.81 #sentinel的节点端口号 sentinel announce-port 26379 ~~~ 复制下面的内容写入sentinel.conf即可 ~~~ daemonize yes bind 0.0.0.0 sentinel monitor mymaster 39.105.163.81 6379 2 sentinel auth-pass mymaster 20020929_wskE@ sentinel down-after-milliseconds mymaster 30000 sentinel parallel-syncs mymaster 1 sentinel failover-timeout mymaster 180000 sentinel announce-ip 39.105.163.81 sentinel announce-port 26379 ~~~ 再创建三个配置文件 ~~~shell touch sentinel26379.conf touch sentinel26380.conf touch sentinel26381.conf ~~~ 编辑sentinel26379.conf内容 ~~~properties #引用公共配置 include /redis/sentinel.conf ##进程端口号 port 26379 ##进程编号记录文件 pidfile "/var/run/sentinel-26379.pid" ##日志记录文件(为了方便查看日志,先注释掉,搭建好环境后再打开 logfile "/redis/log/sentinel-26379.log" ~~~ ~~~sh include /redis/sentinel.conf port 26379 pidfile "/var/run/sentinel-26379.pid" logfile "/redis/log/sentinel-26379.log" ~~~ 编辑sentinel26380.conf内容 ~~~properties #引用公共配置 include /redis/sentinel.conf #进程端口号 port 26380 #进程编号记录文件 pidfile "/var/run/sentinel-26380.pid" #日志记录文件(为了方便查看日志,先注释掉,搭建好环境后再打开 logfile "/redis/log/sentinel-26380.log" ~~~ ~~~sh include /redis/sentinel.conf port 26380 pidfile "/var/run/sentinel-26380.pid" logfile "/redis/log/sentinel-26380.log" ~~~ 编辑sentinel26381.conf内容 ~~~properties #引用公共配置 include /redis/sentinel.conf #进程端口号 port 26381 #进程编号记录文件 pidfile "/var/run/sentinel-26381.pid" #日志记录文件(为了方便查看日志,先注释掉,搭建好环境后再打开 logfile "/redis/log/sentinel-26381.log" ~~~ ~~~sh include /redis/sentinel.conf port 26381 pidfile "/var/run/sentinel-26381.pid" logfile "/redis/log/sentinel-26381.log" ~~~ 启动三个哨兵 ~~~properties redis-sentinel sentinel26379.conf redis-sentinel sentinel26380.conf redis-sentinel sentinel26381.conf ~~~ 然后再开启三个窗口查看哨兵日志 ~~~shell tail -f /redis/log/sentinel-26379.log tail -f /redis/log/sentinel-26380.log tail -f /redis/log/sentinel-26381.log ~~~ 测试:现在把主机6379杀死等待30秒,看日志输出 ##### 2.7 开启端口 要在阿里云控制台的安全组中开放端口,同时在linux服务器的防火墙中添加开放的端口 防火墙命令 ~~~sh #查看firewall服务状态 systemctl status firewalld #开启防火墙服务 systemctl start firewalld #重启 systemctl restart firewalld #关闭 systemctl stop firewalld #开启端口 firewall-cmd --zone=public --add-port=6379/tcp --permanent #含义: --zone #设置作用域为全局生效 --add-port=8080/tcp #添加端口 --permanent #永久生效 # 配置立即生效 firewall-cmd --reload #查看已经加入防火墙的端口号 firewall-cmd --zone=public --list-ports #查看防火墙规则 firewall-cmd --list-all #检查端口被哪个进程占用 netstat -lnpt|grep 8080 ~~~ 启动顺序为:先主,后从,再哨兵。启动后先检测哨兵的日志 A: B C B: A C C: A B ### 四、Springboot使用redis哨兵 #### 1. 创建项目,导入maven依赖 ~~~xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> ~~~ #### 2. 添加yml配置信息 > lettuce连接池使用commons-pool2依赖 ~~~yaml ###################以下为Redis增加的配置########################### spring: data: redis: timeout: 10000 password: 123456 ###################以下为redis哨兵增加的配置########################### sentinel: master: mymaster nodes: 101.200.171.101:26379,101.200.172.102:26380,101.200.174.103:26381 ###################以下为lettuce连接池增加的配置########################### lettuce: pool: max-active: 100 # 连接池最大连接数(使用负值表示没有限制) max-idle: 100 # 连接池中的最大空闲连接 min-idle: 50 # 连接池中的最小空闲连接 max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制) ~~~ #### 3. 配置序列化器 ~~~java package cn.hxzy.config; /** * @author mengshujun * @create 2024/5/5 21:06 */ import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; 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.StringRedisSerializer; @Configuration public class RedisConfig { @Bean("redis") @SuppressWarnings("all") public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); //自定义Jackson序列化配置 Jackson2JsonRedisSerializer jsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL); jsonRedisSerializer.setObjectMapper(objectMapper); //key使用String的序列化方式 StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); template.setKeySerializer(stringRedisSerializer); //hash的key也是用String的序列化方式 template.setHashKeySerializer(stringRedisSerializer); //value的key使用jackson的序列化方式 template.setValueSerializer(jsonRedisSerializer); //hash的value也是用jackson的序列化方式 template.setHashValueSerializer(jsonRedisSerializer); template.afterPropertiesSet(); return template; } } ~~~ #### 4. 配置RedisSentinelConfiguration 在RedisConfig类中,添加如下方法,进行哨兵节点的注册 ~~~java @Bean public RedisSentinelConfiguration redisSentinelConfiguration(){ RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration() //主节点名称 .master("mymaster") //哨兵 .sentinel("101.200.174.107",26379) .sentinel("101.200.174.107",26380) .sentinel("101.200.174.107",26381); //配置的哨兵密码 redisSentinelConfiguration.setPassword("123456"); return redisSentinelConfiguration; } ~~~ #### 5. 进行测试 在测试类中,写入测试代码 ~~~java @Autowired private RedisTemplate redisTemplate; @Autowired private StringRedisTemplate stringRedisTemplate; @Test public void test01() { stringRedisTemplate.opsForValue().set("k2", "v2"); String k2 = stringRedisTemplate.opsForValue().get("k2"); System.out.println(k2); User u1=new User(10003,"zhangsan"); redisTemplate.opsForValue().set("user",u1); Object aDo = redisTemplate.opsForValue().get("user"); System.out.println(aDo); } ~~~ #### 6.配置RedisUtils 企业中往往操作redis对象,总会把RedisTemplate常用的方法封装成为一个工具类,这样极大的方便操作redis,并且在工具类中统一进行了异常处理等操作。 工具: ~~~java package cn.hxzy.util; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; @Component public final class RedisUtils { @Autowired private RedisTemplate<String, Object> redisTemplate; // =============================common============================ /** * 指定缓存失效时间 * * @param key 键 * @param time 时间(秒) */ public boolean expire(String key, long time, TimeUnit timeUnit) { try { if (time > 0) { redisTemplate.expire(key, time, timeUnit); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 根据key 获取过期时间 * * @param key 键 不能为null * @return 时间(秒) 返回0代表为永久有效 */ public long getExpire(String key) { return redisTemplate.getExpire(key, TimeUnit.SECONDS); } /** * 判断key是否存在 * * @param key 键 * @return true 存在 false不存在 */ public boolean hasKey(String key) { try { return redisTemplate.hasKey(key); } catch (Exception e) { e.printStackTrace(); return false; } } /** * 删除缓存 * * @param key 可以传一个值 或多个 */ @SuppressWarnings("unchecked") public void del(String... key) { if (key != null && key.length > 0) { if (key.length == 1) { redisTemplate.delete(key[0]); } else { redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key)); } } } // ============================普通对象操作============================= /** * 普通缓存获取 * * @param key 键 * @return 值 */ public Object get(String key) { return key == null ? null : redisTemplate.opsForValue().get(key); } /** * 普通缓存放入 * * @param key 键 * @param value 值 * @return true成功 false失败 */ public boolean set(String key, Object value) { try { redisTemplate.opsForValue().set(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 普通缓存放入并设置时间 * * @param key 键 * @param value 值 * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期 * @return true成功 false 失败 */ public boolean set(String key, Object value, long time) { try { if (time > 0) { redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); } else { set(key, value); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 递增 * * @param key 键 * @param delta 要增加几(大于0) */ public long incr(String key, long delta) { if (delta < 0) { throw new RuntimeException("递增因子必须大于0"); } return redisTemplate.opsForValue().increment(key, delta); } /** * 递减 * * @param key 键 * @param delta 要减少几(小于0) */ public long decr(String key, long delta) { if (delta < 0) { throw new RuntimeException("递减因子必须大于0"); } return redisTemplate.opsForValue().increment(key, -delta); } // ================================Map================================= /** * HashGet * * @param key 键 不能为null * @param item 项 不能为null */ public Object hget(String key, String item) { return redisTemplate.opsForHash().get(key, item); } /** * 获取hashKey对应的所有键值 * * @param key 键 * @return 对应的多个键值 */ public Map<Object, Object> hmget(String key) { return redisTemplate.opsForHash().entries(key); } /** * HashSet * * @param key 键 * @param map 对应多个键值 */ public boolean hmset(String key, Map<String, Object> map) { try { redisTemplate.opsForHash().putAll(key, map); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * HashSet 并设置时间 * * @param key 键 * @param map 对应多个键值 * @param time 时间(秒) * @return true成功 false失败 */ public boolean hmset(String key, Map<String, Object> map, long time, TimeUnit timeUnit) { try { redisTemplate.opsForHash().putAll(key, map); if (time > 0) { expire(key, time, timeUnit); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 向一张hash表中放入数据,如果不存在将创建 * * @param key 键 * @param item 项 * @param value 值 * @return true 成功 false失败 */ public boolean hset(String key, String item, Object value) { try { redisTemplate.opsForHash().put(key, item, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 向一张hash表中放入数据,如果不存在将创建 * * @param key 键 * @param item 项 * @param value 值 * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间 * @return true 成功 false失败 */ public boolean hset(String key, String item, Object value, long time, TimeUnit timeUnit) { try { redisTemplate.opsForHash().put(key, item, value); if (time > 0) { expire(key, time, timeUnit); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 删除hash表中的值 * * @param key 键 不能为null * @param item 项 可以使多个 不能为null */ public void hdel(String key, Object... item) { redisTemplate.opsForHash().delete(key, item); } /** * 判断hash表中是否有该项的值 * * @param key 键 不能为null * @param item 项 不能为null * @return true 存在 false不存在 */ public boolean hHasKey(String key, String item) { return redisTemplate.opsForHash().hasKey(key, item); } /** * hash递增 如果不存在,就会创建一个 并把新增后的值返回 * * @param key 键 * @param item 项 * @param by 要增加几(大于0) */ public double hincr(String key, String item, double by) { return redisTemplate.opsForHash().increment(key, item, by); } /** * hash递减 * * @param key 键 * @param item 项 * @param by 要减少记(小于0) */ public double hdecr(String key, String item, double by) { return redisTemplate.opsForHash().increment(key, item, -by); } // ============================set============================= /** * 根据key获取Set中的所有值 * * @param key 键 */ public Set<Object> sGet(String key) { try { return redisTemplate.opsForSet().members(key); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 根据value从一个set中查询,是否存在 * * @param key 键 * @param value 值 * @return true 存在 false不存在 */ public boolean sHasKey(String key, Object value) { try { return redisTemplate.opsForSet().isMember(key, value); } catch (Exception e) { e.printStackTrace(); return false; } } /** * 将数据放入set缓存 * * @param key 键 * @param values 值 可以是多个 * @return 成功个数 */ public long sSet(String key, Object... values) { try { return redisTemplate.opsForSet().add(key, values); } catch (Exception e) { e.printStackTrace(); return 0; } } /** * 将set数据放入缓存 * * @param key 键 * @param time 时间(秒) * @param values 值 可以是多个 * @return 成功个数 */ public long sSetAndTime(String key, long time, TimeUnit timeUnit, Object... values) { try { Long count = redisTemplate.opsForSet().add(key, values); if (time > 0) { expire(key, time, timeUnit); } return count; } catch (Exception e) { e.printStackTrace(); return 0; } } /** * 获取set缓存的长度 * * @param key 键 */ public long sGetSetSize(String key) { try { return redisTemplate.opsForSet().size(key); } catch (Exception e) { e.printStackTrace(); return 0; } } /** * 移除值为value的 * * @param key 键 * @param values 值 可以是多个 * @return 移除的个数 */ public long setRemove(String key, Object... values) { try { Long count = redisTemplate.opsForSet().remove(key, values); return count; } catch (Exception e) { e.printStackTrace(); return 0; } } // ===============================list================================= /** * 获取list缓存的内容 * * @param key 键 * @param start 开始 * @param end 结束 0 到 -1代表所有值 */ public List<Object> lGet(String key, long start, long end) { try { return redisTemplate.opsForList().range(key, start, end); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 获取list缓存的长度 * * @param key 键 */ public long lGetListSize(String key) { try { return redisTemplate.opsForList().size(key); } catch (Exception e) { e.printStackTrace(); return 0; } } /** * 通过索引 获取list中的值 * * @param key 键 * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推 */ public Object lGetIndex(String key, long index) { try { return redisTemplate.opsForList().index(key, index); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 将list放入缓存 * * @param key 键 * @param value 值 */ public boolean lSet(String key, Object value) { try { redisTemplate.opsForList().rightPush(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 将list放入缓存 * * @param key 键 * @param value 值 * @param time 时间(秒) */ public boolean lSet(String key, Object value, long time,TimeUnit timeUnit) { try { redisTemplate.opsForList().rightPush(key, value); if (time > 0) { expire(key, time,timeUnit); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 将list放入缓存 * * @param key 键 * @param value 值 * @return */ public boolean lSet(String key, List value) { try { redisTemplate.opsForList().rightPushAll(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 将list放入缓存 * * @param key 键 * @param value 值 * @param time 时间(秒) * @return */ public boolean lSet(String key, List<Object> value, long time,TimeUnit timeUnit) { try { redisTemplate.opsForList().rightPushAll(key, value); if (time > 0) { expire(key, time,timeUnit); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 根据索引修改list中的某条数据 * * @param key 键 * @param index 索引 * @param value 值 * @return */ public boolean lUpdateIndex(String key, long index, Object value) { try { redisTemplate.opsForList().set(key, index, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 移除N个值为value * * @param key 键 * @param count 移除多少个 * @param value 值 * @return 移除的个数 */ public long lRemove(String key, long count, Object value) { try { Long remove = redisTemplate.opsForList().remove(key, count, value); return remove; } catch (Exception e) { e.printStackTrace(); return 0; } } // ===============================HyperLogLog================================= public long pfadd(String key, String value) { return redisTemplate.opsForHyperLogLog().add(key, value); } public long pfcount(String key) { return redisTemplate.opsForHyperLogLog().size(key); } public void pfremove(String key) { redisTemplate.opsForHyperLogLog().delete(key); } public void pfmerge(String key1, String key2) { redisTemplate.opsForHyperLogLog().union(key1, key2); } } ~~~
07-09
709
![](https://csdnimg.cn/release/blogv2/dist/pc/img/readCountWhite.png)
07-10
730
![](https://csdnimg.cn/release/blogv2/dist/pc/img/readCountWhite.png)
07-10
814
![](https://csdnimg.cn/release/blogv2/dist/pc/img/readCountWhite.png)
09-09
“相关推荐”对你有帮助么?
-
非常没帮助
-
没帮助
-
一般
-
有帮助
-
非常有帮助
提交