场景说明
今天我们来模拟一个这样的场景,我们在本地有多个文本文件,每个文件里面存了很多的32位的字符串作为用户的唯一标识,每个用户存做一行,假如我们每天都有非常大规模的用户,这样我们可能在工作中就存在需要对这些用户进行交集、并集或补集等处理,最简单的方式是通过Java中的集合来进行运算即可,比如通过HashSet来进行相应的一些运算,但是这样的运算存在一个局限性,那就是我们一般在JVM运行过程中初始的内存是有限的,这样如果全部在JVM内存中进行计算的话,很容易出现内存空间不足导致的OOM异常,那么我们今天来介绍一种拓展性更强的方式来进行这样的一些交并补的运算:通过Redis来实现数据的交集、并集、补集
环境说明
-
Redis版本: Redis 6.0.6 -
Jedis版本: 4.2.2 -
工具类hutool版本: 5.8.0.M3 -
pom文件:
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.2.2</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.0.M3</version>
</dependency>
</dependencies>
交并补计算
初始化常量
public class RedisCalculateUtils {
static String oneFileString = "/Users/tmp/test-1.txt";
static String twoFileString = "/Users/tmp/test-2.txt";
static String diffFileString = "/Users/tmp/diff-test.txt";
static String interFileString = "/Users/tmp/inter-test.txt";
static String unionFileString = "/Users/tmp/union-test.txt";
static String oneFileCacheKey = "oneFile";
static String twoFileCacheKey = "twoFile";
static String diffFileCacheKey = "diffFile";
static String interFileCacheKey = "interFile";
static String unionFileCacheKey = "unionFile";
}
初始化数据到指定文件
public static void writeFile() {
File oneFile = new File(oneFileString);
List<String> fs = new ArrayList<>(10000);
for (int i = 10000; i < 15000; i++) {
String s = SecureUtil.md5(String.valueOf(i));
fs.add(s);
}
FileUtil.writeUtf8Lines(fs, oneFile);
File twoFile = new File(twoFileString);
fs.clear();
for (int i = 12000; i < 20000; i++) {
String s = SecureUtil.md5(String.valueOf(i));
fs.add(s);
}
FileUtil.writeUtf8Lines(fs, twoFile);
}
指定文件写入Redis
public static void writeCache() {
try(Jedis jedis = new Jedis("127.0.0.1", 6379)) {
Pipeline p = jedis.pipelined();
List<String> oneFileStringList = FileUtil.readLines(oneFileString, "UTF-8");
for (String s : oneFileStringList) {
p.sadd(oneFileCacheKey, s);
}
p.sync();
List<String> twoFileStringList = FileUtil.readLines(twoFileString, "UTF-8");
for (String s : twoFileStringList) {
p.sadd(twoFileCacheKey, s);
}
p.sync();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
差集的计算
public static void diff(String oneKey, String twoKey, String threeKey) {
try(Jedis jedis = new Jedis("127.0.0.1", 6379)) {
long result = jedis.sdiffstore(threeKey, oneKey, twoKey);
System.out.println("oneKey 与 twoKey 的差集的个数:" + result);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
差集计算结果写入到指定文件
public static void writeDiffToFile() {
File diffFile = new File(diffFileString);
try(Jedis jedis = new Jedis("127.0.0.1", 6379)) {
Set<String> result = jedis.smembers(diffFileCacheKey);
FileUtil.writeUtf8Lines(result, diffFile);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
交集的计算
public static void inter(String[] cacheKeyArray, String destinationKey) {
try(Jedis jedis = new Jedis("127.0.0.1", 6379)) {
long result = jedis.sinterstore(destinationKey, cacheKeyArray);
System.out.println("cacheKeyArray 的交集的个数:" + result);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
交集计算结果写入指定文件
public static void writeInterToFile() {
File interFile = new File(interFileString);
try(Jedis jedis = new Jedis("127.0.0.1", 6379)) {
Set<String> result = jedis.smembers(interFileCacheKey);
FileUtil.writeUtf8Lines(result, interFile);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
并集的计算
public static void union(String[] cacheKeyArray, String destinationKey) {
try(Jedis jedis = new Jedis("127.0.0.1", 6379)) {
long result = jedis.sunionstore(destinationKey, cacheKeyArray);
System.out.println("cacheKeyArray 的并集的个数:" + result);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
并集计算结果写入到指定文件
public static void writeUnionToFile() {
File unionFile = new File(unionFileString);
try(Jedis jedis = new Jedis("127.0.0.1", 6379)) {
Set<String> result = jedis.smembers(unionFileCacheKey);
FileUtil.writeUtf8Lines(result, unionFile);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Redis命令说明
SDIFFSTORE destination key [key …]
举例说明:
key1 = {a,b,c,d}
key2 = {c}
key3 = {a,c,e}
SDIFF key1 key2 key3 = {b,d}
SDIFFSTORE 命令的作用和SDIFF类似,不同的是它将结果保存到 destination 集合,而把结果集返回给客户端。
如果 destination 集合已经存在,则将其覆盖。
SINTERSTORE destination key [key …]
举例说明:
key1 = {a,b,c,d}
key2 = {c}
key3 = {a,c,e}
SINTER key1 key2 key3 = {c}
SINTERSTORE 命令与 SINTER 命令类似,不同的是它并不是直接返回结果集,而是将结果保存在 destination 集合中。
如果 destination 集合存在, 则会被覆盖。
SUNIONSTORE destination key [key …]
举例说明:
key1 = {a,b,c,d}
key2 = {c}
key3 = {a,c,e}
SUNION key1 key2 key3 = {a,b,c,d,e}
SUNIONSTORE 命令的功能类似于 SUNION,不同的是不反回结果集,而是存储在 destination 中。
如果 destination 已经存在,则被覆盖。
参考资料: https://www.redis.com.cn/set.html
|