Redis 入门及实战

目录

1. Redis 基本概念

2. Redis 的优势

3. Redis 适用场景

4. Redis-3.2.6 安装(未整理)与测试

5. 使用 Redis 的 Java API 客户端——Jedis

6. 数据结构

6.1 String -- 字符串

6.1.1 String 使用概述

6.1.2 String 常用操作

6.1.3 String 使用案例

6.2 List -- 列表

6.2.1 List 使用概述

6.2.2 List 应用案例

6.3 Set -- 集合

6.3.1 Set 使用概述

6.3.2 Set 使用案例

6.4 ZSet -- 有序集合

6.4.1 ZSet 使用概述

6.4.2 ZSet 使用案例

6.5 Hash -- 哈希表

6.5.1 Hash 使用概述

6.5.2 Hash 案例

6.6 键值相关的命令

6.7 服务器相关命令

6.8 Redis 事务

7. Redis 的高可用

8. Redis 的持久化

8.1 RDB 持久化

8.2 AOF 持久化

8.3 总结


1. Redis 基本概念

Remote Dictionary Server(Redis) 是一个由 Salvatore Sanfilippo 写的开源的、高性能的、使用 ANSI C 语言编写的、遵守 BSD 协议、支持网络、可基于内存亦可持久化的日志型、Key-Value 存储系统,并提供多种语言的 API。

和 memcached 类似,Redis 常被称作是一款 Key-Value 内存存储系统或者内存数据库,同时由于它支持丰富的数据结构,又被称为一种数据结构服务器(Data Structure Server),因为其值(Value)可以是字符串(String)、哈希(Map)、列表(List)、集合(Set)和有序集合(Sorted Set)等类型。

 

Redis 与其他 Key-Value 缓存产品有如下三个特点:

1. Redis 支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用

2. Redis 不仅仅支持简单的 Key-Value 类型的数据,同时还提供 List, Set, ZSet, Hash 等数据结构的存储

3. Redis 支持数据的备份,即 Master-Slave 模式的数据备份

 

2. Redis 的优势

1. 性能极高:Redis 读的速度可达 110000 次/s,写的速度可达 81000 次/s

2. 丰富的数据类型:Redis 支持二进制案例的 String, List, Hash, Set 以及 Sorted Set 数据类型的操作

3. 原子操作:Redis 的所有操作都是原子性的,同时 Redis 还支持对几个操作合并后的原子性执行

4. 丰富的特性:Redis 还支持 Publish/Subscribe,通知 Key 过期,支持高可用集群等特性

5. 数据持久化机制:持久化机制有两种:1)RDB 方式:定期将内存数据 dump 到磁盘        2)AOF(Append Only File) 持久化机制:用记日志的方式记录每一条数据更新操作,一旦出现灾难事件,可以通过日志重放来恢复整个数据库

 

3. Redis 适用场景

1. TopN 需求:取最新的 N 个数据,如读取作家博客最新的 50 篇文章,通过 List 实现对按时间排序的数据的高效读取

2. 排行榜应用:以特定条件为排序标准,将其设成 Sorted Set 的 score,进而实现高效获取

3. 需要精准设定过期时间的应用:把 Sorted Set 的 score 值设置成过期时间的时间戳,那么就可以简单地通过过期时间排序,定时清除过期数据了

4. 计数器应用:Redis 的命令都是原子性的,可以轻松地利用 INCR, DECR 命令来构建计数器系统

5. 去除大量数据中的重复数据:将数据放入 Set 中,就能实现对重复数据的排除

6. 构建队列系统:使用 List 可以构建队列系统,使用 Sorted Set 甚至可以构建有优先级的队列系统

7. 实时系统、反垃圾系统:通过上面说到的 Set 功能,可以知道有一个终端用户是否进行了某个操作,可以找到其操作的集合并进行分析统计对比等

8. Publish/Subscribe 构建实时消息系统

9. 缓存(会话、商品列表、评论列表、经常查询的数据等)

 

以一个某技术社区为例:

记录帖子的点赞数、评论数和点击数(Hash)

记录用户的帖子 ID 列表 (排序),便于快速显示用户的帖子列表(ZSet)

记录帖子的标题、摘要、作者和封面信息,用于列表页展示(Hash)

记录帖子的点赞用户 ID 列表,评论 ID 列表,用于显示和去重计数(ZSet)

缓存近期热帖内容 (帖子内容空间占用比较大),减少数据库压力 (Hash)

记录帖子的相关文章 ID,根据内容推荐相关帖子(List)

如果帖子 ID 是整数自增的,可以使用 Redis 来分配帖子 ID(计数器)

收藏集和帖子之间的关系(ZSet)

记录热榜帖子 ID 列表,总热榜和分类热榜(ZSet)

缓存用户行为历史,进行恶意行为过滤(ZSet,Hash)

 

4. Redis-3.2.6 安装(未整理)与测试

官网:https://redis.io/ 下载安装包

假设安装已完成

启动 Redis Server

redis-server

后台启动

