文章目录
01.缓存数据(Cache)
将数据库查询结果缓存到Redis,减轻数据库压力
示例:在电商网站中,可以将热门商品的信息存储在Redis中,当用户访问这些商品时,首先从Redis中读取,如果Redis中没有,再从数据库中读取并更新到Redis中。
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String PRODUCT_CACHE_PREFIX = "product:";
public Product getProductById(Long id) {
String cacheKey = PRODUCT_CACHE_PREFIX + id;
// 1. 先查缓存
Product product = (Product) redisTemplate.opsForValue().get(cacheKey);
if (product != null) {
return product;
}
// 2. 缓存没有则查数据库
product = productRepository.findById(id).orElse(null);
if (product != null) {
// 3. 写入缓存,设置过期时间
redisTemplate.opsForValue().set(cacheKey, product, 30, TimeUnit.MINUTES);
}
return product;
}
}
02.布式锁(Distributed Lock)
在分布式系统中,Redis可以用于实现分布式锁,可以在分布式系统中协调多节点对共享资源的访问,确保操作的原子性。
示例:分布式系统中用户购买商品,同一时间只能一个用户创建订单。
@Service
public class OrderService {
@Autowired
private StringRedisTemplate redisTemplate;
private static final String LOCK_PREFIX = "lock:";
private static final long LOCK_EXPIRE = 30; // 30秒
public boolean createOrder(Long productId, Long userId) {
String lockKey = LOCK_PREFIX + "order_" + productId;
String requestId = UUID.randomUUID().toString();
try {
// 尝试获取锁
Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, LOCK_EXPIRE, TimeUnit.SECONDS);
if (Boolean.TRUE.equals(locked)) {
// 获取锁成功,执行业务逻辑
return doCreateOrder(productId, userId);
}
return false;
} finally {
// 释放锁 - 使用Lua脚本保证原子性
String script =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
redisTemplate.execute(
new DefaultRedisScript<>(script, Long.class),
Collections.singletonList(lockKey),
requestId
);
}
}
}
03.计数器(Counter)
Redis的原子增减操作非常适合用于计数器和排行榜应用,如社交媒体的点赞数、阅读数、排名等。
示例:实现文章阅读量、商品点击量等统计
@Service
public class ArticleService {
@Autowired
private StringRedisTemplate redisTemplate;
private static final String VIEW_COUNT_PREFIX = "article:view:";
// 增加阅读量
public void incrementViewCount(Long articleId) {
String key = VIEW_COUNT_PREFIX + articleId;
redisTemplate.opsForValue().increment(key);
}
// 获取阅读量
public Long getViewCount(Long articleId) {
String key = VIEW_COUNT_PREFIX + articleId;
String count = redisTemplate.opsForValue().get(key);
return count == null ? 0L : Long.parseLong(count);
}
// 批量获取阅读量
public Map<Long, Long> getBatchViewCounts(List<Long> articleIds) {
List<String> keys = articleIds.stream().map(id -> VIEW_COUNT_PREFIX + id).collect(Collectors.toList());
List<String> counts = redisTemplate.opsForValue().multiGet(keys);
Map<Long, Long> result = new HashMap<>();
for (int i = 0; i < articleIds.size(); i++) {
Long articleId = articleIds.get(i);
String count = counts.get(i);
result.put(articleId, count == null ? 0L : Long.parseLong(count));
}
return result;
}
}
04.排行榜(Leaderboard)
Redis的原子增减操作非常适合用于计数器和排行榜应用,如社交媒体的点赞数、阅读数、排名等。
示例:实现商品销量排行、游戏积分排行等
@Service
public class RankingService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String SALES_RANK_KEY = "product:sales:rank";
// 更新商品销量
public void updateProductSales(Long productId, int sales) {
redisTemplate.opsForZSet().add(SALES_RANK_KEY, productId, sales);
}
// 获取销量前N的商品
public List<Long> getTopSalesProducts(int topN) {
Set<Object> productIds = redisTemplate.opsForZSet().reverseRange(SALES_RANK_KEY, 0, topN - 1);
return productIds.stream().map(id -> Long.parseLong(id.toString())).collect(Collectors.toList());
}
// 获取商品排名
public Long getProductRank(Long productId) {
// ZREVRANK返回的是从0开始的排名
Long rank = redisTemplate.opsForZSet().reverseRank(SALES_RANK_KEY, productId);
return rank == null ? null : rank + 1; // 转换为1-based排名
}
// 获取商品销量
public Double getProductSales(Long productId) {
return redisTemplate.opsForZSet().score(SALES_RANK_KEY, productId);
}
}
05.消息队列(Message Queue)
Redis支持发布/订阅模式,可以用作轻量级的消息队列系统,用于异步任务处理、事件处理等。
示例:实现简单的消息队列系统
@Service
public class MessageQueueService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String QUEUE_KEY = "message:queue";
// 发送消息
public void sendMessage(String message) {
redisTemplate.opsForList().rightPush(QUEUE_KEY, message);
}
// 接收消息 - 阻塞式
public String receiveMessage() throws InterruptedException {
return (String) redisTemplate.opsForList().leftPop(QUEUE_KEY, 30, TimeUnit.SECONDS);
}
// 批量发送消息
public void batchSendMessages(List<String> messages) {
redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
for (String message : messages) {
connection.listCommands().rPush(QUEUE_KEY.getBytes(), message.getBytes());
}
return null;
});
}
}
06.限流(Rate Limiting)
Redis 适合用于限流(Rate Limiting)场景。限流的目的是控制某个操作在特定时间内的访问频率,比如 API 请求、短信发送、登录尝试等。Redis 的原子操作和高效性能使其成为实现限流的理想工具。
示例:实现API访问限流
@Service
public class RateLimitService {
@Autowired
private StringRedisTemplate redisTemplate;
private static final String RATE_LIMIT_PREFIX = "rate_limit:";
/**
* 限流检查
* @param key 限流key(如用户ID或IP)
* @param limit 时间窗口内允许的最大请求数
* @param windowInSeconds 时间窗口大小(秒)
* @return true-允许访问 false-拒绝访问
*/
public boolean allowRequest(String key, int limit, int windowInSeconds) {
String redisKey = RATE_LIMIT_PREFIX + key;
// 使用Lua脚本保证原子性
String script =
"local current = redis.call('incr', KEYS[1])\n" +
"if current == 1 then\n" +
" redis.call('expire', KEYS[1], ARGV[1])\n" +
"end\n" +
"return current <= tonumber(ARGV[2])";
Long result = redisTemplate.execute(
new DefaultRedisScript<>(script, Long.class),
Collections.singletonList(redisKey),
String.valueOf(windowInSeconds),
String.valueOf(limit)
);
return result != null && result == 1;
}
}
07.会话存储(Session Storage)
在Web应用中,Redis常用于存储用户会话信息,如登录状态、购物车内容等。由于其快速的读写速度,Redis非常适合这种需要频繁访问和更新的数据。
示例:分布式系统中的会话存储
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800) // 30分钟过期
public class RedisSessionConfig {
// Spring Session会自动配置Redis连接
}
// 使用示例
@RestController
public class UserController {
@PostMapping("/login")
public String login(@RequestParam String username, HttpSession session) {
// 存储用户信息到session
session.setAttribute("username", username);
return "登录成功";
}
@GetMapping("/userinfo")
public String getUserInfo(HttpSession session) {
// 从session获取用户信息
String username = (String) session.getAttribute("username");
return "当前用户: " + (username != null ? username : "未登录");
}
}
08.地理位置(Geo)
Redis支持地理空间数据,可以用于构建地理位置应用,如附近的人、地点推荐等功能。
示例:实现附近的人、附近的商家等功能
@Service
public class LocationService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String GEO_KEY = "shop:locations";
// 添加商家位置
public void addShopLocation(Long shopId, double lng, double lat) {
redisTemplate.opsForGeo().add(GEO_KEY, new Point(lng, lat), shopId.toString());
}
// 获取附近商家
public List<Long> getNearbyShops(double lng, double lat, double radius) {
Circle within = new Circle(new Point(lng, lat), new Distance(radius, Metrics.KILOMETERS));
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs
.newGeoRadiusArgs()
.includeDistance()
.sortAscending()
.limit(10);
GeoResults<RedisGeoCommands.GeoLocation<Object>> results = redisTemplate.opsForGeo()
.radius(GEO_KEY, within, args);
return results.getContent().stream()
.map(geoResult -> Long.parseLong(geoResult.getContent().getName().toString()))
.collect(Collectors.toList());
}
// 计算两个商家距离
public Distance calculateDistance(Long shopId1, Long shopId2) {
List<String> ids = Arrays.asList(shopId1.toString(), shopId2.toString());
return redisTemplate.opsForGeo()
.distance(GEO_KEY, ids.get(0), ids.get(1), Metrics.KILOMETERS);
}
}
09. 发布订阅(Pub/Sub)
示例:实现实时消息通知、事件广播等
// 配置消息监听容器
@Configuration
public class RedisPubSubConfig {
@Bean
public RedisMessageListenerContainer redisMessageListenerContainer(
RedisConnectionFactory connectionFactory,
MessageListenerAdapter orderEventListenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(orderEventListenerAdapter,
new ChannelTopic("order:events"));
return container;
}
@Bean
public MessageListenerAdapter orderEventListenerAdapter(OrderEventListener listener) {
return new MessageListenerAdapter(listener, "handleMessage");
}
}
// 消息监听器
@Component
public class OrderEventListener {
public void handleMessage(OrderEvent event) {
switch (event.getType()) {
case CREATED:
handleOrderCreated(event);
break;
case PAID:
handleOrderPaid(event);
break;
// 其他事件处理...
}
}
private void handleOrderCreated(OrderEvent event) {
// 处理订单创建事件
}
}
// 消息发布服务
@Service
public class OrderEventPublisher {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public void publishOrderEvent(OrderEvent event) {
redisTemplate.convertAndSend("order:events", event);
}
}