在使用关系型数据库时,我们可以使用带where的SQL来进行选择查询。比如一个需求是查询大于25岁的人:
SELECT
id,
name,
sex,
age,
address
FROM
Person
WHERE
age > 25
但是在Redis中并不能使用SQL,也就不能进行选择查询。最普通的办法是将所有的数据都查出来,再在Java中进行过滤,如下所示:
@Override
public void test1() {
Map<String, Person> map = new HashMap<>();
String uuid1 = UUID.randomUUID().toString();
Person p1 = new Person(uuid1, "z1", "m", 24, "123");
map.put(uuid1, p1);
String uuid2 = UUID.randomUUID().toString();
Person p2 = new Person(uuid2, "q2", "m", 30, "456");
map.put(uuid2, p2);
String uuid3 = UUID.randomUUID().toString();
Person p3 = new Person(uuid3, "s3", "f", 26, "789");
map.put(uuid3, p3);
String uuid4 = UUID.randomUUID().toString();
Person p4 = new Person(uuid4, "l4", "m", 19, "101");
map.put(uuid4, p4);
String uuid5 = UUID.randomUUID().toString();
Person p5 = new Person(uuid5, "z5", "f", 35, "112");
map.put(uuid5, p5);
redisTemplate.opsForHash().putAll("PERSON", map);
Set<Person> set = new HashSet<>();
Map<Object, Object> entries = redisTemplate.opsForHash().entries("PERSON");
for (Map.Entry<Object, Object> entry : entries.entrySet()) {
Person person = (Person) entry.getValue();
if (person != null && person.getAge() != null && person.getAge() > 25) {
set.add(person);
}
}
for (Person p : set) {
System.out.println(p);
}
}
如上所示,这里模拟了5条Person数据,并使用哈希结构存在Redis中,key是uuid,value是具体的Person。最后需要将所有哈希的值都取出来再遍历进行筛选。这里只是模拟了5条数据,但如果是100万、1000万乃至更多条数据呢?如果将这么大数据量的数据全都取出来,内存也会受不了。所以说这种方案是不可取的。
这里提供一种实现方案的思路,也很简单,就是在插入数据的时候将符合查询条件的数据也一并插入进另一个set集合中。放在这里的需求就是不仅需要将所有数据都插入到map中,还要将大于25岁的数据的uuid放在一个新的set中。其实也就是以空间换时间的思路,插入一些冗余数据,以此来提高查询效率。如下所示:
@Override
public void test2() {
final String AGE_AFTER_25 = "AGE_AFTER_25";
final String MAN = "MAN";
Map<String, Person> map = new HashMap<>();
String uuid1 = UUID.randomUUID().toString();
Person p1 = new Person(uuid1, "z1", "m", 24, "123");
map.put(uuid1, p1);
redisTemplate.opsForSet().add(MAN, uuid1);
String uuid2 = UUID.randomUUID().toString();
Person p2 = new Person(uuid2, "q2", "m", 30, "456");
map.put(uuid2, p2);
redisTemplate.opsForSet().add(AGE_AFTER_25, uuid2);
redisTemplate.opsForSet().add(MAN, uuid2);
String uuid3 = UUID.randomUUID().toString();
Person p3 = new Person(uuid3, "s3", "f", 26, "789");
map.put(uuid3, p3);
redisTemplate.opsForSet().add(AGE_AFTER_25, uuid3);
String uuid4 = UUID.randomUUID().toString();
Person p4 = new Person(uuid4, "l4", "m", 19, "101");
map.put(uuid4, p4);
redisTemplate.opsForSet().add(MAN, uuid4);
String uuid5 = UUID.randomUUID().toString();
Person p5 = new Person(uuid5, "z5", "f", 35, "112");
map.put(uuid5, p5);
redisTemplate.opsForSet().add(AGE_AFTER_25, uuid5);
redisTemplate.opsForHash().putAll("PERSON", map);
System.out.println("年龄大于25岁的人:");
Set<Object> members = redisTemplate.opsForSet().members(AGE_AFTER_25);
List<Object> person1 = redisTemplate.opsForHash().multiGet("PERSON", members);
for (Object p : person1) {
System.out.println(p);
}
System.out.println("年龄大于25岁的男人:");
Set<Object> intersect = redisTemplate.opsForSet().intersect(AGE_AFTER_25, MAN);
List<Object> person2 = redisTemplate.opsForHash().multiGet("PERSON", intersect);
for (Object p : person2) {
System.out.println(p);
}
}
如上所示,在5条数据执行map.put方法后,会将符合条件的数据插入到Redis中。也就是将大于25岁的uuid放入到AGE_AFTER_25这个set中,将是男人的uuid放入到MAN这个set中。最后通过在map中查询key是AGE_AFTER_25的数据返回即可。
最后演示了一个组合条件查询的情况,即查找年龄大于25岁的男人这个需求。其实也就是将AGE_AFTER_25和MAN这两个集合取交集的结果。Redis中的set也支持交集、差集、并集这些操作,我们可以善加利用。这样的话就不需要将所有数据都取出来,而改为按需查询来实现。
当然这里的范围查询的条件一定是热点查询,Redis本身存的也都是热点缓存。如果你查询的条件是不经常会执行的,那么说实话,你这么操作的意义也不大。