1. 记录帖子的阅读数量
1.1 业务代码
public class PostService {
private JedisPool jedisPool = JedisFactory.getInstance().getJedisPool();
// 增加帖子的阅读数量
public void increaseReadCount(int postId) {
String key = "post:read:count:" + postId;
try (Jedis jedis = jedisPool.getResource()) {
jedis.incr(key);
} catch (Exception e) {
e.printStackTrace();
}
}
// 增加帖子的点赞数量
public void increaseLikeCount(int postId) {
String key = "post:like:count:" + postId;
try (Jedis jedis = jedisPool.getResource()) {
jedis.incr(key);
} catch (Exception e) {
e.printStackTrace();
}
}
// 减少帖子的点赞数量
public void decreaseLikeCount(int postId) {
String key = "post:like:count:" + postId;
try (Jedis jedis = jedisPool.getResource()) {
jedis.decr(key);
} catch (Exception e) {
e.printStackTrace();
}
}
}
1.2 测试代码
public class ServiceTest {
PostService postService = new PostService();
@Test
public void testIncreasePostReadCount() {
postService.increaseReadCount(101);
postService.increaseReadCount(101);
postService.increaseReadCount(101);
}
@Test
public void testIncreasePostLikeCount() {
postService.increaseLikeCount(101);
postService.increaseLikeCount(101);
postService.increaseLikeCount(101);
}
@Test
public void testDecreasePostLikeCount() {
postService.decreaseLikeCount(101);
}
}
2. 生成热门帖子排行榜
2.1 业务代码
public class PostService {
private JedisPool jedisPool = JedisFactory.getInstance().getJedisPool();
// 添加热门帖子
public void addPostList(int postId, double score) {
String key = "post:list";
try (Jedis jedis = jedisPool.getResource()) {
jedis.zadd(key, score, String.valueOf(postId));
if (jedis.zcard(key) > 10) {
jedis.zpopmin(key);
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 返回热门帖子列表
public List<Integer> getPostList() {
String key = "post:list";
try (Jedis jedis = jedisPool.getResource()) {
Set<String> set = jedis.zrevrange(key, 0, -1);
List<Integer> list = new ArrayList<>();
for (String id : set) {
list.add(Integer.valueOf(id));
}
return list;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
2.2 测试代码
public class ServiceTest {
PostService postService = new PostService();
@Test
public void testAddPostList() {
for (int i = 0; i < 30; i++) {
postService.addPostList(i + 1, (i + 1) * 10);
}
}
@Test
public void testGetPostList() {
List<Integer> list = postService.getPostList();
System.out.println(list);
}
}
3. 记录用户的粉丝数量
3.1 业务代码
public class UserService {
private JedisPool jedisPool = JedisFactory.getInstance().getJedisPool();
// 关注 userId:用户id, targetId:用户将要关注的用户id
public void follow(int userId, int targetId) {
String key1 = "following:" + userId;
String key2 = "follower:" + targetId;
Jedis jedis = jedisPool.getResource();
Transaction tx = null;
try {
// 加乐观锁,
// 为什么加在key1上?因为同一时刻A只能关注一个人,而B可以同一时刻被多个人关注
jedis.watch(key1);
tx = jedis.multi();
tx.sadd(key1, String.valueOf(targetId));
tx.sadd(key2, String.valueOf(userId));
tx.exec();
} catch (Exception e) {
e.printStackTrace();
// 捕捉到异常则取消提交事务
if (tx != null) {
tx.discard();
}
} finally {
if (tx != null) {
tx.close();
}
if (jedis != null) {
jedis.close();
}
}
}
// 获取粉丝数
public long getFollowerCount(int userId) {
try (Jedis jedis = jedisPool.getResource()) {
String key = "follower:" + userId;
return jedis.scard(key);
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
// 获取关注数
public long getFollowingCount(int userId) {
try (Jedis jedis = jedisPool.getResource()) {
String key = "following:" + userId;
return jedis.scard(key);
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
// 获取共同关注列表
public Set<Integer> getSameFollowing(int userId1, int userId2) {
try (Jedis jedis = jedisPool.getResource()) {
String key1 = "following:" + userId1;
String key2 = "following:" + userId2;
Set<String> set = jedis.sinter(key1, key2);
Set<Integer> result = new HashSet<>();
for (String member : set) {
result.add(Integer.valueOf(member));
}
return result;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
3.2 测试代码
public class ServiceTest {
UserService userService = new UserService();
@Test
public void testFollow() {
userService.follow(1, 8);
userService.follow(2, 8);
userService.follow(3, 8);
userService.follow(1, 9);
userService.follow(2, 9);
userService.follow(9, 1);
userService.follow(9, 2);
}
@Test
public void testGetFollowCount() {
System.out.println(userService.getFollowingCount(1));
System.out.println(userService.getFollowerCount(1));
System.out.println(userService.getSameFollowing(1, 2));
System.out.println(userService.getSameFollowing(3, 2));
}
}
4. 记录用户的兴趣标签
4.1 业务代码
public class UserService {
private JedisPool jedisPool = JedisFactory.getInstance().getJedisPool();
// 添加兴趣标签
public void addTags(int userId, String... tags) {
if (tags == null || tags.length == 0) {
throw new IllegalArgumentException("参数为空!");
}
try (Jedis jedis = jedisPool.getResource()) {
String key = "tags:" + userId;
// pipeline流水线
Pipeline pipeline = jedis.pipelined();
for (String tag : tags) {
pipeline.sadd(key, tag);
}
pipeline.syncAndReturnAll();
} catch (Exception e) {
e.printStackTrace();
}
}
// 获取兴趣标签
public Set<String> getTags(int userId) {
try (Jedis jedis = jedisPool.getResource()) {
String key = "tags:" + userId;
return jedis.smembers(key);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
4.2 测试代码
public class ServiceTest {
UserService userService = new UserService();
@Test
public void testTags() {
// 添加标签
userService.addTags(1, "音乐");
userService.addTags(1, "运动");
userService.addTags(1, new String[]{"游戏", "交友", "鬼畜"});
// 查询标签
System.out.println(userService.getTags(1));
}
}
5. 记录用户的待办事项
5.1 业务代码
public class UserService {
private JedisPool jedisPool = JedisFactory.getInstance().getJedisPool();
// 添加待办事项
public void addTodoItem(int userId, String item) {
if (item == null || item.length() == 0) {
throw new IllegalArgumentException("参数为空!");
}
try (Jedis jedis = jedisPool.getResource()) {
String key = "todo:list:" + userId;
jedis.rpush(key, item);
} catch (Exception e) {
e.printStackTrace();
}
}
// 移除待办事项
public void delTodoItem(int userId, String item) {
if (item == null || item.length() == 0) {
throw new IllegalArgumentException("参数为空!");
}
try (Jedis jedis = jedisPool.getResource()) {
String key = "todo:list:" + userId;
jedis.lrem(key, 1, item);
} catch (Exception e) {
e.printStackTrace();
}
}
// 获取所有待办事项
public List<String> getTodoItem(int userId) {
try (Jedis jedis = jedisPool.getResource()) {
String key = "todo:list:" + userId;
return jedis.lrange(key, 0, -1);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
5.2 测试代码
public class ServiceTest {
UserService userService = new UserService();
@Test
public void testTodoItem() {
// 添加待办事项
userService.addTodoItem(1, "看书");
userService.addTodoItem(1, "购物");
userService.addTodoItem(1, "订外卖");
userService.addTodoItem(1, "打游戏");
// 删除待办事项
userService.delTodoItem(1, "打游戏");
// 查询待办事项
System.out.println(userService.getTodoItem(1));
}
}
6. 存储用户的登录会话
6.1 业务代码
public class UserService {
private JedisPool jedisPool = JedisFactory.getInstance().getJedisPool();
// 创建令牌
public String createToken(int userId) {
try (Jedis jedis = jedisPool.getResource()) {
String token = CommonUtil.generateUUID();
String key = "token:" + token;
jedis.setex(key, 10, String.valueOf(userId));
return token;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// 验证令牌
public Integer validateToken(String token) {
if (token == null || token.length() == 0) {
throw new IllegalArgumentException("参数为空!");
}
try (Jedis jedis = jedisPool.getResource()) {
String key = "token:" + token;
String userId = jedis.get(key);
if (userId != null) {
return Integer.valueOf(userId);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
6.2 测试代码
public class ServiceTest {
UserService userService = new UserService();
@Test
public void testToken() throws InterruptedException {
String token = userService.createToken(1);
System.out.println(userService.validateToken(token));
Thread.sleep(10000);
System.out.println(userService.validateToken(token));
System.out.println(userService.validateToken(CommonUtil.generateUUID()));
}
}
7. 限制服务的访问次数
7.1 业务代码
public class UserService {
private JedisPool jedisPool = JedisFactory.getInstance().getJedisPool();
private Map<String, Integer> serviceMap = new HashMap<>();
public UserService() {
serviceMap.put("request", 20); // 普通请求
serviceMap.put("password", 5); // 输入密码
}
// 是否可用
public boolean isAvailable(String service, int userId) {
if (service == null || service.length() == 0) {
throw new IllegalArgumentException("参数为空!");
}
boolean result = false;
try (Jedis jedis = jedisPool.getResource()) {
String key = "times:" + service + ":" + userId;
String times = jedis.get(key);
if (times == null) {
result = true;
jedis.setex(key, 15, String.valueOf(serviceMap.get(service)));
} else {
result = Integer.valueOf(times) > 0;
}
jedis.decr(key);
return result;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
7.2 测试代码
public class ServiceTest {
UserService userService = new UserService();
@Test
public void testServiceAvailable() throws InterruptedException {
System.out.println(userService.isAvailable("password", 1));
System.out.println(userService.isAvailable("password", 1));
System.out.println(userService.isAvailable("password", 1));
System.out.println(userService.isAvailable("password", 1));
System.out.println(userService.isAvailable("password", 1));
System.out.println(userService.isAvailable("password", 1));
Thread.sleep(15 * 1000);
System.out.println(userService.isAvailable("password", 1));
}
}
8. 统计网站的独立访客
8.1 业务代码
public class DataService {
private JedisPool jedisPool = JedisFactory.getInstance().getJedisPool();
private SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
// 将指定的ip计入UV
public void recordUV(String ip) {
if (ip == null || ip.length() == 0) {
throw new IllegalArgumentException("参数为空!");
}
try (Jedis jedis = jedisPool.getResource()) {
String key = "uv:" + df.format(new Date());
jedis.pfadd(key, ip);
} catch (Exception e) {
e.printStackTrace();
}
}
// 统计指定日期范围内的UV
public long calculateUV(Date start, Date end) {
if (start == null || end == null) {
throw new IllegalArgumentException("参数为空!");
}
// 整理该日期范围内的key
List<String> keys = new ArrayList<>();
Calendar calendar = Calendar.getInstance();
calendar.setTime(start);
while (!calendar.getTime().after(end)) {
String key = "uv:" + df.format(calendar.getTime());
keys.add(key);
calendar.add(Calendar.DATE, 1);
}
// 合并统计结果
try (Jedis jedis = jedisPool.getResource()) {
return jedis.pfcount(keys.toArray(new String[0]));
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
}
8.2 测试代码
public class ServiceTest {
DataService dataService = new DataService();
@Test
public void testRevordUV() {
dataService.recordUV("139.9.119.1");
dataService.recordUV("139.9.119.2");
dataService.recordUV("139.9.119.3");
dataService.recordUV("139.9.119.1");
dataService.recordUV("139.9.119.2");
dataService.recordUV("139.9.119.3");
dataService.recordUV("139.9.119.1");
dataService.recordUV("139.9.119.2");
dataService.recordUV("139.9.119.3");
}
@Test
public void testCalculateUV() {
Date start = new Date("2021/11/01");
Date end = new Date("2021/11/30");
long uv = dataService.calculateUV(start, end);
System.out.println(uv);
}
}
9. 统计用户的在线天数
9.1 业务代码
public class DataService {
private JedisPool jedisPool = JedisFactory.getInstance().getJedisPool();
private SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
// 记录用户的在线状态
public void recordOnline(int userId) {
Calendar calendar = Calendar.getInstance();
int y = calendar.get(Calendar.YEAR);
int n = calendar.get(Calendar.DAY_OF_YEAR);
try (Jedis jedis = jedisPool.getResource()) {
String key = "online:" + y + ":" + userId;
jedis.setbit(key, n - 1, true);
} catch (Exception e) {
e.printStackTrace();
}
}
// 统计用户的在线状态
public int[] calculateOnline(int userId) {
Calendar calendar = Calendar.getInstance();
int y = calendar.get(Calendar.YEAR);
int n = calendar.getActualMaximum(Calendar.DAY_OF_YEAR);
try (Jedis jedis = jedisPool.getResource()) {
String key = "online:" + y + ":" + userId;
Pipeline pipeline = jedis.pipelined();
for (int i = 0; i < n; i++) {
pipeline.getbit(key, i);
}
List<Object> list = pipeline.syncAndReturnAll();
int[] flags = new int[n];
for (int i = 0; i < n; i++) {
flags[i] = Boolean.parseBoolean(list.get(i).toString()) ? 1 : 0;
}
return flags;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
9.2 测试代码
public class ServiceTest {
DataService dataService = new DataService();
@Test
public void testRecordOnline() {
dataService.recordOnline(1);
dataService.recordOnline(2);
dataService.recordOnline(3);
dataService.recordOnline(1);
dataService.recordOnline(2);
dataService.recordOnline(3);
dataService.recordOnline(1);
dataService.recordOnline(2);
dataService.recordOnline(3);
}
@Test
public void testCalculateOnline() {
int[] flags = dataService.calculateOnline(1);
for (int i = 0; i < flags.length; i++) {
System.out.print(flags[i] + " ");
if ((i + 1) % 20 == 0)
System.out.println();
}
}
}
10. 布隆过滤器
11. 延时队列(通过Jedis实现)
11.1 业务代码
public interface DelayQueueHandler {
void handle(String message);
}
public class DelayQueue {
private String key;
private DelayQueueHandler handler;
private JedisPool jedisPool = JedisFactory.getInstance().getJedisPool();
public DelayQueue(String service, DelayQueueHandler handler) {
this.key = "delay:queue" + service;
this.handler = handler;
}
// 加入队列
public void push(String message, long delayMiliseconds) {
if (message == null || message.length() == 0) {
return;
}
try (Jedis jedis = jedisPool.getResource()) {
jedis.zadd(key, System.currentTimeMillis() + delayMiliseconds, message);
} catch (Exception e) {
e.printStackTrace();
}
}
// 消费数据
public void consume() {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
System.out.println("尝试消费...");
try (Jedis jedis = jedisPool.getResource()) {
Set<String> set = jedis.zrangeByScore(
key, 0, System.currentTimeMillis(), 0, 100);
if (set != null && set.size() > 0) {
for (String message : set) {
handler.handle(message);
jedis.zrem(key, message);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}, 0, 3000);
}
public static void main(String[] args) {
DelayQueue queue = new DelayQueue("EndExam", new DelayQueueHandler() {
@Override
public void handle(String message) {
System.out.println("处理消息: " + message);
}
});
queue.consume();
queue.push("aaa", 10 * 1000);
queue.push("bbb", 10 * 1000);
queue.push("ccc", 10 * 1000);
queue.push("ddd", 20 * 1000);
queue.push("eee", 30 * 1000);
}
}
12. 延时队列(Redisson内置的延时队列)
12.1 业务代码
public class RedissonTest {
RedissonClient client = RedissonFactory.getInstance().getClusterClient();
@Test
public void testDelayQueue() {
// 目标队列
RBlockingDeque<String> queue = client.getBlockingDeque("queue:EndExam");
// 临时队列
RDelayedQueue<String> delayedQueue = client.getDelayedQueue(queue);
// 消费数据
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
System.out.println("正在消费...");
String message = (String) queue.take();
System.out.println("处理消息:" + message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
delayedQueue.offer("aaa", 10, TimeUnit.SECONDS);
delayedQueue.offer("bbb", 10, TimeUnit.SECONDS);
delayedQueue.offer("ccc", 10, TimeUnit.SECONDS);
delayedQueue.offer("ddd", 20, TimeUnit.SECONDS);
delayedQueue.offer("eee", 30, TimeUnit.SECONDS);
try {
Thread.sleep(1000 * 60);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
13. 分布式锁(通过Jedis实现)
13.1 业务代码
public class DistributedLock {
private JedisPool jedisPool = JedisFactory.getInstance().getJedisPool();
private String key, value;
private String unlockScript =
"if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
public DistributedLock(String lockName) {
this.key = lockName;
}
// 加锁,单位毫秒
public void lock(long expireTime) {
try (Jedis jedis = jedisPool.getResource()) {
while (true) {
value = CommonUtil.generateUUID();
String result = jedis.set(key, value, SetParams.setParams().nx().px(expireTime));
if ("OK".equals(result)) {
break;
}
Thread.sleep(100);
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 解锁
public void unlock() {
try (Jedis jedis = jedisPool.getResource()) {
List<String> keys = Arrays.asList(new String[]{key});
List<String> args = Arrays.asList(new String[]{value});
jedis.eval(unlockScript, keys, args);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
DistributedLock lock = new DistributedLock("lock:vote");
System.out.println("尝试加锁...");
lock.lock(1000 * 5);
System.out.println("加锁成功...");
try {
System.out.println("修改票数: " + Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
System.out.println("解锁成功...");
}
}
}).start();
}
}
}
14. 分布式锁(通过Redisson实现)
14.1 业务代码——单节点实现
public class RedissonTest {
RedissonClient client = RedissonFactory.getInstance().getClusterClient();
@Test
public void testDistributedLock() throws InterruptedException {
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
RLock rlock = client.getLock("lock:vote");
System.out.println("尝试加锁...");
rlock.lock(5000, TimeUnit.MILLISECONDS);
System.out.println("加锁成功...");
try {
System.out.println("修改票数: " + Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
} finally {
rlock.unlock();
System.out.println("解锁成功...");
}
}
}).start();
}
Thread.sleep(1000 * 15);
}
}
15. 分布式缓存(一)
15.1 业务代码
1. 模拟访问MySQL数据库
public class UserDao {
public User findUserById(int id) {
System.out.println("查询ID为" + id + "的用户...");
return new User(id, "user", "123", null, null);
}
public void updateUser(User user) {
System.out.println("修改ID为" + user.getId() + "的用户...");
}
public void deleteUser(int id) {
System.out.println("删除ID为" + id + "的用户...");
}
}
2. 模拟访问分布式缓存Redis
public class UserCache {
private JedisPool jedisPool = JedisFactory.getInstance().getJedisPool();
public void setUser(User user) {
try (Jedis jedis = jedisPool.getResource()) {
String key = "user:" + user.getId();
jedis.setex(key, 10, JSON.toJSONString(user));
} catch (Exception e) {
e.printStackTrace();
}
}
public User getUser(int userId) {
try (Jedis jedis = jedisPool.getResource()) {
String key = "user:" + userId;
return JSON.parseObject(jedis.get(key), User.class);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public void delUser(int userId) {
try (Jedis jedis = jedisPool.getResource()) {
String key = "user:" + userId;
jedis.del(key);
} catch (Exception e) {
e.printStackTrace();
}
}
}
3. 业务Service代码
public class UserService {
private UserCache userCache = new UserCache();
private UserDao userDao = new UserDao();
// 查询用户
public User findUser(int userId) {
User user = userCache.getUser(userId);
if (user == null) {
user = userDao.findUserById(userId);
userCache.setUser(user);
}
return user;
}
// 修改用户
public void modifyUser(User user) {
userDao.updateUser(user);
userCache.delUser(user.getId());
}
public static void main(String[] args) throws InterruptedException {
UserService userService = new UserService();
for (int i = 0; i < 5; i++) {
// 第1次访问,访问1次Mysql数据库,后4次访问缓存
User user = userService.findUser(1);
System.out.println(user);
}
userService.modifyUser(userService.findUser(1));
for (int i = 0; i < 5; i++) {
// 因为修改过,缓存中的数据被删除
// 第2次访问,访问1次Mysql数据库,后4次访问缓存
User user = userService.findUser(1);
System.out.println(user);
}
Thread.sleep(10000);
for (int i = 0; i < 5; i++) {
// 因为缓存过期,
// 第3次访问,访问1次Mysql数据库,后4次访问缓存
User user = userService.findUser(1);
System.out.println(user);
}
}
}
16. 分布式缓存(二)——内存淘汰策略
16.1 数据过期策略
16.2 内存淘汰策略
16.3 LRU算法
16.4 LFU算法
17. 分布式缓存(三)——缓存与数据库的同步问题
17.1 四种同步策略
17.2 更新缓存还是删除缓存?
17.3 先操作数据库还是缓存?
- 先删除缓存成功,再更新数据库失败:
1). A先删除缓存;
2). 然后A更新数据库失败;
3). B此时从缓存中获取数据;
4). 因为缓存中无数据,B从数据库中查询数据因为更新数据库操作失败因此查到的是旧数据;
5). B再将旧数据写回到缓存中。 - 先更新数据库成功,再删除缓存失败:
1). A先更新数据库;
2). 然后A删除缓存失败;
3). B此时从缓存中获取数据,获取到的数据是旧数据。 - 先删除缓存,再更新数据库:
1). A先删除缓存;
2). 然后B从缓存中获取数据
3). 因为缓存中没有数据,所以B从数据库中获取数据(旧数据);
4). B获取到数据后再将数据写入到缓存中;
5). 此时A再更新数据库。【缓存旧数据,数据库新数据】 - 先更新数据库,再删除缓存:
1). A先更新数据库;
2). B从缓存中获取数据(旧数据)
3). A再删除缓冲;【缓存无数据,数据库新数据】