9.3 AOF文件的重写
1. AOF带来的问题
AOF的方式也同时带来了另一个问题。持久化文件会变的越来越大。例如我们调用incr test命令100次,文件中必须保存全部的100条命令,其实有99条都是多余的。因为要恢复数据库的状态其实文件中保存一条set test 100就够了。为了压缩aof的持久化文件Redis提供了AOF重写(ReWriter)机制。
2. AOF重写
用来在一定程度上减小AOF文件的体积
3. 触发重写方式
# 1.客户端方式触发重写 - 执行BGREWRITEAOF命令 不会阻塞redis的服务 # 2.服务器配置方式自动触发 - 配置redis.conf中的auto-aof-rewrite-percentage选项 参加下图↓↓↓ - 如果设置auto-aof-rewrite-percentage值为100和auto-aof-rewrite-min-size 64mb,并且启用的AOF持久化时,那么当AOF文件体积大于64M,并且AOF文件的体积比上一次重写之后体积大了至少一倍(100%)时,会自动触发,如果重写过于频繁,用户可以考虑将auto-aof-rewrite-percentage设置为更大
4. 重写原理
注意:重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,替换原有的文件这点和快照有点类似。
# 重写流程 - 1. redis调用fork ,现在有父子两个进程 子进程根据内存中的数据库状态快照,往临时文件中写入重建数据库状态的命令 - 2. 父进程继续处理client请求,除了把写命令写入到原来的aof文件中。同时把收到的写命令缓存起来。这样就能保证如果子进程重写失败的话并不会出问题。 - 3. 当子进程把快照内容写入新文件已命令方式写到临时文件中后,子进程发信号通知父进程。然后父进程把缓存的写命令也写入到临时文件。 - 4. 现在父进程可以使用临时文件替换老的aof文件,并重命名,后面收到的写命令也开始往新的aof文件中追加。 1.创建父子进程 2.子进程将当前的数据状态拍一个快照 3.将快照转化为写命令 4.父进程急需处理客户端写操作 5.继续将写命令向老的Aof文件中追加 同时再缓存一份 新的谢命令 6.子进程将快照写入临时文件之后通知父进程,父进程将缓存的新命令加入临时文件中 7.使用新的Aof文件替换老的Aof文件
9.4 持久化总结
两种持久化方案既可以同时使用(aof),又可以单独使用,在某种情况下也可以都不使用,具体使用那种持久化方案取决于用户的数据和应用决定。
无论使用AOF还是快照机制持久化,将数据持久化到硬盘都是有必要的,除了持久化外,用户还应该对持久化的文件进行备份(最好备份在多个不同地方)。
10. java操作Redis
10.1 环境准备
1. 引入依赖
<!--引入jedis连接依赖--> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency>
2.创建jedis对象
public static void main(String[] args) { //1.创建jedis对象 Jedis jedis = new Jedis("192.168.112.102", 6379);//1.redis服务必须关闭防火墙 2.redis服务必须开启远程连接 jedis.select(0);//选择操作的库默认0号库 //2.执行相关操作 //.... //3.释放资源 jedis.close(); }
10.2 操作key相关API
private Jedis jedis; @Before public void before(){ this.jedis = new Jedis("192.168.202.205", 7000); } @After public void after(){ jedis.close(); } //测试key相关 @Test public void testKeys(){ //删除一个key jedis.del("name"); //删除多个key jedis.del("name","age"); //判断一个key是否存在exits Boolean name = jedis.exists("name"); System.out.println(name); //设置一个key超时时间 expire pexpire Long age = jedis.expire("age", 100); System.out.println(age); //获取一个key超时时间 ttl Long age1 = jedis.ttl("newage"); System.out.println(age1); //随机获取一个key String s = jedis.randomKey(); //修改key名称 jedis.rename("age","newage"); //查看可以对应值的类型 String name1 = jedis.type("name"); System.out.println(name1); String maps = jedis.type("maps"); System.out.println(maps); }
10.3操作String相关API
//测试String相关 @Test public void testString(){ //set jedis.set("name","小陈"); //get String s = jedis.get("name"); System.out.println(s); //mset jedis.mset("content","好人","address","海淀区"); //mget List<String> mget = jedis.mget("name", "content", "address"); mget.forEach(v-> System.out.println("v = " + v)); //getset String set = jedis.getSet("name", "小明"); System.out.println(set); //............ }
10.4操作List相关API
//测试List相关 @Test public void testList(){ //lpush jedis.lpush("names1","张三","王五","赵柳","win7"); //rpush jedis.rpush("names1","xiaomingming"); //lrange List<String> names1 = jedis.lrange("names1", 0, -1); names1.forEach(name-> System.out.println("name = " + name)); //lpop rpop String names11 = jedis.lpop("names1"); System.out.println(names11); //llen jedis.linsert("lists", BinaryClient.LIST_POSITION.BEFORE,"xiaohei","xiaobai"); //........ }
10.5操作Set的相关API
//测试SET相关 @Test public void testSet(){ //sadd jedis.sadd("names","zhangsan","lisi"); //smembers jedis.smembers("names"); //sismember jedis.sismember("names","xiaochen"); //... }
10.6 操作ZSet相关API
//测试ZSET相关 @Test public void testZset(){ //zadd jedis.zadd("names",10,"张三"); //zrange jedis.zrange("names",0,-1); //zcard jedis.zcard("names"); //zrangeByScore jedis.zrangeByScore("names","0","100",0,5); //.. }
10.7 操作Hash相关API
//测试HASH相关 @Test public void testHash(){ //hset jedis.hset("maps","name","zhangsan"); //hget jedis.hget("maps","name"); //hgetall jedis.hgetAll("mps"); //hkeys jedis.hkeys("maps"); //hvals jedis.hvals("maps"); //.... }
11.SpringBoot整合Redis
Spring Boot Data(数据) Redis 中提供了RedisTemplate和StringRedisTemplate,其中StringRedisTemplate是RedisTemplate的子类,两个方法基本一致,不同之处主要体现在操作的数据类型不同,RedisTemplate中的两个泛型都是Object,意味着存储的key和value都可以是一个对象,而StringRedisTemplate的两个泛型都是String,意味着StringRedisTemplate的key和value都只能是字符串。
存储手机验证码 redis 60s
获取验证码:15236674712 发送(fdsfs) key:15236674712(超时时间) value:fdsfs
登录验证 :15236674712 fdsfs value=get(15236674712)
list key value() cart book book book
user 购物车(一条一条的购物项) hash KEY(用户标识) key(商品id) value(购物项对象)
排行榜 zset key(排行榜标识) 搜索的关键字(评分 )
注意: 使用RedisTemplate默认是将对象序列化到Redis中,所以放入的对象必须实现对象序列化接口
11.1 环境准备
1.引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
2.配置application.propertie
spring.redis.host=localhost spring.redis.port=6379 spring.redis.database=0
11.2 StringRedisTemplate操作
1.key相关的操作
//key相关操作 @Test public void testKey() { //查看所有key Set<String> keys = stringRedisTemplate.keys("*"); keys.forEach(key -> System.out.println(key)); //删除一个key //成功返回true 否则返回false System.out.println("name是否删除成功: "+stringRedisTemplate.delete("name")); //删除多个key //删除成功几个key返回就是数字几 System.out.println("成功删除了个数为: "+stringRedisTemplate.delete(Arrays.asList("name", "age"))); //判断某个key是否存在 //存在返回 true System.out.println("maps是否存在: "+stringRedisTemplate.hasKey("maps")); //设置key超时时间 参数:key,时间,时间单位 Boolean nsme = stringRedisTemplate.expire("nsme", 30, TimeUnit.SECONDS); System.out.println("给nsme设置超时时间的状态:"+nsme); //获取key超时时间 //返回的是存活时间 默认毫秒 System.out.println("name过期时间: "+stringRedisTemplate.getExpire("name")); //-2 //参数1:查看那个key超时时间 参数2:返回时间单位 System.out.println("lists超时时间: "+stringRedisTemplate.getExpire("lists", TimeUnit.SECONDS)); //-2 //移动key 到某一个库 System.out.println("是否移动成功: "+stringRedisTemplate.move("sets", 3)); //随机返回一个key System.out.println("随机返回的key: "+stringRedisTemplate.randomKey()); }
2.String类型的操作
@Test public void testString(){ //设置一个值 //永久设置一个key value stringRedisTemplate.opsForValue().set("name","张三"); //设置一个key同时 指定key超时时间 stringRedisTemplate.opsForValue().set("age","19",10,TimeUnit.HOURS); //在value后追加值 stringRedisTemplate.opsForValue().append("name"," 他是一个好人"); //给key对应数字类型value进行自减操作 -1 stringRedisTemplate.opsForValue().decrement("age"); //参数2:自减步长 stringRedisTemplate.opsForValue().decrement("age",2); System.out.println(stringRedisTemplate.opsForValue().get("name")); System.out.println(stringRedisTemplate.opsForValue().get("age")); //获取value指定范围内结果 System.out.println(stringRedisTemplate.opsForValue().get("name", 0, -1)); //获取原始并设置新value System.out.println(stringRedisTemplate.opsForValue().getAndSet("name", "小明明")); //存在key不做任何操作不存在则设置value stringRedisTemplate.opsForValue().setIfAbsent("aa","xiaoming"); //一次性设置多个key value 只要其中存在一个key 不做任何操作 原子性 //stringRedisTemplate.opsForValue().multiSetIfAbsent(map) //获取key所对应值的长度 System.out.println(stringRedisTemplate.opsForValue().size("name")); }
3.List类型的操作
@Test public void testList(){ //获取list类型操作对象 ListOperations<String, String> listOperations = stringRedisTemplate.opsForList(); //创建lists列表并放入一个值 listOperations.leftPush("lists","zhangsan"); //创建lists列表并放入多个元素 listOperations.leftPushAll("names","张三","李四","王五"); listOperations.leftPushAll("ages",Arrays.asList("19","18","17")); //获取一个列表 listOperations.range("names",0,-1).forEach(name-> System.out.println(name)); System.out.println("================================="); //列表中某个元素之前 leftpush 之后 rightpush插入元素 listOperations.leftPush("names","李四","小陈"); listOperations.rightPush("names","李四","小明"); listOperations.range("names",0,-1).forEach(name-> System.out.println(name)); //获取指定下标的元素 System.out.println(listOperations.index("names", 1)); //向list放入一个值 保证list必须存在 不会创建新的列表 listOperations.leftPushIfPresent("names","xxx"); //names: xx 王五 ... ages: 小明 17 18 19 //针对于两个列表 说明:从参数1列表右边弹出一个元素并把弹出元素放入参数2列表左边 参数1: 列表 参数2:列表 System.out.println(listOperations.rightPopAndLeftPush("names","ages")); //remove删除重复元素 listOperations.remove("names",2,"zhangsan"); //根据下标修改一个值 listOperations.set("names",2,"xiaochen"); //截取list列表中指定内容 注意:修改list内容 listOperations.trim("names",0,4); //查询列表长度 System.out.println(listOperations.size("names")); }
4.Set类型的操作
@Test public void testSet(){ SetOperations<String, String> setOperations = stringRedisTemplate.opsForSet(); //向set集合添加一个元素 setOperations.add("sets","xxx","123","234"); //查看集合成员 setOperations.members("sets").forEach(val-> System.out.println(val)); System.out.println("==================================="); //从set集合随机返回指定元素 setOperations.distinctRandomMembers("sets",4).forEach( val-> System.out.println(val)); //查询集合元素个数 System.out.println(setOperations.size("sets")); //从集合中返回并移除一个元素 System.out.println(setOperations.pop("sets")); //从一个集合移动到另一个集合中 注意:必须是同一种类型 setOperations.move("sets","xxx","sets1"); //去掉第一个集合中其它集合含有的相同元素 setOperations.difference("sets","sets1"); setOperations.difference("sets",Arrays.asList("sets1","sets2","sets3")); //求多个集合元素交集 setOperations.intersect("sets1","sets"); setOperations.intersect("sets1",Arrays.asList("sets","sets2")); //求多个集合合集 setOperations.union("sets","sets1"); setOperations.union("sets",Arrays.asList("sets1","sets2")); }
5.ZSet类型的操作
@Test public void testZset(){ //获取Zset操作对象 ZSetOperations<String, String> zSetOperations = stringRedisTemplate.opsForZSet(); //创建一个zsets集合并放入一个元素 zSetOperations.add("zsets","张三",10.0); HashSet<ZSetOperations.TypedTuple<String>> sets = new HashSet<>(); sets.add(new DefaultTypedTuple<>("小黑",8.0)); sets.add(new DefaultTypedTuple<>("小名",2.0)); sets.add(new DefaultTypedTuple<>("小网",6.0)); zSetOperations.add("zsets",sets); //返回zsets集合中指定范围元素 zSetOperations.range("zsets", 0, -1).forEach(el-> System.out.println(el)); //all System.out.println("========================"); //all score zSetOperations.rangeWithScores("zsets", 0, -1).forEach(t-> System.out.println("el: "+t.getValue()+" score: "+t.getScore())); System.out.println("========================"); //返回zsets集合中指定分数范围的元素 //小x zSetOperations.rangeByScore("zsets",0.0,8.0).forEach(el-> System.out.println(el)); System.out.println("========================"); //分页返回zsets集合中指定分数范围的元素 //小名 小网 zSetOperations.rangeByScore("zsets",0.0,8.0,0,2).forEach(el-> System.out.println(el)); System.out.println("========================"); //小x score zSetOperations.rangeByScoreWithScores("zsets",0.0,8.0).forEach(t-> System.out.println("el: "+t.getValue()+" score: "+t.getScore())); //正向排名 升序 zSetOperations.rank("zsets","张三"); //反向排名 降序 zSetOperations.reverseRank("zsets","张三"); //返回指定元素分数 zSetOperations.score("zsets","小名"); //移除某一个元素 zSetOperations.remove("zsets","张三"); //给某个元素自增分数 zSetOperations.incrementScore("zsets","张三",2.0); }
6.Hash类型的操作
@Test public void testHash(){ //获取hash类型操作对象 HashOperations<String, Object, Object> hashOperations = stringRedisTemplate.opsForHash(); //hset hashOperations.put("maps","name","张三"); //hmset HashMap<String, String> m = new HashMap<>(); m.put("address","xxx"); m.put("content","xxx"); hashOperations.putAll("maps", m); //hgetall hashOperations.entries("maps").forEach((k,v)-> System.out.println("key: "+k +" value: "+v)); //hget hashOperations.get("maps","name"); //hmget hashOperations.multiGet("maps",Arrays.asList("name","addres","content")).forEach(val-> System.out.println(val)); //hdel hashOperations.delete("maps","name"); //hexists hashOperations.hasKey("maps","name"); //hkeys hashOperations.keys("maps"); //hvals hashOperations.values("maps"); //hsetnx hashOperations.putIfAbsent("maps","name","xiaochen"); //hincrby hashOperations.increment("maps","age",11.11); }