Redis–狂神说Redis基础汇总(完结)
2021.6.12-2021.6.14:端午学学玩玩弄完了Redis基础的汇总,越学越觉得自己知识量的匮乏。
参考链接:狂神说Java–Redis汇总:https://www.bilibili.com/video/BV1S54y1R7SB?p=1
1.String字符串
OK 127.0.0.1:6380> set k1 1 OK 127.0.0.1:6380> INCR k1 2 127.0.0.1:6380> DECR k1 1 127.0.0.1:6380> keys * k1 127.0.0.1:6380> set k2 huyuqiao OK 127.0.0.1:6380> GETRANGE K2 0 3 127.0.0.1:6380> GETRANGE k2 0 3 huyu 127.0.0.1:6380> GETRANGE k2 0 -1 huyuqiao 127.0.0.1:6380> SETRANGE k2 1 XX 8 127.0.0.1:6380> get k2 hXXuqiao 127.0.0.1:6380> SETEX k3 30 "hello, world" #set exist:存在或不存在都替换 OK 127.0.0.1:6380> ttl k3 28 127.0.0.1:6380> ttl k2 -1 127.0.0.1:6380> keys * k1 k2 127.0.0.1:6380> ttl k3 -2 127.0.0.1:6380> setnx mykey "redis" #set if not exist:不存在就set,存在就set失败,还是原来值 1 127.0.0.1:6380> ttl mykey -1 127.0.0.1:6380> setnx mykey "mongodb" 0 127.0.0.1:6380> get mykey redis 127.0.0.1:6380> ttl k2 #永久返回-1 -1 127.0.0.1:6380> ttl k3 #过期返回-2 -2 127.0.0.1:6380> 127.0.0.1:6380> mset user:1:name huyuqiao user:1:age 22 OK 127.0.0.1:6380> mget user:1:name user:1:age huyuqiao 22 127.0.0.1:6380>
List
127.0.0.1:6380> FLUSHALL OK 127.0.0.1:6380> clear 127.0.0.1:6380> LPUSH list 1 1 127.0.0.1:6380> LPUSH list 2 2 127.0.0.1:6380> LPUSH list 3 3 127.0.0.1:6380> LRANGE list 0 -1 3 2 1 127.0.0.1:6380> RPUSH list a 4 127.0.0.1:6380> LPOP list 3 127.0.0.1:6380> LRANGE 0 -1 ERR wrong number of arguments for 'lrange' command 127.0.0.1:6380> LRANGE list 0 -1 2 1 a 127.0.0.1:6380> LINDEX list 0 2 127.0.0.1:6380> LLEN list 3 127.0.0.1:6380> FLUSHALL OK 127.0.0.1:6380> clear 127.0.0.1:6380> LPUSH list one 1 127.0.0.1:6380> LPUSH list two 2 127.0.0.1:6380> LPUSH list two 3 127.0.0.1:6380> LREM list 1 one #移除list中等于one的一个元素(最后加入的移除掉) 1 127.0.0.1:6380> LRANGE list 0 -1 two two 127.0.0.1:6380> LREM list 2 one 0 127.0.0.1:6380> LREM list 2 two 2 127.0.0.1:6380> LRANGE list 0 -1 127.0.0.1:6380> FLUSHALL 127.0.0.1:6380> LPUSH list one 1 127.0.0.1:6380> LPUSH list two 2 127.0.0.1:6380> LPUSH list three 3 127.0.0.1:6380> LPUSH list four 4 127.0.0.1:6380> LTRIM list 1 2 OK 127.0.0.1:6380> LRANGE list 0 -1 three two 127.0.0.1:6380>
Set
127.0.0.1:6380> FLUSHALL OK 127.0.0.1:6380> SADD myset "hello" 1 127.0.0.1:6380> sadd myset "world" 1 127.0.0.1:6380> sadd myset "huyuqiao" 1 127.0.0.1:6380> smembers myset hello huyuqiao world 127.0.0.1:6380> SISMEMBER myset huyuqiao 1 127.0.0.1:6380> sadd myset "HYQ" 1 127.0.0.1:6380> SMEMBERS myset hello huyuqiao HYQ world 127.0.0.1:6380> SREM myset hello 1 127.0.0.1:6380> scard myset 3 127.0.0.1:6380> SMEMBERS myset huyuqiao HYQ world 127.0.0.1:6380> SRANDMEMBER myset world 127.0.0.1:6380> SRANDMEMBER myset huyuqiao 127.0.0.1:6380>
Hash
huyuqiao 127.0.0.1:6380> FLUSHALL OK 127.0.0.1:6380> clear 127.0.0.1:6380> hset myhash field1 huyuqiao 1 127.0.0.1:6380> hmset myhash field1 hello field2 world OK 127.0.0.1:6380> hmget myhash field1 field2 hello world 127.0.0.1:6380> hgetall myhash field1 hello field2 world 127.0.0.1:6380> hlen myhash 2 127.0.0.1:6380> HEXISTS myhash field1 1 127.0.0.1:6380> HKEYS myhash field1 field2 127.0.0.1:6380> HVALS myhash hello world 127.0.0.1:6380> HSETNX myhash field4 hello #hash设置 1 127.0.0.1:6380> HGETALL myhash field1 hello field2 world field4 hello 127.0.0.1:6380>
Zset
127.0.0.1:6380> zadd salary 100 huyuqiao 1 127.0.0.1:6380> zadd salary 200 HUYUQIAO 1 127.0.0.1:6380> zadd salary 300 HYQ 1 127.0.0.1:6380> ZRANGEBYSCORE salary -inf +inf huyuqiao HUYUQIAO HYQ 127.0.0.1:6380> zrange salary 0 -1 huyuqiao HUYUQIAO HYQ 127.0.0.1:6380> zrem salary huyuqiao 1 127.0.0.1:6380> ZRANGEBYSCORE salary -inf +inf HUYUQIAO HYQ
适用场景:获取附近人,好友距离,网约车定位功能。
# 添加位置经纬度 127.0.0.1:6379> GEOADD china:city 116.41667 39.91667 北京 (integer) 1 127.0.0.1:6379> GEOADD china:city 121.43333 31.23000 上海 (integer) 1 127.0.0.1:6379> GEOADD china:city 106.45000 29.56667 重庆 (integer) 1 127.0.0.1:6379> GEOADD china:city 114.06667 22.61667 深圳 (integer) 1 127.0.0.1:6379> GEOADD china:city 120.20000 30.26667 杭州 (integer) 1 127.0.0.1:6379> GEOADD china:city 108.95000 34.26667 西安 (integer) 1 # 查看不同位置经纬度 127.0.0.1:6379> GEOPOS china:city 北京 西安 #查看不同位置之间距离(默认是m) 127.0.0.1:6379> GEODIST china:city 北京 上海 "1066981.1340" 127.0.0.1:6379> GEODIST china:city 北京 上海 km "1066.9811" 127.0.0.1:6379> GEODIST china:city 北京 重庆 km "1465.8918" 127.0.0.1:6379> # 查看指定经纬度周边范围内的地方(可以以此扩散到周围指定范围内的指定人数的好友) 127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km 重庆 西安 深圳 杭州 127.0.0.1:6379> GEORADIUS china:city 110 30 500 km 重庆 西安 127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist #查看某经纬度周围500km的所有城市和距离/km 重庆 346.0548 西安 484.7511 127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withcoord #查看某经纬度周围500km的所有城市和经纬度 重庆 106.4500012993812561 29.56666939001875249 西安 108.95000249147415161 34.2666710302806834 127.0.0.1:6379> GEORADIUS china:city 110 30 500 km count 1 #查看某经纬度周围500km的三个城市 重庆 127.0.0.1:6379> GEORADIUS china:city 110 30 500 km count 3 #查看某经纬度周围500km的三个城市 重庆 西安 127.0.0.1:6379> #查看地点周边的所有城市(类似于定位) 127.0.0.1:6379> GEORADIUSBYMEMBER china:city 北京 1000 km 北京 西安 127.0.0.1:6379> GEORADIUSBYMEMBER china:city 上海 1000 km 杭州 上海 127.0.0.1:6379> #将城市的二维经纬度转成一位hash字符串 127.0.0.1:6379> GEOHASH china:city 北京 重庆 wx4g14s53n0 wm78nq6w2f0 127.0.0.1:6379> #GEO底层是zset,所有可以用zset命令来操作 127.0.0.1:6379> zrange china:city 0 -1 #查看所有城市 重庆 西安 深圳 杭州 上海 北京 127.0.0.1:6379> zrem china:city 北京 #删除北京这个城市
适用场景: 网站UV量。传统用set统计,但若存在大量用户id,则太消耗内容且麻烦,若只为计数且允许有错误率(0.81%),则可行,否则还是用set统计
基数:集合中不重复元素个数。如{1, 3, 5, 5 ,7}则为{1,3,5,7},基数为4
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-35mpJIkm-1623665896982)(https://i.loli.net/2021/06/13/pDS495ifgXAVrxb.png)]
127.0.0.1:6379> clear 127.0.0.1:6379> PFADD mykey a b c d e f g h i j #设置mykey 集合 (integer) 1 127.0.0.1:6379> PFCOUNT mykey #统计mykey 集合基数数量 (integer) 10 127.0.0.1:6379> PFADD mykey2 i j z x c v b n m (integer) 1 127.0.0.1:6379> PFCOUNT mykey2 (integer) 9 127.0.0.1:6379> PFMERGE mykey3 mykey mykey2 #2个集合取并集 OK 127.0.0.1:6379> PFCOUNT mykey3 (integer) 15 127.0.0.1:6379>
适用场景:判断、统计活跃、不活跃,登录、未登录这些非1即0的场景
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3nPIwQsQ-1623665896988)(https://i.loli.net/2021/06/13/F83vgB47L21XzMj.png)]
[root@VM-8-11-centos ~]# redis-cli -a root --raw 127.0.0.1:6379> setbit sign 0 1 #设置sign用户某天是否打卡 (integer) 0 127.0.0.1:6379> SETBIT sign 1 0 (integer) 0 127.0.0.1:6379> SETBIT sign 2 0 (integer) 0 127.0.0.1:6379> SETBIT sign 3 1 (integer) 0 127.0.0.1:6379> SETBIT sign 4 1 (integer) 0 127.0.0.1:6379> SETBIT sign 5 0 (integer) 0 127.0.0.1:6379> SETBIT sign 6 0 (integer) 0 127.0.0.1:6379> GETBIT sign 3 #得到sign用户某天打卡情况 (integer) 1 127.0.0.1:6379> GETBIT sign 6
Reidsi事务:没有隔离级别概念,即事务不保证原子性(事务内部有多条命令,不一定,可以实现部分成功、部分失败),但是单条命令保证原子性
#开启-结束事务 127.0.0.1:6379> multi OK 127.0.0.1:6379> set k1 v1 QUEUED 127.0.0.1:6379> set k2 v2 QUEUED 127.0.0.1:6379> get k2 QUEUED 127.0.0.1:6379> set k3 v3 QUEUED 127.0.0.1:6379> exec OK OK v2 OK 127.0.0.1:6379> #放弃事务:之前队列代码全部rollback 127.0.0.1:6379> set k1 v1 QUEUED 127.0.0.1:6379> set k2 asdfasdf QUEUED 127.0.0.1:6379> DISCARD OK 127.0.0.1:6379> EXEC ERR EXEC without MULTI 127.0.0.1:6379> get k2 v2
之所以事务非原子性,是因为有两种异常:
编译性异常(检查性异常):代码错了,根本就没某个语句、函数、配置文件等
127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set k1 v1 QUEUED 127.0.0.1:6379> set k2 v2 QUEUED 127.0.0.1:6379> gasdfa k3 #语句错误,相当于没执行,在编译时候就出错了 ERR unknown command 'gasdfa' 127.0.0.1:6379> set k4 v4 QUEUED 127.0.0.1:6379> EXEC EXECABORT Transaction discarded because of previous errors. 127.0.0.1:6379> get k4 127.0.0.1:6379> get k1
运行时异常(非检查性异常):没有某个变量、某个对象没有new等(不会全部rollback)
127.0.0.1:6379> set k1 "v1" OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> incr k1 QUEUED 127.0.0.1:6379> set k2 v2 QUEUED 127.0.0.1:6379> get k2 QUEUED 127.0.0.1:6379> EXEC ERR value is not an integer or out of range OK v2 #上面出错了,但是依然不影响下面语句执行,所以证明了redis事务是不保证原子性的 127.0.0.1:6379>
乐观锁:不会上锁,更新数据时才会比较version是否被人修改过(redis事务中如果被修改过,则事务不会执行成功)
悲观锁:无论做什么,都会上锁,效率低下但是安全
Redis事务中用<font color=red size=3>Watch</font>
实现乐观锁,中途如果被修改,导致version变更,则事务全部都不会成功
127.0.0.1:6379> set money 100 OK 127.0.0.1:6379> set out 0 OK 127.0.0.1:6379> watch money #Redis乐观锁:watch监视money OK 127.0.0.1:6379> MULTI #在另一个窗口将money改成了101 OK 127.0.0.1:6379> DECRBY money 10 QUEUED 127.0.0.1:6379> incrby out 10 QUEUED 127.0.0.1:6379> EXEC 127.0.0.1:6379> get money 101 127.0.0.1:6379>
Jedis:直连,多线程下不安全,类似于BIO模式(Springboot2.X后被淘汰)
Lettuce:采用Netty,多线程下共享,类似于NIO模式
autoconfig->srping.factories中找到redis相关配置
@Configuration(proxyBeanMethods = false) @ConditionalOnClass(RedisOperations.class) @EnableConfigurationProperties(RedisProperties.class) @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) public class RedisAutoConfiguration { @Bean @ConditionalOnMissingBean(name = "redisTemplate") public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); return template; } @Bean @ConditionalOnMissingBean public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { StringRedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(redisConnectionFactory); return template; } }
package com.empirefree.springboot.config; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonTypeInfo; 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; /** * @program: springboot * @description: RedisTemplate配置 * @author: huyuqiao * @create: 2021/06/13 16:05 */ @Configuration public class RedisConfig { @Bean @SuppressWarnings("all") public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<String, Object>(); template.setConnectionFactory(factory); // Json序列化配置 Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY); jackson2JsonRedisSerializer.setObjectMapper(om); // String 的序列化 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; } }
package com.empirefree.springboot.utils; 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; /** * @program: springboot * @description: Redis工具类 * @author: huyuqiao * @create: 2021/06/13 16:14 */ @Component public final class RedisUtil { @Autowired private RedisTemplate<String, Object> redisTemplate; // =============================common============================ /** * 指定缓存失效时间 * @param key 键 * @param time 时间(秒) */ public boolean expire(String key, long time) { try { if (time > 0) { redisTemplate.expire(key, time, TimeUnit.SECONDS); } 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)); } } } // ============================String============================= /** * 普通缓存获取 * @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) { try { redisTemplate.opsForHash().putAll(key, map); if (time > 0) { expire(key, time); } 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) { try { redisTemplate.opsForHash().put(key, item, value); if (time > 0) { expire(key, time); } 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, Object... values) { try { Long count = redisTemplate.opsForSet().add(key, values); if (time > 0) expire(key, time); 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) { try { redisTemplate.opsForList().rightPush(key, value); if (time > 0) expire(key, time); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 将list放入缓存 * * @param key 键 * @param value 值 * @return */ public boolean lSet(String key, List<Object> 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) { try { redisTemplate.opsForList().rightPushAll(key, value); if (time > 0) expire(key, time); 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; } } }
持久化:在指定时间间隔内将内存数据存入磁盘中,断电也能恢复数据,使用快照文件读到内存中。
保存过程:父进程fork一个子进程,将数据持久化到临时文件中,持久化结束,再替换上次的RDB正式文件。
触发条件:
save 900 1
即是在15分钟内修改了1次 即会触发RDB。适用场景:适合大规模数据恢复且数据完整性不敏感的情况。
保存过程:父进程fork一个子进程,以日志形式将所有指令记录下来(读操作不记录),然后将数据只追加不改写到AOF文件,然后替换上次的AOF文件
触发条件:appendfsync always/everysec/no
命令
适用场景:对恢复数据完整性要求严格
重写场景:不断追加文件到一个阈值,则会重写aof文件
发布订阅:可以做消息推送、聊天室等等
#发布者:往Redis某渠道中发消息,所有订阅者都可以接收到 127.0.0.1:6379> PUBLISH huyuqiao "hello,world" 1 #订阅者:订阅Redis中某个渠道channel 127.0.0.1:6379> SUBSCRIBE huyuqiao subscribe huyuqiao 1 message huyuqiao hello,world
#master断了,slaver依然是原来master的slaver。但是slaver断了,master就没有slaver了 >>slaveof no one #关闭slave状态,变成master >>shutdown #停止redis,其slave状态停止,下次启动变成master,且原来master下的该redis就没有了 redis-server redis80.conf #启动80redis窗口。 kill -s 9 pid #关闭某个进程
流程:如下,在启动哨兵后,关闭79主服务,就从80,81中投票选举了80为主服务,然后把79,80都当成了slave,所以下次79启动的时候会默认是slave。另外,由于数据是增量复制,所以数据在80中保存完好,且79启动后就全量复制到79中了
#哨兵模式下的日志 8044:X 14 Jun 16:35:06.885 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. 8044:X 14 Jun 16:35:06.885 # Sentinel ID is c0ce22fc8365ff48663b7db710ce8c359529c3d9 8044:X 14 Jun 16:35:06.885 # +monitor master mymaster 127.0.0.1 6379 quorum 1 8044:X 14 Jun 16:35:51.556 # +sdown master mymaster 127.0.0.1 6379 8044:X 14 Jun 16:35:51.556 # +odown master mymaster 127.0.0.1 6379 #quorum 1/1 8044:X 14 Jun 16:35:51.556 # +new-epoch 1 8044:X 14 Jun 16:35:51.556 # +try-failover master mymaster 127.0.0.1 6379 8044:X 14 Jun 16:35:51.572 # +vote-for-leader c0ce22fc8365ff48663b7db710ce8c359529c3d9 1 8044:X 14 Jun 16:35:51.572 # +elected-leader master mymaster 127.0.0.1 6379 8044:X 14 Jun 16:35:51.572 # +failover-state-select-slave master mymaster 127.0.0.1 6379 8044:X 14 Jun 16:35:51.624 # +selected-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379 8044:X 14 Jun 16:35:51.624 * +failover-state-send-slaveof-noone slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379 8044:X 14 Jun 16:35:51.684 * +failover-state-wait-promotion slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379 8044:X 14 Jun 16:35:51.826 # +promoted-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379 8044:X 14 Jun 16:35:51.826 # +failover-state-reconf-slaves master mymaster 127.0.0.1 6379 8044:X 14 Jun 16:35:51.914 * +slave-reconf-sent slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379 8044:X 14 Jun 16:35:52.831 * +slave-reconf-inprog slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379 8044:X 14 Jun 16:35:52.831 * +slave-reconf-done slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379 8044:X 14 Jun 16:35:52.912 # +failover-end master mymaster 127.0.0.1 6379 8044:X 14 Jun 16:35:52.912 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6380 8044:X 14 Jun 16:35:52.912 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6380 8044:X 14 Jun 16:35:52.912 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380