Redis
- Remote Dictionary Server(远程词典服务器),是一个基于内存的键值型NoSQL(非关系型数据库)
- 官方文档:https://redis.io/commands/
Redis的特征
键值型,支持多种数据结构
单线程,每个命令具备原子性
低延迟,速度快(基于内存、IO多路复用、良好的编码)
支持数据持久化
支持主从集群、分片集群
支持多语言客户端
Redis数据结构
-
基本类型:String、Hash、List、Set、SortedSet
-
特殊类型:GEO、BitMap、HyperLog等
常用命令
查看命令
# 查看group分组下的所有命令
# help @[group]
127.0.0.1:6379> help @string
# 查看command命令
# help [command]
127.0.0.1:6379> help keys
通用命令
# 查询所有符合模板的 key(由于redis是单线程,当数据量比较大时,查询会消耗大量时间,会阻塞其他命令,所以不建议在生产环境上运行)
# keys pattern
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> keys n*
1) "name"
# 删除一个或多个 key,返回删除的条数
# del key [key ...]
127.0.0.1:6379> del k1
(integer) 1
127.0.0.1:6379> del k2 k3
(integer) 2
# 查询一个或多个 key 是否存在,返回存在的条数
# exists key [key ...]
127.0.0.1:6379> exists name age
(integer) 2
# 给一个 key 设置有效期(到期后自动删除该key)
# expire key seconds
127.0.0.1:6379> expire name 5
(integer) 1
# 查询一个 key 剩余有效期(返回值即剩余存活的时间,返回值为 -1 代表永久存活,返回值为 -2 代表已经死亡)
# ttl key
127.0.0.1:6379> ttl name
(integer) 3
127.0.0.1:6379> ttl age
(integer) -1
String类型
-
其 value 是字符串,根据字符串格式的不同,又能分为3类:
string:普通字符串
int:整数类型,可以做自增、自减操作
float:浮点类型,可以做自增、自减操作 -
不管是哪种方式,底层都是用字节数组形式存储,只不过是编码方式不同。字符串类型的最大空间不能超过512M
# 添加或修改对应 key 的值
# set key value [ex seconds] [px milliseconds]
127.0.0.1:6379> set name bob
OK
127.0.0.1:6379> set name clarence
OK
127.0.0.1:6379> set name clarence ex 10
OK
127.0.0.1:6379> set name clarence px 10000
OK
# 根据 key 获取string类型的 value
# get key
127.0.0.1:6379> get name
"clarence"
# 批量添加或修改对应 key 的值
# mset key value [key value ...]
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
OK
# 批量根据 key 获取string类型的 value
# mget key [key ...]
127.0.0.1:6379> mget k1 k2 k3
1) "v1"
2) "v2"
3) "v3"
# 让一个整型的 key 自增 1
# incr key
127.0.0.1:6379> get age
"23"
127.0.0.1:6379> incr age
(integer) 24
127.0.0.1:6379> incr age
(integer) 25
127.0.0.1:6379> get age
"25"
# 让一个整型的 key 自增,并指定增量(increment为负数可实现自减)
# incrby key increment
127.0.0.1:6379> get age
"25"
127.0.0.1:6379> incrby age 2
(integer) 27
127.0.0.1:6379> incrby age 2
(integer) 29
127.0.0.1:6379> incrby age -1
(integer) 28
127.0.0.1:6379> get age
"28"
# 让一个浮点型的 key 自增,并指定增量(increment为负数可实现自减)
# incrbyfloat key increment
127.0.0.1:6379> set score 5.6
OK
127.0.0.1:6379> incrbyfloat score 0.5
"6.1"
127.0.0.1:6379> incrbyfloat score 0.5
"6.6"
127.0.0.1:6379> get score
"6.6"
# 添加一个 string 类型的键值对,前提是这个 key 不存在,否则不执行
# setnx key value
127.0.0.1:6379> setnx name jack
(integer) 0
127.0.0.1:6379> setnx sex male
(integer) 1
# 添加一个 string 类型的键值对,并指定有效期
# setex key seconds value
127.0.0.1:6379> setex name2 10 jack
OK
127.0.0.1:6379> ttl name2
(integer) 6
- String结构存储对象:将对象序列化为json字符串后存储
Hash类型
- Hash类型,也叫散列,其 value 是一个无序字典(类似于 Java 中的 HashMap 结构)
- Hash结构可以将对象中的每个字段独立存储,方便修改对象的单个字段
# 添加或修改对应 hash 类型 key 的 field 的值
# hset key field value
127.0.0.1:6379> hset project:user:3 name jack
(integer) 1
127.0.0.1:6379> hset project:user:3 age 25
(integer) 1
# 获取一个 hash 类型 key 的 field 的值
# hget key field
127.0.0.1:6379> hget project:user:3 name
"jack"
127.0.0.1:6379> hget project:user:3 age
"25"
# 批量添加或修改对应 hash 类型 key 的 field 的值
# hmset key field value [field value ...]
127.0.0.1:6379> hmset project:user:4 name mike age 26
OK
# 批量获取多个 hash 类型 key 的 field 的值
# hmget key field [field ...]
127.0.0.1:6379> hmget project:user:4 name age
1) "mike"
2) "26"
# 获取一个 hash 类型 key 的所有 field 和 value 的值
# hgetall key
127.0.0.1:6379> hgetall project:user:3
1) "name"
2) "jack"
3) "age"
4) "25"
# 获取一个 hash 类型 key 的所有 field 的值
# hkeys key
127.0.0.1:6379> hkeys project:user:3
1) "name"
2) "age"
# 获取一个 hash 类型 key 的所有 value 的值
# hvals key
127.0.0.1:6379> hvals project:user:3
1) "jack"
2) "25"
# 让一个 hash 类型 key 的整型 field 值自增,并指定增量(increment为负数可实现自减)
# hincrby key field increment
127.0.0.1:6379> hincrby project:user:3 age 2
(integer) 27
# 添加一个 hash 类型的 key 的 field 值,前提是这个 field 不存在,否则不执行
# hsetnx key field value
127.0.0.1:6379> hsetnx project:user:3 name zhangsan
(integer) 0
127.0.0.1:6379> hsetnx project:user:3 sex male
(integer) 1
List类型
-
Redis 中的 List 类型与 Java 中的 LinkedList 类似,可以看作是一个双向链表结构的队列
-
特征也与 LinkedList 类似:
有序
元素可重复
插入和删除快
查询速度一般 -
常用来存储有序数据
# 在一个列表前添加一个或多个值(从队列左侧(left)插入)
# lpush key value [value ...]
127.0.0.1:6379> lpush ages 10 20 30
(integer) 3
# 在一个列表后添加一个或多个值(从队列右侧(right)插入)
# rpush key value [value ...]
127.0.0.1:6379> rpush ages 40 50 60
(integer) 6
# 从列表中获取下标范围内的所有元素
# lrange key start stop
127.0.0.1:6379> lrange ages 1 4
1) "20"
2) "10"
3) "40"
4) "50"
# 移除并获取列表中的第一个元素,没有则返回 nil(从队列左侧(left)取值)
# lpop key
127.0.0.1:6379> lpop ages
"30"
127.0.0.1:6379> lpop ages
"20"
# 移除并获取列表中的最后一个元素,没有则返回 nil(从队列右侧(right)取值)
# rpop key
127.0.0.1:6379> rpop ages
"60"
127.0.0.1:6379> rpop ages
"50"
# blpop 和 brpop,与 lpop 和 rpop 类似,区别在于没有元素时会等待指定时间,而不是直接返回 nil(从队列取值,没有元素时阻塞(block)指定时间)
# blpop key timeout
# brpop key timeout
# redis-cli 窗口一
127.0.0.1:6379> blpop ages2 60
1) "ages2"
2) "25"
(16.91s)
# redis-cli 窗口二
127.0.0.1:6379> lpush ages2 15 20 25
(integer) 3
Set类型
- Redis 的 Set 结构与 Java 中的 HashSet 类似,可以看作是一个 value 值为 null 的 HashMap
- 与 HashSet 相似的特征
无序
元素不可重复
查找快
支持交集、并集、差集等功能
# 向 set 中添加(add)一个或多个元素
# sadd key member [member ...]
127.0.0.1:6379> sadd set a b c d
(integer) 4
# 移除(remove) set 中指定的一个或多个元素
# srem key member [member ...]
127.0.0.1:6379> srem set a b
(integer) 2
# 判断给定的值是否是 set 的成员(is member)
# sismember key member
127.0.0.1:6379> sismember set a
(integer) 0
127.0.0.1:6379> sismember set c
(integer) 1
# 获取 set 中的元素个数(card)
# scard key
127.0.0.1:6379> scard set
(integer) 2
# 获取 set 中的所有元素(members)
# smembers key
127.0.0.1:6379> smembers set
1) "d"
2) "c"
# 多个 set 的交集(intersect)
# sinter key [key ...]
# 多个 set 与第一个 set 的差集(difference)
# sdiff key [key ...]
# 多个 set 的并集(union)
# sunion key [key ...]
127.0.0.1:6379> sadd set2 a b c
(integer) 3
127.0.0.1:6379> sadd set3 b c d
(integer) 3
127.0.0.1:6379> sinter set2 set3
1) "c"
2) "b"
127.0.0.1:6379> sdiff set2 set3
1) "a"
127.0.0.1:6379> sdiff set3 set2
1) "d"
127.0.0.1:6379> sunion set2 set3
1) "b"
2) "a"
3) "c"
4) "d"
SortedSet类型
-
Redis 的 SortedSet 是一个可排序的 set 集合,类似 Java 中的 TreeSet,但底层数据结构差别却很大
-
SortedSet 中的每一个元素都带有一个 score 属性,可以基于 score 属性对元素排序,底层的实现是一个跳表(SkipList)加 hash 表
-
SortedSet 具备以下特性:
可排序
元素不重复
查询速度快
# 向 sortedSet 中添加(add)一个或多个元素,如果已经存在则更新其 score 值
# zadd key score member [score member ...]
127.0.0.1:6379> zadd class 90 xiaobai 88 xiaohei 94 xiaohui 84 xiaolan 82 xiaolv 99 xiaohong 60 xiaoqing
(integer) 7
# 移除(remove) sortedSet 中的一个或多个元素
# zrem key member [member ...]
127.0.0.1:6379> zrem class xiaoqing
(integer) 1
# 获取 sortedSet 中指定元素的 score 值
# zscore key member
127.0.0.1:6379> zscore class xiaobai
"90"
# 获取 sortedSet 中指定元素的排名(从0开始计数)
# 正序:zrank key member
# 倒序:zrevrank key member
127.0.0.1:6379> zrank class xiaobai
(integer) 3
127.0.0.1:6379> zrevrank class xiaobai
(integer) 2
# 获取 sortedSet 中的元素个数(card)
# zcard key
127.0.0.1:6379> zcard class
(integer) 6
# 统计 sortedSet 中 score 值在指定范围内的元素个数
# zcount key min max
127.0.0.1:6379> zcount class 90 100
(integer) 3
# 让 sortedSet 中的指定值自增,并指定增量(increment为负数可实现自减)
# zincrby key increment member
127.0.0.1:6379> zincrby class 2 xiaobai
"92"
127.0.0.1:6379> zincrby class -1 xiaobai
"91"
# 按照 score 排序后,获取指定排名范围内的元素
# 正序:zrange key start stop
# 倒序:zrevrange key start stop
127.0.0.1:6379> zrange class 0 2
1) "xiaolv"
2) "xiaolan"
3) "xiaohei"
127.0.0.1:6379> zrevrange class 0 2
1) "xiaohong"
2) "xiaohui"
3) "xiaobai"
# 按照 score 排序后,获取指定 score 范围内的元素
# zrangebyscore key min max
127.0.0.1:6379> zrangebyscore class 90 100
1) "xiaobai"
2) "xiaohui"
3) "xiaohong"
# 所有的排名默认都是升序,且序号从0开始,如果要降序排序只需在命令的 z 后面添加 rev 即可(reverse)
# 例:zrank -> zrevrank
Key的层级结构
- Redis 的 key 允许多个单词直接用 ‘:’ 隔开形成层级结构
# 层级结构
127.0.0.1:6379> set project:user:1 bob
OK
127.0.0.1:6379> set project:user:2 clarence
OK
Java 客户端 - Jedis
- Jedis官网: https://github.com/redis/jedis
- Jedis 是以 Redis 命令作为方法名称,简单实用,但是Jedis实例是线程不安全的,多线程环境下需要基于线程池来使用
Jedis 的基本使用
- 引入依赖
<dependencies>
<!--jedis-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.2.0</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.7.1</version>
<scope>test</scope>
</dependency>
</dependencies>
- 单元测试 Demo
package com.fc;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import redis.clients.jedis.Connection;
import redis.clients.jedis.JedisPooled;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class JedisTest {
// 默认连接 127.0.0.1:6379
// JedisPooled jedis = new JedisPooled();
// 建立连接(有密码的需要设置密码)
// JedisPooled jedis = new JedisPooled("192.168.198.131", 6379, null, "123456");
// 建立连接(无密码)
JedisPooled jedis = new JedisPooled("127.0.0.1", 6379);
// 另一种创建方式
private JedisPooled jedis2;
@BeforeEach
void beforeTest() {
GenericObjectPoolConfig<Connection> poolConfig = new GenericObjectPoolConfig<>();
// 最大连接
poolConfig.setMaxTotal(8);
// 最大空闲连接
poolConfig.setMaxIdle(8);
// 最小空闲连接
poolConfig.setMinIdle(0);
// 最长等待时间
poolConfig.setMaxWait(Duration.ofSeconds(200));
// 可以选择使用第几号库
jedis2 = new JedisPooled(poolConfig, "127.0.0.1", 6379, 1000, null, null, 0);
}
// String 类型
@Test
public void testString () {
String result = jedis.set("name", "clarence");
System.out.println(result);
String name = jedis.get("name");
System.out.println(name);
}
// Hash 类型
@Test
public void testHash () {
jedis.hset("mydemo:user:1", "name", "clarence");
jedis.hset("mydemo:user:1", "age", "25");
Map<String, String> map = jedis.hgetAll("mydemo:user:1");
System.out.println(map);
}
// List 类型
@Test
public void testList () {
jedis.lpush("names", "xiaobai", "xiaohei", "xiaolan");
List<String> names = jedis.lrange("names", 0, 3);
System.out.println(names);
}
// Set 类型
@Test
public void testSet () {
jedis.sadd("set", "a", "b", "c");
Set<String> set = jedis.smembers("set");
System.out.println(set);
}
// SortedSet 类型
@Test
public void testSortedSet () {
jedis.zadd("class", 90, "xiaobai");
List<String> aClass = jedis.zrangeByScore("class", 90, 100);
System.out.println(aClass);
Map<String, Double> map = new HashMap<>();
map.put("xiaohei", 88.0);
map.put("xiaohui", 94.0);
map.put("xiaolan", 84.0);
map.put("xiaolv", 82.0);
map.put("xiaohong", 99.0);
jedis.zadd("class", map);
List<String> aClass1 = jedis.zrange("class", 0, 6);
System.out.println(aClass1);
}
}