Redis在实际开发中有哪些应用场景?
Redis在实际开发中有哪些应用场景?下面我将结合Redisson来演示Redis在分布式场景中热点数据缓存、计数器、队列、发布/订阅、分布式锁…等场景下的应用。
热门数据排序
在现实场景中我们可能会遇到这样的场景,就是根据商品浏览数、或者根据评论点赞数来排序显示我们的商品。这种情况下我们可以选用Redisson来作为技术方案之一。例如我们常见的百度热榜也可以采用此类方式来实现,每当用户点击一次链接,排序分数+1,这样可以根据分数来排名实现当天的热榜展示。
这里接上面Redis应用场景1-热点数据缓存来继续给大家演示Redisson在排序场景下的应用。环境配置等参考我的Redis应用场景1。
实现排序功能使用的是Redis的Sorted Set有序集合。Sorted Set的底层数据结构使用的是ziplist+skiplist来实现。当满足下面两个条件时使用的是ziplist:
- 集合中元素数量不超过128
- 所有元素成员的长度不超过64字节
否则就会转换成skiplist。看到这里各位javaer有没有熟悉的感觉?是的,这里和Java中的HashMap存储机制类似,他们都是当集合容量达到一定时,会从一种数据结构转换成另一种数据结构。
在Redisson中通过ScoredSortedSet来封装Sorted Set,下面使用ScoredSortedSet来模拟高并发下2005超级女声
观众投票情况:
package com.example.redisdemo;
import org.junit.jupiter.api.Test;
import org.redisson.api.RFuture;
import org.redisson.api.RScoredSortedSet;
import org.redisson.api.RedissonClient;
import org.redisson.client.protocol.ScoredEntry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Collection;
import java.util.Iterator;
import java.util.Random;
import java.util.concurrent.ExecutionException;
@SpringBootTest
public class ScoredSortedTests {
@Autowired
private RedissonClient redissonClient;
@Test
public void ScoredSortedTest() throws ExecutionException, InterruptedException {
// RScoredSortedSet是Redisson中排序API
RScoredSortedSet<String> superGirlSet = redissonClient.getScoredSortedSet("superGirl");
// 初始化前十名超级女声选手:2005超级女声前十名包括李宇春、周笔畅、张靓颖、何洁、纪敏佳、黄雅莉、叶一茜、易慧、赵静怡、朱妍。
superGirlSet.add(0, "李宇春");
superGirlSet.add(0, "周笔畅");
superGirlSet.add(0, "张靓颖");
superGirlSet.add(0, "何洁");
superGirlSet.add(0, "纪敏佳");
superGirlSet.add(0, "黄雅莉");
superGirlSet.add(0, "叶一茜");
superGirlSet.add(0, "易慧");
superGirlSet.add(0, "赵静怡");
superGirlSet.add(0, "朱妍");
String[] superGirls = {"李宇春", "周笔畅", "张靓颖", "何洁", "纪敏佳", "黄雅莉", "叶一茜", "易慧", "赵静怡", "朱妍"};
//创建100个线程,每个线程模拟投票100次
CountDownLatch latch = new CountDownLatch(100);
for (int i = 0; i < 100; i++) {
Thread thread = new Thread(() -> {
for (int j = 0; j < 100; j++) {
Random random = new Random();
int i1 = random.nextInt(10);
superGirlSet.addScore(superGirls[i1], 1);
}
latch.countDown();
});
thread.start();
}
// 注意,这里使用CountDownLatch来休眠主线程,因为需要等待上面多线程都执行完,
// 否则,上有多线程还没执行完,这里的主线程就走完了,redisson会报错:Redisson is shutdown
latch.await();
RFuture<Collection<ScoredEntry<String>>> collectionRFuture = superGirlSet.entryRangeReversedAsync(0, -1);
Iterator<ScoredEntry<String>> iterator = collectionRFuture.get().iterator();
System.out.println("2005超级女声得票数:");
while (iterator.hasNext()) {
ScoredEntry<String> next = iterator.next();
System.out.println(next.getValue() + ":" + next.getScore());
}
}
}
投票结果出来了:2005年度超级女声人气得主:张靓颖!!!:
计数器
通过Redisson实现计数器的是RAtomicLong类,和Java中的AtomicLong类似,两者的区别是AtomicLong提供单机环境下的高并发原子操作,RAtomicLong则是提供在分布式场景下的原子操作。
其实除了计数器功能,在分布式数据库的自增ID、分布式系统商品的库存、限流器等场景下都可以使用RAtomicLong的原子读写能力。
下面是代码演示:
@Test
public void CounterTest() throws InterruptedException {
// 计数功能用到RAtomicLongAPI
RAtomicLong counter = redissonClient.getAtomicLong("counter");
//从0开始
counter.set(0);
//创建100个线程,每个线程模拟计数100次
CountDownLatch latch = new CountDownLatch(100);
for (int i = 0; i < 100; i++) {
Thread thread = new Thread(() -> {
for (int j = 0; j < 100; j++) {
counter.incrementAndGet();
}
latch.countDown();
});
thread.start();
}
// 注意,这里使用CountDownLatch来休眠主线程,因为需要等待上面多线程都执行完,
// 否则,上有多线程还没执行完,这里的主线程就走完了,redisson会报错:Redisson is shutdown
latch.await();
System.out.println("现在的RAtomicLong的值是:" + counter.get());
}
看结果: