本文主要介绍 scan、sscan 、smembers 的区别及在工作中的实际运用场景。
scan 和 sscan
适用范围
- scan:用于执行 redis 的
通用扫描命令,可以用于迭代遍历任何类型的键; - sscan:用于执行 redis 的
集合扫描命令,专门用于迭代遍历集合类型的键
参数
- scan:接受游标、匹配模式和返回数量等参数,可以更灵活地控制迭代过程;
- sscan:接受集合键、游标和扫描参数等参数,用于迭代遍历指定集合中的成员。
返回值
- scan:返回的是一个包含游标和键列表的结果集;
- sscan:返回的是一个包含游标和成员列表的结果集。
使用场景
- scan:适用于需要迭代遍历大量键或按照特定模式匹配键的情况;
- sscan:适用于需要迭代遍历特定集合中的成员的情况。
scan案例
假设我们有一个 redis 数据库,其中包含多个以 "user:" 开头的键,每个键代表一个用户的信息。我们想要获取所有以 "user:" 开头的键,并打印它们的值。
import redis.clients.jedis.Jedis;
import redis.clients.jedis.ScanParams;
import redis.clients.jedis.ScanResult;
public class RedisScannerExample {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost", 6379);
try {
// 设置匹配模式为以 "user:" 开头的键
String pattern = "user:*";
// 设置每次迭代返回的最大数量为 10
int pageSize = 10;
// 设置初始游标为 "0"
String cursor = ScanParams.SCAN_POINTER_START;
// 创建 ScanParams 对象,并设置匹配模式和每次迭代返回的最大数量
ScanParams params = new ScanParams().match(pattern).count(pageSize);
// 开始迭代扫描
do {
// 执行 SCAN 命令
ScanResult<String> result = jedis.scan(cursor, params);
// 获取当前迭代返回的键列表
for (String key : result.getResult()) {
// 打印键和对应的值
System.out.println("Key: " + key + ", Value: " + jedis.get(key));
}
// 更新游标
cursor = result.getStringCursor();
} while (!cursor.equals(ScanParams.SCAN_POINTER_START));
} finally {
// 关闭连接
jedis.close();
}
}
}
sscan 案例
假设我们有一个 redis 集合,其中存储了一组用户的 ID,我们想要获取该集合中的所有用户 ID。
import redis.clients.jedis.Jedis;
import redis.clients.jedis.ScanParams;
import redis.clients.jedis.ScanResult;
public class RedisSScanExample {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost", 6379);
try {
// 集合的键
String key = "user_ids";
// 设置初始游标为 "0"
String cursor = ScanParams.SCAN_POINTER_START;
// 创建 ScanParams 对象,设置每次迭代返回的最大数量
ScanParams params = new ScanParams().count(10);
// 开始迭代扫描集合
do {
// 执行 SSCAN 命令
ScanResult<String> result = jedis.sscan(key, cursor, params);
// 获取当前迭代返回的成员列表
for (String member : result.getResult()) {
// 打印成员
System.out.println("Member: " + member);
}
// 更新游标
cursor = result.getStringCursor();
} while (!cursor.equals(ScanParams.SCAN_POINTER_START));
} finally {
// 关闭连接
jedis.close();
}
}
}
sscan和smembers
sscan 和 smembers 是 redis 中用于操作集合的两个命令,区别如下:
返回方式
- sscan:以迭代器的方式返回集合中的元素,每次调用返回一批元素;
- smembers:一次性的返回集合中所有元素。
使用场景
- SSCAN:适用于大型集合,因为它可以分批返回元素,不会因为集合大小而造成阻塞或占用大量内存;
- SMEMBERS:适用于小型集合,因为它一次性返回所有元素,适用于集合元素数量较少的情况。
性能
- SSCAN:由于以迭代器方式返回元素,可以在大型集合中实现高性能的迭代操作;
- SMEMBERS:性能相对较差,尤其在大型集合中,因为它需要一次性返回所有元素。
smembers案例
假设我们有一个 redis 集合,其中存储了一组用户的 ID,我们想要获取该集合中的所有用户 ID。
import redis.clients.jedis.Jedis;
import java.util.Set;
public class RedisSMEMBERSExample {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost", 6379);
try {
// 集合的键
String key = "user_ids";
// 获取集合中的所有成员
Set<String> members = jedis.smembers(key);
// 打印集合中的所有成员
for (String member : members) {
System.out.println("Member: " + member);
}
} finally {
// 关闭连接
jedis.close();
}
}
}
线上使用
在 redis 使用场景(一):并行流+二级缓存 文章中提到,使用 redis 缓存来构建本地缓存,在test_user_data集合中大约保存了 50 多万的用户 id,就是使用的 sscan。
public Set<String> sscanAll(String keyPattern, int pageSize) {
try {
Set<String> result = new HashSet<>();
String envPattern = prefixKey + keyPattern;
ScanParams scanParams = new ScanParams();
scanParams.count(pageSize);
String cursor = ScanParams.SCAN_POINTER_START;
while(true){
ScanResult scanResult = jedisCluster.sscan(envPattern, cursor, scanParams);
List<String> currentPage = scanResult.getResult();
cursor = scanResult.getStringCursor();
result.addAll(currentPage);
if("0".equals(cursor)){
break;
}
}
return result;
} catch (Exception e) {
throw e;
}
}
public Set<String> sscanAll(String keyPattern){
return sscanAll(keyPattern, 1000);
}
总结
smembers、sscan、scan 各有适用场景,应根据集合大小、内存限制和性能要求来选择最合适的命令。