nohup redis-server 1>~/data/redis/redis_std.log 2>~/data/redis/redis_err.log &

启动客户端执行命令

redis-cli

检测 Redis 是否正常工作

PING

 

5. 使用 Redis 的 Java API 客户端——Jedis

1. 新建一个 Maven 工程,导入如下依赖:

<dependency> <groupId>redis.clients</groupId> <artiFactId>jedis</artiFactId> <version>2.9.0</version> 
</dependency>

2. 编写一个测试类用来测试客户端是否可以访问服务器

package cn.gldwolf.jedis import redis.clients.jedis.Jedis; \public class JedisClientDriver { public static void main(String[] args) { // 创建一个 Jedis 客户端对象 Jedis client = new Jedis("hdp01", 6379); // 测试服务器是否连通 String response = client.ping(); Sytstme.out.println(response); // 若能 ping 通,则会返回一个 PONG } 
}

 

6. 数据结构

6.1 String -- 字符串

6.1.1 String 使用概述

String 是 Redis 最基本的类型,可以理解成与 Memcached 一模一样的类型,一个 Key 对应一个 Value.

String 类型是二进制安全的。意思是 Redis 的 String 可以包含任何数据,比如 jpg 图片或者序列化的对象。

String 类型是 Redis 最基本的数据类型,一个键最大能存储 512MB.

命令

介绍

SET key value

设置值

SETNX key value

如果 Key 存在,返回 0 且修改不生效

SETEX key seconds value

指定有效期为 seconds 秒

SETRANGE key offset value

将 Key 对应的 Value 第 n 位后面的字符替换成 value

MSET key value [key value ...]

一次设置多个值

MSETNX key value [key value ...]

类似 SETNX,设置多个值,如果 Key 存在,则返回 0 且修改不生效

GET key

获取 Key 对应的值

GETSET key value

设置 Key 的值,并返回 Key 的旧值

GETRANGE key start end

获取索引位置从 start 到 end 的 Key 对应值的字符串

MGET key [key ...]

一次获取多个 Key 对应的值,如果不存在,则返回 nil

INCR key

对 Key 的值作 +1 操作,如果 INCR 一个不存在的值,则对 Key 赋值为 1,如果 Key 对应值不是 Int 类型,则返回错误:-ERR value is not an integer or out of range

INCRBY key increment

加指定值 increment,Key 不存在的时候会设置 Key,并认为原来的 Value 为 0

DECR key

对 Key 的值作 -1 操作,DECR 一个不存在的 Key,则设置 Key 为 -1

DECRBY key decrement

减指定值

APPEND key value

将 value 追加到 Key 对应的值的末尾,返回新字符串的长度

STRLEN key

读取 Key 对应的 value 的长度

 

6.1.2 String 常用操作

1. 插入和读取一条 String 类型的数据

2. 对 String 类型数据进行增减(前提是这条数据的 Value 可以看作数字)

3. 一次性插入或者获取多条数据

4. 在插入一条 String 类型的数据的同时为它指定一个存活期限

如下所示:test 只有 7 秒的存活期,7 秒之后会自动删除

 

6.1.3 String 使用案例

1. 将对象序列化成 byte 数组

package cn.gldwolf.jedis;import redis.clients.jedis.Jedis;import java.io.*;/*** @author: Gldwolf* @email: ZengqiangZhao@sina.com* @date: 2019/6/16 22:47*/class Student implements Serializable {  // 必须要实现序列化private String name;private String sex;private int age;public Student() {}public Student(String name, int age, String sex) {this.name = name;this.age = age;this.sex = sex;}@Overridepublic String toString() {return "Student name: " + name + ", age: " + age + ", sex: " + sex;}
}public class TestJedisObject {public static void main(String[] args) throws IOException, ClassNotFoundException {// 创建一个 Jedis 连接对象Jedis jedis = new Jedis("hdp01", 6379);Student student = new Student("唐伯虎", 23, "男");// 将对象序列化成字节数组ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos);// 用对象序列化流来将 student 对象序列化,然后把序列化之后的二进制数据写到 baos 流中oos.writeObject(student);// 将 baos 转换成字节数组byte[] sBytes = baos.toByteArray();// 将对象序列化之后的 byte 数组存到 Redis 的 String 数据结构中jedis.set("student_Tang".getBytes(), sBytes);// 根据 Key 从 Redis 中取出对象的 byte 数据byte[] response = jedis.get("student_Tang".getBytes());// 将字节数据反序列化出对象ByteArrayInputStream bais = new ByteArrayInputStream(response);ObjectInputStream ois = new ObjectInputStream(bais);// 从对象读取流中读取出 responseStudent 对象Student responseStudent = (Student)ois.readObject();System.out.println(responseStudent);}
}

2. 将对象转换成 JSON 字符串进行存储

package cn.gldwolf.jedis;import com.google.gson.Gson;
import redis.clients.jedis.Jedis;import java.io.*;/*** @author: Gldwolf* @email: ZengqiangZhao@sina.com* @date: 2019/6/16 23:00*/class Student {  // 这种方式就不需要实现序列化private String name;private String sex;private int age;public Student() {}public Student(String name, int age, String sex) {this.name = name;this.age = age;this.sex = sex;}@Overridepublic String toString() {return "Student name: " + name + ", age: " + age + ", sex: " + sex;}
}public class JedisClientDriver {public static void main(String[] args) throws IOException, ClassNotFoundException {// 创建一个 Jedis 连接对象Jedis jedis = new Jedis("hdp01", 6379);Student student = new Student("唐伯虎", 23, "男");// 利用 gson 将对象转成 json 串Gson gson = new Gson();String pJson = gson.toJson(student);// 将 json 串存入 Redisjedis.set("student_Tang", pJson);// 从 Redis 中取出对象的 json 串String responseJson = jedis.get("student_Tang");// 将返回的 json 串解析成 Student 对象Student responseStudent = gson.fromJson(responseJson, Student.class);System.out.println(responseStudent);}
}

6.2 List -- 列表

6.2.1 List 使用概述

Redis 列表是简单的字符串列表,按照插入顺序排序。我们可以添加一个元素到列表的头部(左边)或者尾部(右边)。

命令

介绍

LPUSH/RPUSH key value [value ...]

从头/尾部向 list 添加值,返回 list 长度

LRANGE key start stop

返回 list 对应索引区间的值

LINSERT/RINSERT key BEFORE|AFTER pivot value

在 list 的 pivot 的前面/后面插入 value

LSET key index value

将特定索引的值设置为 value, 注意:如果 index 为负值,则从 list 尾部开始算起

LREM key count value

从 list 中删除 count 个和 value 相同的值,若 count > 0,则从链头算起,若 count < 0,则从链尾算起,若 count = 0,则删除全部

LTRIM key start stop

仅保留 list 中索引从 start 到 end 的值

LPOP/RPOP key

从头部/尾部删除元素,同时返回该元素

RPOPLPUSH source destination

从 source 的尾部移除元素并添加到 destination 的头部,最后返回被移除的元素值,整个操作是原子性的,如果 source 是空或者不存在则返回 nil

LINDEX key index

返回 list 中 index 索引位置的元素

LLEN key

返回 list 的长度

 

6.2.2 List 应用案例

1. 需求描述

任务调度系统:生产者不断产生任务,放入 task-queue 排队,消费者不断拿出任务来进行处理,同时放入一个 temp-queue 队列暂存,如果任务处理成功,则清除 temp-queue,否则,将任务弹回 task-queue

 

2. 架构思路图解

 

3. 代码实现

TaskProducer.class

package cn.gldwolf.jedis;import redis.clients.jedis.Jedis;import java.util.Random;
import java.util.UUID;/*** @author: Gldwolf* @email: ZengqiangZhao@sina.com* @date: 2019/6/17 15:44*/public class TaskProducer {public static void main(String[] args) {Jedis jedis = new Jedis("hdp01", 6379);Random random = new Random();// 生成任务while (true) {try {// 生成任务的速度有一定的随机性,在 1-2 秒之间Thread.sleep(random.nextInt(1000) + 1000);// 生成一个 taskIdString taskId = UUID.randomUUID().toString();// 往任务队列 "task-queue" 中插入,第一次插入时,"task-queue" 还不存在// 但是 lpush 方法会在 Redis 库中创建一个新的 list 数据jedis.lpush("task-queue", taskId);System.out.println("在任务队列中加入一个新的任务:" + taskId);} catch (InterruptedException e) {e.printStackTrace();}}}
}

TaskConsumer.class

package cn.gldwolf.jedis;import redis.clients.jedis.Jedis;import java.util.Random;
import java.util.UUID;/*** @author: Gldwolf* @email: ZengqiangZhao@sina.com* @date: 2019/6/17 11:31*/public class TaskConsumerForRedisList {public static void main(String[] args) {Jedis jedis = new Jedis("hdp01", 6379);Random random = new Random();while (true) {try {// 获取 taskId,将 taskId 从 "task-queue" 中弹出,并放到 "temp-queue" 队列中String taskId = jedis.rpoplpush("task-queue", "temp-queue");// 模拟处理任务的耗时Thread.sleep(1000);// 模拟有成功有失败的情况int nextInt = random.nextInt(10);// 如果任务队列中没有任务,则等待 0.5 秒if (jedis.llen("task-queue") == 0) {Thread.sleep(500);} else {// 模拟失败的情况:当 taskId 为 8 时,设置任务处理失败if (nextInt == 8) {// 失败的情况下,需要将任务从 "temp-queue" 中弹回 "task-quque"jedis.rpoplpush("temp-queue", "task-queue");System.out.println("===== 任务处理失败,失败的任务 ID 为:" + taskId + " =====");} else {// 成功的情况下,将任务从 "temp-queue" 中删除jedis.rpop("temp-queue");System.out.println("***** 任务处理成功,任务 ID 为:" + taskId + " *****");}}} catch (InterruptedException e) {e.printStackTrace();}}}
}

6.3 Set -- 集合

6.3.1 Set 使用概述

Redis 的 Set 是 String 类型的无序集合。

集合是通过哈希表实现的,所以添加、删除、查找的时间复杂度都是 O(1)。

命令

介绍

SMEMBERS key

查看 Set 中的元素

SADD key member [member ...]

向 Set 中插入 value,成功插入则返回 1,如果 Set 中已有这个 Value 则失败并返回 0

SREM key member [member ...]

删除 Set 中对应的 Value,删除成功则返回 1,若不存在则返回 0

SPOP key [count]

随机删除 Set 中的 count 个元素,并返回其值

SDIFF key [key ...]

返回 Set1 中在 Set2 里不存在的元素

SDIFFSTORE destination key [key ...]

例:SDIFFSTORE set3 set1 set2

将 Set1 中不在 Set2 中的元素保存在 Set3 中

SINTER key [key ...]

返回 Set1 和 Set2 中的共有元素

SINTERSTORE destination key [key ...]

将 Set1 和 Set2 中的共有元素保存到 Set3 中

SUNION key [key ...]

返回 Set1 和 Set2 的并集

SUNIONSTORE destination key [key ...]

将 Set1 和 Set2 的并集保存到 Set3 中

SMOVE source destination member

将 Set1 的 Value 移动到 Set2 中,若 Value 存在于 Set1,那么无论 Set2 是否已存在这个 Value,都返回 1(成功),若 Value 不存在于 Set1,则返回 0 (失败)

SCARD key

返回 Set 中的 Value 的个数

SISMEMBER key member

判断 member 是否存在于 Set 中,存在则返回 1,不存在则返回 0

SRANDMEMBER key [count]

随机返回 Set 中的 count 个元素

 

6.3.2 Set 使用案例

package cn.gldwolf.jedis;import redis.clients.jedis.Jedis;import java.util.Set;/*** @author: Gldwolf* @email: ZengqiangZhao@sina.com* @date: 2019/6/17 16:52*/public class SetTest {public static void main(String[] args) {Jedis jedis = new Jedis("hdp01", 6379);jedis.sadd("Hadoop", "HDFS", "MapReduce", "YARN", "Zookeeper");jedis.sadd("Spark", "Flume", "Kafka", "Redis", "Zookeeper");// 判断一个元素是否属于指定的集合System.out.println("----------- 判断一个集合中是否存在某个值——sismember ----------");Boolean isExist = jedis.sismember("Hadoop", "HDFS");System.out.println("Are there HDFS in the Hadoop: " + isExist);// 求两个集合的差集System.out.println("----------- 两个集合的差集——sdiff ----------");Set<String> diff = jedis.sdiff("Hadoop","Spark");for (String di : diff){System.out.println(di);}// 求两个集合的并集System.out.println("----------- 两个集合的并集——sunion ----------");Set<String> union = jedis.sunion("Hadoop","Spark");for(String un : union){System.out.println(un);}// 求两个集合的交集System.out.println("----------- 两个集合的交集——sinter ----------");Set<String> intersect = jedis.sinter("Hadoop","Spark");for(String inter : intersect){System.out.println(inter);}}
}

 

6.4 ZSet -- 有序集合

6.4.1 ZSet 使用概述

ZSet 和 Set 一样也是 String 类型元素的集合,且不允许存在重复的元素。

不同的是每个元素都会关联一个 double 类型的分数(score),Redis 正是通过分数来为集合中的成员进行从小到大的排序。

ZSet 的成员是唯一的,但分数(score)却可以重复。

命令

介绍

ZADD key [NX|XX] [CH] [INCR] score member [score member ...]

如果 Zset 中不存在这个元素,则向 ZSet 中添加一个或多个元素,同时返回 1。或者对一个已经存在的元素更新其 score 同时返回 0

ZRANGE key start stop [WITHSCORES]

根据 index 返回给定范围内的元素,WITHSCORES 为可选,表示是否同时显示元素对应的 score

ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]

根据 score 返回给定范围内的元素,WITHSCORES 为可选,表示是否同时返回元素对应的 score

ZREM key member [member ...]

删除 ZSet 中的一个或多个元素

ZINCRBY key increment member

如果 ZSet 中存在这个元素,则对这个元素的 score 作 +increment 的操作,否则添加这个元素,并设置其 score 为 increment

ZRANK key member

返回这个元素在 ZSet 中根据 score 的排名,排名按 score 从小到大

ZREVRANK key member

返回这个元素在 ZSet 中根据 score 的排名,排名按 score 从大到小

ZCOUNT key min max

根据给定的 score 范围返回元素的个数

ZCARD key

返回 ZSet 中的元素的个数

ZSCORE key member

获取元素对应的 score

ZREMRANGEBYRANK key start stop

删除给定的索引范围内的所有元素(索引按 score 从小到大排序生成)

ZREMRANGEBYSCORE key min max

删除给定的 score 范围内的所有元素

 

6.4.2 ZSet 使用案例

1. 案例需求:KPL 中英雄使用率排行榜

    1)在 Redis 中需要一个榜单所对应的 sorted set 数据

    2)玩家每选择一个英雄打一场游戏,就对 sorted set 数据对应的英雄分数 +1

    3)在查看榜单时,就调用 ZRANGE 来看榜单中的排序结果

 

2. 代码实现

模拟游戏玩家的选择

package cn.gldwolf.jedis;import redis.clients.jedis.Jedis;import java.util.Random;/*** @author: Gldwolf* @email: ZengqiangZhao@sina.com* @date: 2019/6/17 21:29*/public class KPLPlayer {public static void main(String[] args) throws InterruptedException {Jedis jedis = new Jedis("hdp01", 6379);Random random = new Random();String[] heros = {"李白", "孙悟空", "韩信", "赵云", "后羿", "鲁班", "妲己"};while (true) {int index = random.nextInt(heros.length);// 选择一个英雄String hero = heros[index];// 开始玩游戏Thread.sleep(100);// 给集合中的该英雄的出场次数 +1// 第一次添加的时候,集合不存在,此时使用 zincrby 方法会创建jedis.zincrby("Heros_rank", 1, hero);System.out.println("*** " + hero + " 出场了!!!***");}}
}

查看排行榜

package cn.gldwolf.jedis;import redis.clients.jedis.Jedis;
import redis.clients.jedis.Tuple;import java.util.Set;/*** @author: Gldwolf* @email: ZengqiangZhao@sina.com* @date: 2019/6/17 21:56*/public class KPLViewer {public static void main(String[] args) throws InterruptedException {Jedis jedis = new Jedis("hdp01", 6379);for (int i = 0; ; i++){// 每隔两秒查看一次榜单Thread.sleep(2000);System.out.println("===== 第 " + i + " 次查看榜单 =====");// 查看榜单的Set<Tuple> herosRankWithScore = jedis.zrevrangeWithScores("Heros_rank", 0, 5);for (Tuple hero : herosRankWithScore) {System.out.println(hero.getElement() + ": " + hero.getScore());}}}
}

 

6.5 Hash -- 哈希表

6.5.1 Hash 使用概述

Redis Hash 是一个键值对集合。

Redis Hash 类型可以看成是具有 String Key 和 String Value 的 Map 容器。

Redis Hash 是一个 String 类型的 field 和 value 的映射表,Hash 特别用于存储对象.

命令

介绍

HSET key field value

给 object 对象添加属性和值

HSETNX key field value

类似 HSET,如果 field 已存在,则返回 0 且修改不生效

HMSET key field value [field value ...]

同时设置多个属性及其值

HGET key field

获取对象的属性的值

HMGET key field [field ...]

获取对象的多个属性的值

HINCRBY key field increment

给对象的属性的值增加 increment

HEXISTS key field

判断对象指定的 field 是否存在,存在则返回 1,不存在则返回 0

HLEN key

返回对象 fields 的数量

HDEL key field [field ...]

删除对象的一个或多个 field

HKEYS key

返回对象所有的 field

HVALS key

返回对象所有的 field 对应的 value

HGETALL key

返回对象所有的 field 和 value

 

6.5.2 Hash 案例

1. 案例需求:实现购物车

需求分析:1)加入购物车 2)查询购物车 3)修改购物车 4)清空购物车

2. 代码实现

package cn.gldwolf.jedis;import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.Jedis;import java.util.Map;
import java.util.Set;/*** @author: Gldwolf* @email: ZengqiangZhao@sina.com* @date: 2019/6/18 10:12*/public class CartServiceByRedis {private Jedis jedis = null;@Beforepublic void init() {jedis = new Jedis("hdp01", 6379);}/*** 添加商品到购物车*/@Testpublic void addItemsToCart() {jedis.hset("ZZQ", "AirDots", "3");jedis.hset("Gldwolf", "AirPods", "10");jedis.hset("JJ", "Honor 20Pro", "1");}/*** 查询购物车的商品信息*/@Testpublic void getCartInfo() {Map<String, String> goodsInfo = jedis.hgetAll("JJ");Set<Map.Entry<String, String>> entrySet = goodsInfo.entrySet();for (Map.Entry<String, String> entry : entrySet) {System.out.println("JJ: " + entry.getKey() + " --> " + entry.getValue());}}/*** 更改购物车信息*/@Testpublic void modifyCart() {// 把 Gldwolf 选购的 AirPods 的数量增加 5jedis.hincrBy("Gldwolf", "AirPods", 5);}/*** 从购物车中删除商品*/@Testpublic void deleteGoods() {jedis.hdel("ZZQ", "AirDots");}@Afterpublic void closeRedis() {jedis.close();}
}

6.6 键值相关的命令

命令

介绍

返回值

KEYS pattern

查询满足 pattern 的键

返回满足条件的 Key

EXISTS key [key ...]

判断一个 Key 是否存在

存在返回 1,不存在返回 0

DEL key [key ...]

删除一个或多个 Key

返回删除的 Key 的数量

EXPIRE key seconds

设置一个 Key 的到期时间,以秒为单位

设置成功返回 1,Key 不存在或不能被设置返回 0

EXPIREAT key timestamp

设置一个 Key 在指定时间戳之时到期

设置成功返回 1,Key 不存在或不能被设置返回 0

TTL key

查询 Key 的有效时长

返回时长,如果该键不存在或没有超时设置,则返回 -1

MOVE key db

将当前数据库中的 Key 移动到其他数据库中

成功返回 1,否则返回 0

PERSIST key

移除给定 Key 的到期时间

成功返回 1,如果该 Key 不存在或没有设置过期时间则返回 0

RANDOMKEY -

随机获取一个 Key

返回一个随机获取的 Key

RENAME key newkey

重命名 Key,如果 newKey 已经存在则覆盖

如果 Key 不存在则返回错误信息,否则返回 OK

RENAMENX key newkey

如果 newKey 存在,则失败且返回 0,否则 重命名 Key

成功返回 1,否则返回 0

TYPE key

查询 Key 的类型

返回 Key 的类型

 

6.7 服务器相关命令

命令

介绍

SELECT index

选择数据库(Redis 数据库编号为 0-15)

QUIT -

退出

ECHO message

打印 message

DBSIZE -

返回当前数据库中 Key 的数量

INFO [section]

获取服务器的信息和统计

FLUSHDB -

删除当前选择数据库中的所有 Key

FLUSHALL -

删除所有数据库中的所有的 Key(跑路)

 

6.8 Redis 事务

可以一次执行多个命令,本质是一组命令的集合。一个事务中的所有命令都会序列化,按照顺序串行化执行而不会被其它命令插入,不允许加塞。

 

命令:

discard: 取消事务放弃执行事务块内的所有命令

exec: 执行事务块内的命令

multi: 标记一个事务块的开始

unwatch: 取消 watch 对所有 Key 的监视

watch: 监视一个或多个 Key,如果在事务执行之前被其他命令所改动,那么事务被打断

 

不保证原子性:Redis 同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚

Note: 1)如果中间一条命令语法写错了,则全部撤销 2)运行时报出异常,只撤销异常的语句

 

7. Redis 的高可用

在 Web 服务器中,高可用是指服务器可以正常访问的时间,衡量的标准是在多长时间内可以提供正常服务(99.9%, 99.99% 等)。但是在 Redis 语境中,高可用的含义不仅仅是能提供正常的服务(如主从分离,快速容灾技术),还要保证数据的安全性以及数据容量的可扩展性。

在 Redis 中,实现高可用的技术主要包括:持久化、复制、哨兵和集群。解释说明如下:

1. 持久化

持久化是最简单的高可用方法(有时甚至不被归为高可用的手段),主要作用是数据备份,即将数据存储到硬盘,保证数据不会因进程的退出而丢失。

2. 主从复制

复制是高可用 Redis 的基础,哨兵和集群都是在复制的基础上实现高可用的。复制主要实现了数据的多机备份,以及对于读操作的负载均衡和简单的故障恢复。缺陷:故障恢复无法自动化;写操作无法负载均衡;存储能力受到单机的限制。

3. 哨兵

哨兵在复制的基础上,哨兵实现了自动化的故障恢复。缺陷:写操作无法负载;存储能力受到单机的限制。

4. 集群

通过集群,Redis 解决了写操作无法负载均衡,以及存储能力受到单机限制的问题,实现了较为完善的高可用方案。

 

 

8. Redis 的持久化

持久化功能:Redis 是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将 Redis 中的数据以某种形式(数据或命令)从内存中持久化到磁盘。当下次重启 Redis 时,利用持久化的文件实现数据恢复。除此之外,为了进行灾难备份,可以将持久化文件拷贝到一个远程位置。

Redis 的持久化分为:

RDB 持久化

AOP 持久化

RDB 持久化原理:将 Redis 在内存中的数据库记录定时 dump 到磁盘上的 RDB 持久化

AOF 持久化原理:将 Redis 的操作日志依次以追加的方式写入磁盘文件(类似于 MySQL 的 binlog)

由于 AOF 持久化的实时性更好,即当进程意外退出时丢失的数据更少,因此 AOF 是目前主流的持久化方式,不过 RDB 持久化仍然有其用武之地。

 

8.1 RDB 持久化

RDB 持久化是批在指定时间间隔内,将内存中的数据集快照写入磁盘(因此也称作快照持久化),实际操作过程是 fork 一个子进程,先将数据集写入临时文件,保存的文件后缀是 .rdb,写入成功后,再替换之前的文件,用二进制压缩存储。当 Redis 重新启动时,可以读取快照文件恢复数据。

有两个触发条件:

1. 手动方式

save 命令和 bgsave 命令都可以生成 RDB 文件。

save 命令会阻塞 Redis 服务器进程,直到 RDB 文件创建完毕为止,在 Redis 服务器阻塞期间,服务器不能处理任何命令请求。

而 bgsave 命令会创建一个子进程,由子进程来负责创建 RDB 文件,父进程(即 Redis 主进程)则继续响应处理请求。

bgsave 命令执行过程中,只有 fork 子进程时会阻塞服务器,而对于 save 命令,整个过程都会阻塞服务器,因此 save 已基本废弃,线上环境要杜绝 save 的使用。此外,在自动触发 RDB 持久化时,Redis 也会选择 bgsave 而不是 save 来进行持久化。

2. 自动触发

save m n

自动触发最常见的情况是在配置文件中通过 save m n,指定当 m 秒内发生 n 次变化时会触发 bgsave。

 

save 900 1 --> 在 900 秒内如果超过 1 个 Key 被修改,就发起快照保存

save 300 10 --> 在 300 秒内如果超过 10 个 Key 被修改,就发起快照保存

save 60 10000 --> 在 60 秒内如果超过 10000 个 Key 被修改,就发起快照保存

 

8.2 AOF 持久化

AOF 比快照方式有更好的持久化性,是由于在使用 AOF 持久化方式时,Redis 会将每一个收到的写命令都通过 write 函数追加到文件中(默认是 appendonly.aof)。当 Redis 重启时会通过重新执行文件中保存的命令来在内存中重建整个数据库的内容。

当然,由于 os 会在内核中缓存 write 做的修改,所以可能不是立即写到磁盘上,这样 AOF 方式的持久化也还是有可能会丢失部分修改。不过我们可以通过配置文件告诉 Redis 我们想要通过 fsync 函数强制 os 写入到磁盘的时机。有如下三种方式(默认是每秒 fsync 一次):

appendonly yes # 启用 AOF 持久化方式 # appendfsync always # 每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用 appendfsync everysec # 每秒种强制写入磁盘一次,在性能和持久化方面做了很好的折衷,推荐 # appendfsync no # 完全依赖 os, 性能最好,但是持久化没有保证

 

8.3 总结

相同数据集的数据 AOF 文件要远大于 RDB 文件,恢复速度慢于 RDB

AOF 运行效率要慢于 RDB,每秒同步策略效率较好,不同步效率和 RDB 相同

Redis 还能对 AOF 文件进行后台重写,使得 AOF 文件的体积不至于过大

总结:

RDB 持久化方式能够在指定的时间间隔内对数据进行快照存储

AOF 持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF 命令以 Redis 协议追加保存每次写的操作到文件末尾

 

 

 

 

 

 

 

 

 

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/557393.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Flink官网自学笔记

1. What is Apache Flink? Apache Flink 是一款用来进行分布式流数据和批数据处理的开源平台。Apache Flink 是一个对有界数据流和无界数据流进行有状态计算的框架和分布式处理引擎。Flink 被设计用于在所有常见的集群环境中运行&#xff0c;以内存中的速度和任意规模进行计算…

HBase 原理

1. HBase 底层原理 1.1 系统架构 1.1.1 Client 职责 1. HBase 有两张特殊的表&#xff1a; .META.: 记录了用户所有表拆分出来的 Region 映射信息&#xff0c;.META. 可以有多个 Region -ROOT-(新版中已去掉这一层): 记录了 .META. 表的 Region 信息&#xff0c;-ROOT- 只有…

用IDEA debug按键功能

用IDEA debug按键功能 一、断点 断点键&#xff0c;是用户在所选行代码处标记的功能点&#xff0c;表示在debug时代码执行到此处暂停。 注&#xff1a;断点可设置多个 二、启动debug 在设置好断点后单击此功能键&#xff0c;启动debug功能。 三、中止任务 点击该功能键&a…

shiro原理及其运行流程介绍

什么是shiro shiro是apache的一个开源框架&#xff0c;是一个权限管理的框架&#xff0c;实现 用户认证、用户授权。 spring中有spring security (原名Acegi)&#xff0c;是一个权限框架&#xff0c;它和spring依赖过于紧密&#xff0c;没有shiro使用简单。 shiro不依赖于sp…

shiro中文api_Shiro

1 shiro Apache shiro 是一个 Java 安全框架。 功能&#xff1a;认证、授权、加密和会话管理功能 应用环境&#xff1a;JavaEE、JavaSE Subject 可看做成一个用户 SecurityManager 框架的核心API Reaim 域对象&#xff0c;用于取数据库中的数据&#xff0c;进行权限比对。…

JVM 学习二:类加载器子系统

1 类加载器子系统的作用 类加载器子系统负责从文件系统或者网络中加载 Class 文件&#xff0c;Class 文件在文件开关有特定的文件标识ClassLoader 只负责 Class 文件的加载&#xff0c;至于它是否可以运行&#xff0c;则由 Execution Engine&#xff08;执行引擎&#xff09;决…

JVM 学习三:类加载器

类加载器 1 类加载器的分类 JVM 支持两种类型的类加载器&#xff1a;引导类加载器&#xff08;Bootstrap ClassLoader&#xff09;和自定义类加载器&#xff08;User-Defined ClassLoader&#xff09; 从概念上来讲&#xff0c;自定义类加载器一般指的是程序中由开发人员自定…

JVM 学习四:类加载之双亲委派机制与沙箱安全机制

1 双亲委派机制 Java 虚拟机对 Class 文件的加载采用的是按需加载的方式&#xff0c;也就是说&#xff1a;当需要使用该类时才会将它的 Class 文件加载到内存生成 Class 对象&#xff0c;而且加载某个类的 Class 文件时&#xff0c;Java 虚拟机采用的是双亲委派模式&#xff0c…

Java8新特性:Stream介绍和总结

Java8新特性&#xff1a;Stream介绍和总结 什么是Stream 流&#xff08;Stream&#xff09;是数据渠道&#xff0c;用于操作数据源&#xff08;集合、数组等&#xff09;所生成的元素序列。 集合讲的是数据&#xff0c;流讲的是计算 注意&#xff1a; Stream自己不会存储元素…

URL传Base64 造成报错 Illegal base64 character 20

报错如下&#xff1a; errorInternal Server Error, messageIllegal base64 character 20, tracejava.lang.IllegalArgumentException: Illegal base64 character 20 at java.util.Base64Decoder.decode0(Base64.java:714)atjava.util.Base64Decoder.decode0(Base64.java:714) …

Linux 中使用 sort 指令分组排序详解

Linux 中使用 sort 指令分组排序详解 sort 中进行分组排序主要用到的选项为 -k&#xff0c;此文&#xff0c;我们着重于该选项的使用方式&#xff0c;用到的其它选项不做解释&#xff0c;有兴趣的同学可以查看帮助文档 1. 数据准备 现有数据如下&#xff0c;文件名 sort_so…

Shiro-单点登录原理

单点登录原理 一、单系统登录机制 1、http无状态协议 web应用采用browser/server架构&#xff0c;http作为通信协议。http是无状态协议&#xff0c;浏览器的每一次请求&#xff0c;服务器会独立处理&#xff0c;不与之前或之后的请求产生关联&#xff0c;这个过程用下图说明…

@Autowired作用在普通方法上

Autowired作用在普通方法上 Autowired作用在普通方法上&#xff0c;会在注入的时候调用一次该方法&#xff0c;如果方法中有实体参数&#xff0c;会对方法里面的参数进行装配&#xff0c;并调用一次该方法。这个可以用来在自动注入的时候做一些初始化操作。

@Autowired注解作用在方法上

Autowired注解作用在方法上 Autowired注解作用在方法上 &#xff08;1&#xff09;该方法如果有参数&#xff0c;会使用autowired的方式在容器中查找是否有该参数 &#xff08;2&#xff09;会执行该方法

Spring定时任务

Spring定时任务(一)&#xff1a;SpringTask使用 背景&#xff1a;在日常开发中&#xff0c;经常会用到任务调度这类程序。实现方法常用的有&#xff1a;A. 通过java.util.Timer、TimerTask实现。 B.通过Spring自带的SpringTask。 C. 通过Spring结合Quartz实现。本文我们将讲述…

Spring的@Scheduled注解实现定时任务

Spring的Scheduled注解实现定时任务 【简介篇】 项目经常会用到定时任务&#xff0c;实现定时任务的方式有很多种。在Spring框架中&#xff0c;实现定时任务很简单&#xff0c;常用的实现方式是使用注解Scheduled。 Scheduled 常用来实现简单的定时任务。例如凌晨1点跑批&am…

深入学习二叉树(一) 二叉树基础

深入学习二叉树(一) 二叉树基础 前言 树是数据结构中的重中之重&#xff0c;尤其以各类二叉树为学习的难点。一直以来&#xff0c;对于树的掌握都是模棱两可的状态&#xff0c;现在希望通过写一个关于二叉树的专题系列。在学习与总结的同时更加深入的了解掌握二叉树。本系列文…

深入学习二叉树(二) 线索二叉树

深入学习二叉树(二) 线索二叉树 1 前言 在上一篇简单二叉树的学习中&#xff0c;初步介绍了二叉树的一些基础知识&#xff0c;本篇文章将重点介绍二叉树的一种变形——线索二叉树。 2 线索二叉树 2.1 产生背景 现有一棵结点数目为n的二叉树&#xff0c;采用二叉链表的形式…

深入学习二叉树(三) 霍夫曼树

深入学习二叉树(三) 霍夫曼树 1 前言 霍夫曼树是二叉树的一种特殊形式&#xff0c;又称为最优二叉树&#xff0c;其主要作用在于数据压缩和编码长度的优化。 2 重要概念 2.1 路径和路径长度 在一棵树中&#xff0c;从一个结点往下可以达到的孩子或孙子结点之间的通路&…

深入学习二叉树(四) 二叉排序树

深入学习二叉树(四) 二叉排序树 1 前言 数据结构中&#xff0c;线性表分为无序线性表和有序线性表。 无序线性表的数据是杂乱无序的&#xff0c;所以在插入和删除时&#xff0c;没有什么必须遵守的规则&#xff0c;可以插入在数据尾部或者删除在数据尾部。但是在查找的时候&a…