一、简述
Redis是一款高性能的键值对存储数据库,它支持五种基本数据类型,分别是字符串(String)、列表(List)、哈希(Hash)、集合(Set)、有序集合(Sorted Set)。
二、五种基本数据类型
2.1 字符串(String)
String是Redis最基本的类型,一个key对应一个value,它是数据安全的,并且可以包含任何数据,一个Redis的字符串最多可以是512M(字符串长度小于1M的时候,每次扩容都是加倍现有空间,如果超过1M,扩容的时候只会扩容1M空间)
常用的应用场景:缓存用户信息、用户登录状态、配置文件等等
常用命令
set <key><value> 添加键值对
get <key> 查询对应键值
append <key><value>将给定的 追加到原值的末尾
strlen <key> 获得值的长度
setnx <key><value> 只有在 key 不存在时 设置 key 的值
incr <key> 将 key 中储存的数字值增1只能对数字值操作,如果为空,新增值为
decr <key> 将 key 中储存的数字值减1只能对数字值操作,如果为空,新增值为-1
mset <key1><value1><key2><value2> ..... 同时设置一个或多个 key-value对
mget <key1><key2><key3> .....同时获取一个或多个 value
msetnx <key1><value1><key2><value2> ..... 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。
getrange <key><起始位置><结束位置> 获得值的范围,类似java中的substring,前包,后包
setrange <key><起始位置><value>用 <value> 覆写<key> 所储存的字符串值,从<起始位置>开始(索引从0开始)。
setex <key><过期时间><value> 设置键值的同时,设置过期时间,单位秒。
2.2 列表(List)
列表相当于是单个键对应多个值,它是一个有序的字符串元素集合,它按照插入的顺序来存储元素,可以在列表的头部或尾部进行插入和删除操作。它的底层实际是一个双向列表,对两端的操作性能很高,通过索引下标的操作中间的节点性能会较差
常用的应用场景:消息队列,任务队列,历史记录等
常用命令
lpush/rpush <key><value1><value2><value3> .... 从左边/右边插入一个或多个值。
lpop/rpop <key> 从左边/右边吐出一个值。值在键在,值光键亡。
rpoplpush <key1><key2>从<key1> 列表右边吐出一个值,插到<key2>列表左边。
lrange <key><start><stop> 按照索引下标获得元素(从左到右)
lrange key 0 -1 0左边第一个,-1右边第一个,(0-1表示获取所有)
lindex <key><index> 按照索引下标获得元素(从左到右)
llen <key> 获得列表长度
linsert <key> before <value><newvalue> 在<value>的后面插入<newvalue>插入值
lrem <key><n><value> 从左边删除n个value(从左到右)
lset<key><index><value> 将列表key下标为index的值替换成value
2.3 集合(Set)
无序的字符串元素集合,跟List有点相似,不一样的是集合中的元素是唯一的,不允许重复的
常用的应用场景:签到系统,好友关系,去重等功能
常用的命令
sadd <key><value1><value2> ..... 将一个或多个 member 元素加入到集合 key 中,已经存在的member 元素将被忽略
smembers <key>取出该集合的所有值。
sismember <key><value>判断集合<key>是否为含有该<value>值,有1,没有0
scard<key>返回该集合的元素个数。
srem <key><value1><value2> .... 删除集合中的某个元素。
spop <key>随机从该集合中吐出一个值。
srandmember <key><n>随机从该集合中取出n个值。不会从集合中删除 。
smove <source><destination>value把集合中一个值从一个集合移动到另一个集合
sinter <key1><key2>返回两个集合的交集元素。
sunion <key1><key2>返回两个集合的并集元素。
sdiff <key1><key2>返回两个集合的差集元素(key1中的,不包含key2中的)
2.4 哈希(Hash)
哈希是一个键值对的集合,其中键和值都是字符串,它可以用来存储对象的属性,类似与关系型数据库中的表记录
常用的应用场景:存储用户信息,商品信息等对象数据
常用的命令
hset <key><field><value>给<key>集合中的 <field>键赋值<value>
hget <key1><field>从<key1>集合<field>取出 value
hmset <key1><field1><value1><field2><value2>... 批量设置hash的值
hexists<key1><field>查看哈希表 key 中,给定域 field 是否存在。
hkeys <key>列出该hash集合的所有field
hvals <key>列出该hash集合的所有value
hincrby <key><field><increment>为哈希表 key 中的域 field 的值加上增量 1 -1
hsetnx <key><field><value>将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在
2.5 有序集合(Sorted Set/zset)
sorted Set和zset是一个东西,他们都是同一数据类型,只是称呼不同。有序集合和set是非常的相似,不同之处是有序集合的每个成员都关联了一个评分(score),这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。集合成员是唯一的,但是评分可以重复。因为元素是有序的,所以我们也可以跟快的根据评分(score)或者次序(position)来获取范围元素
常用的应用场景:排行榜,热门话题等功能
常用的命令
zadd <key><score1><value1><score2><value2>…将一个或多个 member 元素及其score 值加入到有序集 key 当中
zrange <key><start><stop> [WITHSCORES] 返回有序集 key 中,下标在<start><stop>之间的元素,带WITHSCORES,可以让分数一起和值返回到结果集。
zrangebyscore key minmax [withscores] [limit offset count]返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列。
zrevrangebyscore key maxmin [withscores] [limit offset count] 同上,改为从大到小排列。
zincrby <key><increment><value> 为元素的score加上增量
zrem <key><value>删除该集合下,指定值的元素
zcount <key><min><max>统计该集合,分数区间内的元素个数
zrank <key><value>返回该值在集合中的排名,从0开始。
三、五种数据类型使用案例
3.1 连接redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
或
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.8.0</version>
</dependency>
注意点:这个是springboot的jar,其实也可以直接导入jedis的ajr
下面连接redis调用的都是这个RedisTestUtil工具类
package com.study.redis;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* @author Administrator
* @version 1.0
* @project study
* @description
* @date 2025/5/20 星期二 17:11:46
*/
public class RedisTestUtil {
/**
* 地址
*/
private static final String HOST = "127.0.0.1";
/**
* 端口
*/
private static final int PORT = 6379;
/**
* 缓存时间
*/
private static final int TIMEOUT = 30000;
/**
* 密码
*/
private static final String PASSWORD = "hm23";
/**
* 使用的redis库
*/
private static final int DATABASE = 0;
private static final JedisPool jedisPool;
static {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(100);
config.setMaxIdle(10);
config.setMinIdle(5);
config.setTestOnBorrow(true);
jedisPool = new JedisPool(config, HOST, PORT, TIMEOUT, PASSWORD, DATABASE);
}
public static Jedis getJedis() {
return jedisPool.getResource();
}
public static void close(Jedis jedis) {
if (jedis != null) {
jedis.close();
}
}
}
注意点:如果想springboot连接redis可以查看springboot连接redis_springboot链接单机redis bean注入地址信息-CSDN博客
3.2 String使用案例(存储token)
用字符串缓存一个token
public static void main(String[] args) {
String stringKey = "auth:token:";
String token = "token222222222222222222222222222222";
//连接redis
Jedis jedis = RedisTestUtil.getJedis();
// redis缓存token
jedis.set(stringKey, token);
//redis获取对应的值
String content = jedis.get(stringKey);
System.out.println("获取到的值为>>>>>>>>"+content);
}
3.3 List使用案例(用户操作历史记录)
用list缓存历史记录,每个用户对应一个list
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
import java.util.Date;
/**
* @author Administrator
* @version 1.0
* @project study
* @description
* @date 2025/5/20 星期二 17:29:29
*/
@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
public class HistoryRecord {
private Integer id;
private String userId; // 用户ID
private String content; // 记录内容
private String time; // 记录时间
}
public class RedisTest {
private static final String HISTORY_KEY_PREFIX = "user:history:";
private static final int MAX_RECORDS = 20; // 每个用户最大记录数
public static void main(String[] args) {
// 缓存userId为1的历史记录
RedisTest test = new RedisTest();
for (int i = 0; i < 10; i++) {
test.addRecord(i,"1","点击了"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
//缓存userId为2的历史记录
for (int i = 0; i < 10; i++) {
test.addRecord(i,"2","点击了"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 获取userId为1历史记录
System.out.println("==============================获取userId为1历史记录=============================================");
List<HistoryRecord> historyRecordList = test.getRecords("1");
for (HistoryRecord historyRecord : historyRecordList) {
System.out.println(JSON.toJSONString(historyRecord));
}
// 获取用户最新的n条历史记录
System.out.println("==============================获取用户最新的n条历史记录=============================================");
List<HistoryRecord> historyRecordList2 = test.getLatestRecords("1",2);
for (HistoryRecord historyRecord : historyRecordList2) {
System.out.println(JSON.toJSONString(historyRecord));
}
System.out.println("==============================删除单条历史记录=============================================");
test.deleteRecord("1","2");// 第二个参数为操作id
System.out.println("==============================清空用户的历史记录=============================================");
test.clearHistory("1");
}
/**
* 添加历史记录(自动去重,保持最新记录)
*/
public void addRecord(Integer id,String userId, String content) {
Jedis jedis = RedisTestUtil.getJedis();
String key = HISTORY_KEY_PREFIX + userId;
HistoryRecord record = new HistoryRecord(id,userId,content, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
String json = JSON.toJSONString(record);
// 1. 从左边删除,先删除旧记录(如果存在)
jedis.lrem(key, 0, json);
// 2. 添加新记录到列表头部(最新记录在前)
jedis.lpush(key, json);
// 3. 限制列表长度,超出部分自动删除
if (jedis.llen(key) > MAX_RECORDS) {
jedis.rpop(key);
}
}
/**
* 获取用户的历史记录(按时间倒序)
*/
public List<HistoryRecord> getRecords(String userId) {
Jedis jedis = RedisTestUtil.getJedis();
String key = HISTORY_KEY_PREFIX + userId;
List<String> jsonList = jedis.lrange(key, 0, -1);
List<HistoryRecord> records = new ArrayList<>();
for (String json : jsonList) {
records.add(JSON.parseObject(json, HistoryRecord.class));
}
return records;
}
/**
* 获取用户最新的N条历史记录
*/
public List<HistoryRecord> getLatestRecords(String userId, int count) {
Jedis jedis = RedisTestUtil.getJedis();
String key = HISTORY_KEY_PREFIX + userId;
List<String> jsonList = jedis.lrange(key, 0, count - 1);
List<HistoryRecord> records = new ArrayList<>();
for (String json : jsonList) {
records.add(JSON.parseObject(json, HistoryRecord.class));
}
return records;
}
/**
* 删除单条记录实录
* @param userId
* @param recordId 操作的id
*/
public void deleteRecord(String userId, String recordId) {
Jedis jedis = RedisTestUtil.getJedis();
String key = HISTORY_KEY_PREFIX + userId;
List<String> jsonList = jedis.lrange(key, 0, -1);
// 查找并删除匹配ID的记录
for (String json : jsonList) {
HistoryRecord record = JSON.parseObject(json, HistoryRecord.class);
if (record.getId().equals(Integer.valueOf(recordId))) {
jedis.lrem(key, 0, json);
break;
}
}
}
/**
* 清空用户的历史记录
*/
public void clearHistory(String userId) {
Jedis jedis = RedisTestUtil.getJedis();
String key = HISTORY_KEY_PREFIX + userId;
jedis.del(key);
}
}
3.3 Set使用案例(当月签到情况)
利用set缓存一个签到功能,通过用户id可以查看到当月签到情况
public class RedisTest {
private static final String HISTORY_KEY_PREFIX = "user:history:";
private static final int MAX_RECORDS = 20; // 每个用户最大记录数
private static final String SIGN_KEY_PREFIX = "sign:";
public static void main(String[] args) {
Long userId = 1001L;
RedisTest test = new RedisTest();
// 签到
boolean isNewSign = test.doSign(userId);
System.out.println("签到结果: " + (isNewSign ? "新签到" : "已签到"));
// 检查是否签到
System.out.println("今日是否签到: " + test.checkSign(userId));
// 获取当月签到次数
System.out.println("当月签到次数: " + test.getSignCount(userId));
// 获取连续签到次数
System.out.println("连续签到次数: " + test.getContinuousSignCount(userId));
// 获取签到详情
List<Boolean> signDetail = test.getSignDetail(userId);
System.out.println("当月签到详情: " + signDetail);
}
// 生成 Key(格式:sign:{userId}:{yearmonth})
private String getSignKey(Long userId) {
LocalDate now = LocalDate.now();
String yearMonth = now.format(DateTimeFormatter.ofPattern("yyyyMM"));
return SIGN_KEY_PREFIX + userId + ":" + yearMonth;
}
// 签到(返回 true 表示首次签到,false 表示已签到)
public boolean doSign(Long userId) {
Jedis jedis = RedisTestUtil.getJedis();
LocalDate now = LocalDate.now();
int dayOfMonth = now.getDayOfMonth();
String key = getSignKey(userId);
// 使用 SADD 命令添加成员(Set 自动去重,存在则返回 0)
return jedis.sadd(key, String.valueOf(dayOfMonth)) == 1;
}
// 检查今日是否已签到
public boolean checkSign(Long userId) {
Jedis jedis = RedisTestUtil.getJedis();
LocalDate now = LocalDate.now();
int dayOfMonth = now.getDayOfMonth();
String key = getSignKey(userId);
// 使用 SISMEMBER 命令检查成员是否存在
return jedis.sismember(key, String.valueOf(dayOfMonth));
}
// 获取当月签到总次数
public long getSignCount(Long userId) {
Jedis jedis = RedisTestUtil.getJedis();
String key = getSignKey(userId);
// 使用 SCARD 命令获取集合大小
return jedis.scard(key);
}
// 获取当月连续签到次数
public long getContinuousSignCount(Long userId) {
Jedis jedis = RedisTestUtil.
3.4 Hash使用案例(缓存商品信息)
利用Hash缓存商品信息
@Data
@Accessors(chain = true)
public class Product {
private Long productId;
private String name;
private Double price;
private Integer stock;
private String category;
private String description;
private Long updateTime;
}
public class RedisTest {
private final Jedis jedis = RedisTestUtil.getJedis();
private static final String PRODUCT_KEY_PREFIX = "product:";
public static void main(String[] args) {
// 创建商品对象
Product product = new Product();
product.setProductId(1001L);
product.setName("iPhone 15 Pro");
product.setPrice(6999.00);
product.setStock(100);
product.setCategory("手机/数码");
product.setDescription("A17芯片,灵动岛设计");
product.setUpdateTime(System.currentTimeMillis() / 1000);
RedisTest redisTest = new RedisTest();
// 存储商品到 Redis(过期时间 1天 = 86400秒)
redisTest.saveProduct(product, 86400);
System.out.println("商品已缓存");
// 获取所有属性
Map<String, String> productData = redisTest.getProduct(1001L);
System.out.println("商品信息:" + productData);
// 更新库存
redisTest.updateProductField(1001L, "stock", "80");
System.out.println("库存更新后:" + redisTest.getProductField(1001L, "stock"));
// 删除描述字段
redisTest.deleteProductField(1001L, "description");
System.out.println("删除描述后:" + productData.get("description")); // 应输出 null
}
// 存储商品信息到 Redis Hash(带过期时间)
public void saveProduct(Product product, int expireSeconds) {
String key = getProductKey(product.getProductId());
Map<String, String> hash = new HashMap<>();
hash.put("name", product.getName());
hash.put("price", String.valueOf(product.getPrice()));
hash.put("stock", String.valueOf(product.getStock()));
hash.put("category", product.getCategory());
hash.put("description", product.getDescription());
hash.put("update_time", String.valueOf(product.getUpdateTime()));
jedis.hmset(key, hash); // 批量设置字段
jedis.expire(key, expireSeconds); // 设置过期时间(秒)
}
// 获取商品的所有属性
public Map<String, String> getProduct(Long productId) {
String key = getProductKey(productId);
return jedis.hgetAll(key); // 返回所有字段和值
}
// 获取单个属性(如价格)
public String getProductField(Long productId, String field) {
String key = getProductKey(productId);
return jedis.hget(key, field);
}
// 更新商品属性(如库存)
public Long updateProductField(Long productId, String field, String value) {
String key = getProductKey(productId);
return jedis.hset(key, field, value); // 返回 1 表示新增字段,0 表示更新现有字段
}
// 删除商品属性(如描述)
public Long deleteProductField(Long productId, String field) {
String key = getProductKey(productId);
return jedis.hdel(key, field); // 返回删除的字段数
}
// 生成完整 Key
private String getProductKey(Long productId) {
return PRODUCT_KEY_PREFIX + productId;
}
}
3.4 Sort Set使用案例(排行榜)
利用Sort Set缓存一个排行榜
@Accessors(chain = true)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RankItem {
private int rank; // 排名
private String userId; // 用户 ID
private double score; // 分数
}
public class RedisTest {
private Jedis jedis = RedisTestUtil.getJedis();
private static final String RANK_KEY = "rank:score"; // 排行榜 Key
public static void main(String[] args) {
RedisTest test = new RedisTest();
// 添加测试数据
test.updateScore("user:1001", 95.0);
test.updateScore("user:1002", 88.0);
test.updateScore("user:1003", 92.0);
test.updateScore("user:1004", 76.0);
test.updateScore("user:1005", 95.0); // 与 1001 分数相同
// 获取前三名
List<RankItem> top3 = test.getTopN(3);
System.out.println("排名前三用户为:");
top3.forEach(System.out::println);
// 获取用户 1002 的排名
long rank = test.getUserRank("user:1002");
System.out.println("用户1002排名: " + rank);
}
// 添加/更新用户分数
public void updateScore(String userId, double score) {
jedis.zadd(RANK_KEY, score, userId);
}
// 获取排行榜前 N 名(降序)
public List<RankItem> getTopN(int n) {
// 获取前 N 名(分数从高到低)
Set<Tuple> topN = jedis.zrevrangeWithScores(RANK_KEY, 0, n - 1);
List<RankItem> result = new ArrayList<>();
int rank = 1;
for (Tuple tuple : topN) {
result.add(new RankItem(
rank++,
tuple.getElement(), // 用户 ID
tuple.getScore() // 分数
));
}
return result;
}
// 获取用户排名(从 1 开始)
public long getUserRank(String userId) {
// ZREVRANK 返回 0 为第一名
Long rank = jedis.zrevrank(RANK_KEY, userId);
return rank != null ? rank + 1 : -1; // -1 表示未找到
}
// 获取用户分数
public double getUserScore(String userId) {
Double score = jedis.zscore(RANK_KEY, userId);
return score != null ? score : 0;
}
}
参考文档:Redis五大基本数据类型(String、LIst、Set、Hash、ZSet)及其底层结构-腾讯云开发者社区-腾讯云