📖 本文目录
本篇文章简介
本篇文章是参阅 黑马程序员Redis入门到实战教程,全面透析redis底层原理 整理的二刷笔记 —— 备战 2022秋招 —— 继续加油努力!!!
📖 Redis 实战 —— 基础篇 📑 Redis 常见命令 🔖 Redis 数据结构介绍
官方命令地址:Commands | Redis
可以通过下列命令快速查看使用说明
1 2 3 4 5 6 7 8 9 10 11 12 127.0 .0.1 :6379 > helpredis-cli 6.2 .6 To get help about Redis commands type : "help @<group>" to get a list of commands in <group > "help <command>" for help on <command> "help <tab>" to get a list of possible help topics "quit" to exit To set redis-cli preferences: ":set hints" enable online hints ":set nohints" disable online hints Set your preferences in ~/.redisclirc
⭐ 通用命令 1️⃣ KEYS : 查看符合模板的所有 key——不建议在生产环境设备上使用
可以通过 help [command]
可以查看一个命令的具体用法,例如:
1 2 3 4 5 6 127.0 .0.1 :6379 > help KEYS KEYS pattern summary: Find all keys matching the given pattern since: 1.0 .0 group : generic
Supported glob-style patterns:
h?llo
matches hello
, hallo
and hxllo
h*llo
matches hllo
and heeeello
h[ae]llo
matches hello
and hallo,
but not hillo
h[^e]llo
matches hallo
, hbllo
, … but not hello
h[a-b]llo
matches hallo
and hbllo
Examples
1 2 3 4 5 6 7 8 9 10 11 12 redis:6379 > MSET firstname Jack lastname Stuntman age 35 "OK" redis:6379 > KEYS *name* 1 ) "firstname" 2 ) "lastname" redis:6379 > KEYS a?? 1 ) "age" redis:6379 > KEYS * 1 ) "firstname" 2 ) "age" 3 ) "lastname" redis:6379 >
2️⃣ DEL :删除指定的KEY —— 也可以删除多个 KEY
help
1 2 3 4 5 6 127.0 .0.1 :6379 > help DEL DEL key [key ... ] summary: Delete a key since: 1.0 .0 group : generic
📚 Removes the specified keys. A key is ignored if it does not exist.
Example
1 2 3 4 5 6 7 redis:6379 > SET key1 "Hello" "OK" redis:6379 > SET key2 "World" "OK" redis:6379 > DEL key1 key2 key3 (integer) 2 redis:6379 >
3️⃣ EXISTS:判断对应的 KEY 是否存在
help
1 2 3 4 5 6 127.0 .0.1 :6379 > help EXISTS EXISTS key [key ... ] summary: Determine if a key exists since: 1.0 .0 group : generic
📚 The user should be aware that if the same existing key is mentioned in the arguments multiple times, it will be counted multiple times. So if somekey
exists, EXISTS somekey somekey
will return 2.
Example
1 2 3 4 5 6 7 8 9 10 11 redis:6379 > SET key1 "Hello" "OK" redis:6379 > EXISTS key1 (integer) 1 redis:6379 > EXISTS nosuchkey (integer) 0 redis:6379 > SET key2 "World" "OK" redis:6379 > EXISTS key1 key2 nosuchkey (integer) 2 redis:6379 >
4️⃣ EXPIRE:给一个 key 设置有效期,有效期到期时该 KEY 会被自动删除
EXPIRE KEY seconds [NX|XX|GT|LT]
help
1 2 3 4 5 6 127.0 .0.1 :6379 > help EXPIRE EXPIRE key seconds summary: Set a key's time to live in seconds since: 1.0.0 group: generic
The EXPIRE
command supports a set of options:
NX
– Set expiry only when the key has no expiry
XX
– Set expiry only when the key has an existing expiry
GT
– Set expiry only when the new expiry is greater than current one
LT
– Set expiry only when the new expiry is less than current one
Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 redis:6379 > SET mykey "Hello" "OK" redis:6379 > EXPIRE mykey 10 (integer) 1 redis:6379 > TTL mykey (integer) 10 redis:6379 > SET mykey "Hello World" "OK" redis:6379 > TTL mykey (integer) -1 redis:6379 > EXPIRE mykey 10 XX (integer) 0 redis:6379 > TTL mykey (integer) -1 redis:6379 > EXPIRE mykey 10 NX (integer) 1 redis:6379 > TTL mykey (integer) 10 redis:6379 >
5️⃣ TTL:查看一个 KEY 的剩余有效期
help
1 2 3 4 5 6 127.0 .0.1 :6379 > help TTL TTL key summary: Get the time to live for a key since: 1.0 .0 group : generic
📚 Starting with Redis 2.8 the return value in case of error changed:
The command returns -2
if the key does not exist.
The command returns -1
if the key exists but has no associated expire.
Example
1 2 3 4 5 6 redis:6379 > SET mykey "Hello" "OK" redis:6379 > EXPIRE mykey 10 (integer) 1 redis:6379 > TTL mykey (integer) 10
⭐ String
常用命令
1️⃣ SET:添加或者修改已经存在的一个String类型的键值对
2️⃣ GET:根据 KEY 获取 String 类型的 value
3️⃣ MSET:批量添加多个 String 类型的键值对
4️⃣ MGET:根据多个 KEY 获取多个 String 类型的 value
5️⃣ INCR:让一个整型的 KEY 自增 1
6️⃣ INCRBY:让一个整型的 KEY 自增并指定步长
7️⃣ INCRBYFLOAT:让一个浮点类型的数字自增并指定步长
8️⃣ SETNX:添加一个 String 类型的键值对,前提是这个 KEY 不存在,否则不执行
9️⃣ SETEX:添加一个 String类型的键值对,并且指定有效期
非常用命令
1️⃣ SUBSTR:根据 KEY 获取 String 指定范围的 subString
2️⃣ STRLEN:根据 KEY 获取 String 字符串的长度
3️⃣ GETRANGE:用于获取存储在指定 key 中的子字符串。
4️⃣ GETSET:返回之前的值,在设置新的值。
5️⃣ GETDEL:返回之前的值,然后删除对应的 KEY - VALUE
❓ Redis 没有类似MySQL 中的 Table 的概念,我们该如何区分不同类型的key呢?
举个🌰
例如,需要存储用户、商品信息到 redis,有一个用户 id 是1 ,有一个商品 id 也是 1
⭐ Hash
常用命令
1️⃣ HSET:添加 或者 修改 hash 类型 key 的 field 的值。
HSET key field value [field value ...]
2️⃣ HGET :获取一个 hash 类型 key 的 field 的值。
3️⃣ HMSET:批量添加多个 hash 类型 key 的 field 的值
4️⃣ HMGET:批量获取多个 hash 类型的 key 中的所有的 field 和 value
5️⃣ HGETALL:获取一个 hash 类型的 key 中的所有的 field和value
6️⃣ HKEYS:获取一个 hash 类型的 key 中的所有的 field
7️⃣ HVALS:获取一个 hash 类型的 key 中的所有的 value
8️⃣ HINCRBY:让一个 hash 类型 key 的字段值 自增并指定步长
9️⃣ HSETNX:添加一个 hash 类型的 key 的 field 值,前提是这个 field 不存在,否则不执行。
非常用命令
1️⃣ HDEL :删除指定 hash 类型的key 中的一个或者多个 field
2️⃣ HLEN:获取指定 hash 类型的 key 中的 field 个数
⭐ List
常见命令
1️⃣ LPUSH key element … :向列表的左侧插入一个或者多个元素。
2️⃣ LPOP key:移除并返回列表左侧的第一个元素,没有则返回null。
3️⃣ RPUSH key element … :向列表右侧插入一个或多个元素。
4️⃣ RPOP key :移除并返回列表右侧的第一个元素
5️⃣ LRANGE key start end:返回一段角标范围内的所有元素。
6️⃣ BLPOP 和 BRPOP:与LPOP 和 RPOP 类似,只不过在没有元素时等待指定时间,而不是直接返回 null。
非常用命令
1️⃣ RPOPLPUSH:移除源List中的最后一个元素,将其插入到目标List当中。
help
1 2 3 4 5 6 127.0 .0.1 :6379 > help RPOPLPUSH RPOPLPUSH source destination summary: Remove the last element in a list, prepend it to another list and return it since: 1.2 .0 group : list
Example
1 2 3 4 5 6 7 8 9 10 11 12 13 redis:6379 > RPUSH mylist "one" (integer) 1 redis:6379 > RPUSH mylist "two" (integer) 2 redis:6379 > RPUSH mylist "three" (integer) 3 redis:6379 > RPOPLPUSH mylist myotherlist "three" redis:6379 > LRANGE mylist 0 -1 1 ) "one" 2 ) "two" redis:6379 > LRANGE myotherlist 0 -1 1 ) "three"
课后问题
❓ 如何利用 List 结构模拟一个栈
❓ 如何利用 List 结构模拟一个队列
❓ 如何利用 List 结构模拟一个阻塞队列
⭐ Set
常用命令
1️⃣ SADD key member … : 向 set 中添加一个或者多个元素。
2️⃣ SREM key member …:移除 set 中的指定元素。
3️⃣ SCARD key : 返回 set 中元素的个数。
4️⃣ SISMEMBER key member :判断一个元素是否存在于 set 中
5️⃣ SMEMBERS : 显示 set 集合中的所有元素。
常用操作命令
1️⃣ SDIFF key [key] : key 所对应集合元素之差
2️⃣ SDIFFSTORE destination key [key] :和上述命令作用是一样的,不过会创建一个新的键为key的 set 用于存储结果集,如果这个结果集存在则会被覆盖。
3️⃣ SINTER key [key]:用于筛选 交集 集合操作
4️⃣ SINTERSTORE destination key [key] :和上述命令作用是一样的,不过会创建一个新的键为key的 set 用于存储结果集,如果这个结果集存在则会被覆盖。
5️⃣ SSCAN key cursor [MATCH pattern] [COUNT count]:迭代优化返回 set 数据集
❓ SET 命令的练习
将下列数据用 Redis 的 set 集合来存储
1 2 3 4 5 6 7 8 9 10 11 12 127.0 .0.1 :6379 > SADD user:zhangsan:friends lisi wangwu zhaoliu(integer) 3 127.0 .0.1 :6379 > SMEMBERS user:zhangsan:friends1 ) "zhaoliu" 2 ) "wangwu" 3 ) "lisi" 127.0 .0.1 :6379 > SADD user:lisi:friends wangwu mazi ergou(integer) 3 127.0 .0.1 :6379 > SMEMBERS user:lisi:friends1 ) "mazi" 2 ) "wangwu" 3 ) "ergou"
计算 张三的好友有几人
1 2 127.0 .0.1 :6379 > SINTER user:zhangsan:friends user:lisi:friends1 ) "wangwu"
计算张三和李四有哪些共同好友
1 2 127.0 .0.1 :6379 > SINTER user:zhangsan:friends user:lisi:friends1 ) "wangwu"
查询哪些人是张三的好友 却不是 李四的好友
1 2 3 127.0 .0.1 :6379 > SDIFF user:zhangsan:friends user:lisi:friends1 ) "zhaoliu" 2 ) "lisi"
查询张三和李四的好友总共有哪些人
1 2 3 4 5 6 127.0 .0.1 :6379 > SUNION user:zhangsan:friends user:lisi:friends1 ) "lisi" 2 ) "mazi" 3 ) "ergou" 4 ) "zhaoliu" 5 ) "wangwu"
判断李四是否是张三的好友
1 2 127.0 .0.1 :6379 > SISMEMBER user:zhangsan:friends lisi(integer) 1
判断张三是不是李四的好友
1 2 127.0 .0.1 :6379 > SISMEMBER user:lisi:friends zhangsan(integer) 0
将 李四 从张三的好友列表中移除
1 2 127.0 .0.1 :6379 > SREM user:zhangsan:friends lisi(integer) 1
⭐ SortedSet
常见命令
1️⃣ ZADD key score member:添加一个或多个元素到 sorted set
2️⃣ ZREM key member:删除 sorted set 中的一个指定元素
3️⃣ ZSCORE key member :获取sorted set 中的指定元素的 score 值
4️⃣ ZRANK key member :获取 sorted set 中指定元素的排名。
5️⃣ ZCARD key :获取 sorted set 中的元素个数
6️⃣ ZCOUNT key min max:统计 score 值在给定范围内的元素个数。
7️⃣ ZINCRBY key increment member:让 sorted set 中的指定元素自增,步长为指定的 increment 值。
8️⃣ ZRANGE key min max:按照 score 排序后,获取指定排名范围内的元素
9️⃣ ZRANGEBYSCORE key min max:按照 score排序后,获取指定 score 范围内的元素。
🔢 ZDIFF 、ZINTER、ZUNION:求差集、交集、并集。
📚 注意点:
所有排名默认都是升序,如果要降序则在命令的Z 后面添加REV (reverse)即可
❓ SortedSet 命令的练习
将班级的下列学生得分存入 Redis 的 SortedSet 中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 127.0 .0.1 :6379 > ZADD student 85 Jack 82 Rose 95 Tom 78 Jerry 92 Amy 76 Miles(integer) 6 127.0 .0.1 :6379 > ZSCAN student 0 1 ) "0" 2 ) 1 ) "Miles" 2 ) "76" 3 ) "Jerry" 4 ) "78" 5 ) "Rose" 6 ) "82" 7 ) "Jack" 8 ) "85" 9 ) "Amy" 10 ) "92" 11 ) "Tom" 12 ) "95"
删除 Tom 同学
1 2 127.0 .0.1 :6379 > zrem student Tom(integer) 1
获取 Amy 同学的分数
1 2 127.0 .0.1 :6379 > ZSCORE student Amy"92"
获取 Rose 同学的排名
1 2 127.0 .0.1 :6379 > ZREVRANK student Rose(integer) 2
查询 80 分以下的同学有几个学生
1 2 127.0 .0.1 :6379 > ZCOUNT student 0 80 (integer) 2
给 Amy 同学 加 2 分
1 2 127.0 .0.1 :6379 > ZINCRBY student 2 Amy"94"
查出成绩前3名的同学
1 2 3 4 127.0 .0.1 :6379 > ZREVRANGE student 0 2 1 ) "Amy" 2 ) "Jack" 3 ) "Rose"
查询成绩 80 分以下的所有同学
1 2 3 127.0 .0.1 :6379 > ZRANGEBYSCORE student 0 80 1 ) "Miles" 2 ) "Jerry"
📑 Redis 的 Java 客户端
Clients | Redis
🔖 Jedis
Jedis
以 Redis 命令作为方法名称,学习成本低,简单实用。但是 Jedis 实例是线程不安全的,多线程环境需要基于连接池来使用。
redis/jedis: Redis Java client designed for performance and ease of use. (github.com)
步骤一:引入相关的依赖
1 2 3 4 5 <dependency > <groupId > redis.clients</groupId > <artifactId > jedis</artifactId > <version > 3.7.0</version > </dependency >
步骤二:建立连接进行测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class TestConnection { private final static String REDIS_HOST = "192.168.56.103" ; private final static Integer REDIS_PORT = 6379 ; public static Jedis getJedisConnection () { return new Jedis (REDIS_HOST,REDIS_PORT); } public static boolean closeJedisConnection (Jedis jedisConnection) { if (jedisConnection != null ){ jedisConnection.close(); return true ; } throw new RuntimeException ("JedisConnection 已经关闭 请勿二次关闭!" ); } public static void main (String[] args) { Jedis jedis = new Jedis (REDIS_HOST, REDIS_PORT); jedis.select(0 ); String ping = jedis.ping(); System.out.println(ping); } }
测试String
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Test public void testString () { Jedis jedisConnection = getJedisConnection(); String result = jedisConnection.set("name" , "Alascanfu" ); System.out.println("result => " + result); String name = jedisConnection.get("name" ); System.out.println("name => " + name); System.out.println(jedisConnection.del("name" ) == 0 ? "No" : "Yes" ); closeJedisConnection(jedisConnection); }
测试 hash
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Test public void testHash () { Jedis jedisConnection = getJedisConnection(); jedisConnection.hset("www.alascanfu.com:user" ,"name" ,"Alascanfu" ); jedisConnection.hset("www.alascanfu.com:user" ,"age" ,"21" ); jedisConnection.hset("www.alascanfu.com:user" ,"sex" ,"Man" ); Map<String, String> map = jedisConnection.hgetAll("www.alascanfu.com:user" ); System.out.println(map); System.out.println(jedisConnection.del("www.alascanfu.com:user" ) == 0 ? "No" : "Yes" ); closeJedisConnection(jedisConnection); }
⭐ Jedis 线程池
Jedis 本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此我们推荐大家使用 Jedis 连接池代替 Jedis 的连接方式
JedisConnectionFactory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class JedisConnectionFactory { private static final String REDIS_HOST = "192.168.56.103" ; private static final Integer REDIS_PORT = 6379 ; private static final JedisPool jedisPool; static { JedisPoolConfig jedisPoolConfig = new JedisPoolConfig (); jedisPoolConfig.setMaxTotal(8 ); jedisPoolConfig.setMaxIdle(8 ); jedisPoolConfig.setMinIdle(0 ); jedisPoolConfig.setMaxWaitMillis(200 ); jedisPool = new JedisPool (jedisPoolConfig,REDIS_HOST,REDIS_PORT,1000 ); } private static Jedis getJedis () { return jedisPool.getResource(); } }
🔖 lettuce
Lettuce
Lettuce 是基于 Netty 实现的,支持同步、异步和响应编程方式 ,并且是线程安全 的。支持 Redis 的哨兵模式、集群模式和管道模式。
🔖 Redisson
Redisson 是 一个基于 Redis 实现的分布式、可伸缩的 Java 数据结构集合。包含了诸如 Map 、Queue 、Lock、Semaphore、AtomicLong 等强大功能。
📑 SpringData Redis
🔖 SpringData Redis 快速入门
SpringData Redis 中提供了 RedisTemplate 工具类,其中封装了各种对 Redis 的操作。并将不同类型的操作 API 封装到不同的类型当中
API
返回值类型
说明
redisTemplate.opsForValue()
ValueOperations
操作 String 类型数据
redisTemplate.opsForHash()
HashOperations
操作 Hash 类型数据
redisTemplate.opsForList()
ListOperations
操作 List 类型数据
redisTemplate.opsForSet()
SetOperations
操作 Set 类型数据
redisTemplate.opsForZSet()
ZSetOperations
操作 Sorted Set类型数据
redisTemplate
通用的命令
步骤一:引入相关依赖,SpringBoot已经提供了对 SpringDataRedis 的支持了
1 2 3 4 5 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-redis</artifactId > </dependency >
1 2 3 4 5 <dependency > <groupId > org.apache.commons</groupId > <artifactId > commons-pool2</artifactId > </dependency >
1 2 3 4 5 6 <dependency > <groupId > com.fasterxml.jackson.core</groupId > <artifactId > jackson-databind</artifactId > <version > 2.13.3</version > </dependency >
步骤二:配置文件
pom.xml
1 2 3 4 5 6 7 8 9 10 spring: redis: host: 192.168 .56 .103 port: 6379 lettuce: pool: max-idle: 8 min-idle: 0 max-active: 8 max-wait: 200
步骤三:测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @SpringBootTest class SpringdataRedisApplicationTests { @Autowired private RedisTemplate redisTemplate ; @Test public void testString () { redisTemplate.opsForValue().set("www.alascanfu.com:user" ,"Alascanfu" ); String str = (String) redisTemplate.opsForValue().get("www.alascanfu.com:user" ); System.out.println(str); Boolean delete = redisTemplate.delete("www.alascanfu.com:user" ); System.out.println(delete); } }
🔖 SpringDataRedis 的序列化方式
SpringDataRedis 的序列化方式
添加配置 RedisConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Configuration public class RedisConfig { @Bean public RedisTemplate<String , Object> redisTemplate (RedisConnectionFactory redisConnectionFactory) throws Exception{ RedisTemplate<String, Object> redisTemplate = new RedisTemplate <>(); redisTemplate.setConnectionFactory(redisConnectionFactory); GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer (); redisTemplate.setKeySerializer(RedisSerializer.string()); redisTemplate.setHashKeySerializer(RedisSerializer.string()); redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer); redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer); return redisTemplate; } }
测试
1 2 3 4 5 6 7 8 @Test public void testStringObject () { redisTemplate.opsForValue().set("user:201901094106" ,new User (201901094106L ,"Alascanfu" ,21 )); User user = (User) redisTemplate.opsForValue().get("user:201901094106" ); System.out.println(user); }
📚 此时就不会出现乱码的错误啦
🔖 SpringDataRedis 序列化带来的一些缺点
所以为了解决上述问题的话我们需要弃用自动的序列化以及反序列化,采用统一的String序列化器,要求只能存储String 类型的 key 和 value。当需要存储 Java 对象时,手动完成对象的序列化与反序列化。
小付的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 @Test public void testStringObject () throws JsonProcessingException { User alascanfu = new User (201901094106L , "Alascanfu" , 21 ); String body = objectMapper.writeValueAsString(alascanfu); redisTemplate.opsForValue().set("user:201901094106" ,body); body = (String) redisTemplate.opsForValue().get("user:201901094106" ); User user = objectMapper.readValue(body, User.class); System.out.println(user); }
老师的方法
🔖 封装好的 JsonUtil 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @Slf4j @Component public class JsonUtil { @Autowired private static ObjectMapper objectMapper ; public static String toJson (Object object) { try { objectMapper.writeValueAsString(object); } catch (JsonProcessingException e) { e.printStackTrace(); } return null ; } public static Object FromJsonTo (String body , Class classType) { try { return objectMapper.readValue(body,classType); } catch (JsonProcessingException e) { e.printStackTrace(); } return null ; } }