Mastering Redis with Spring Boot: Pub/Sub, Transactions, Pipelines, and Lua Locks
This guide demonstrates how to use Spring Boot 2.7 with Redis 6.2 to implement Pub/Sub messaging, transactional operations, pipelined commands, and Lua‑based distributed locks, providing code examples, configuration details, and explanations of each feature’s advantages and limitations.
1. Redis Pub/Sub
Spring Data Redis offers dedicated messaging integration similar to JMS in the Spring Framework. Pub/Sub consists of publishing and subscribing. The RedisTemplate class is used for message production. Drawbacks: no acknowledgment mechanism and messages are not persisted, so they can be lost if no consumer is available or if Redis restarts.
@Component
public class MsgMessageListener implements MessageListener {
@Override
public void onMessage(Message message, byte[] pattern) {
System.out.printf("从通道【%s】, 接收到消息: %s%n", new String(message.getChannel()), new String(message.getBody())) ;
}
} @Configuration
public class MessageReceiverConfig {
@Bean
RedisMessageListenerContainer redisMessageListenerContainer(
RedisConnectionFactory connectionFactory,
MessageListener listener) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
// 这里监听了2个通道msg和chat
container.addMessageListener(listener, Arrays.asList(ChannelTopic.of("msg"), ChannelTopic.of("chat"))) ;
return container;
}
} @Component
public class MessageSender {
@Autowired
private StringRedisTemplate stringRedisTemplate;
public void sendMessage(String channel, String message) {
stringRedisTemplate.convertAndSend(channel, message);
}
}2. Redis Transactions
Redis supports transactions via MULTI, EXEC, and DISCARD. Spring Data Redis provides SessionCallback for programmatic transaction handling, but it cannot guarantee that all operations run on the same connection.
@Service
public class RedisTransactionService {
private final StringRedisTemplate stringRedisTemplate ;
public RedisTransactionService(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate ;
}
public void multiOperator() {
List<Object> txResults = stringRedisTemplate.execute(new SessionCallback<List<Object>>() {
public List<Object> execute(RedisOperations operations) throws DataAccessException {
operations.multi();
operations.opsForHash().put("users:666", "name", "张三") ;
operations.opsForValue().set("id", "666") ;
return operations.exec() ;
}
});
for (Object ret : txResults) {
System.out.println(ret) ;
}
}
}Declarative transaction support can be enabled by configuring the template:
@Bean
StringRedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate(redisConnectionFactory) ;
// 手动开启事务功能
template.setEnableTransactionSupport(true);
return template;
} @Transactional
public void multiOperator2() {
stringRedisTemplate.opsForValue().set("oo", "xx") ;
Set<String> keys = stringRedisTemplate.keys("*") ;
// 输出[]。读操作必须在空闲(不感知事务)连接上运行
System.out.println(keys) ;
String value = stringRedisTemplate.opsForValue().get("oo") ;
// 返回null,因为在事务中设置的值不可见
System.out.println(value) ;
}3. Pipelines
Redis pipelines allow sending multiple commands without waiting for replies, improving performance for bulk operations. Spring Data Redis provides executePipelined to run a RedisCallback or SessionCallback in pipeline mode.
@Service
public class RedisPipeliningService {
private final StringRedisTemplate stringRedisTemplate ;
public RedisPipeliningService(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate ;
}
public List<Object> pipe() {
List<Object> results = stringRedisTemplate.executePipelined(
new RedisCallback<Object>() {
public Object doInRedis(RedisConnection connection) throws DataAccessException {
StringRedisConnection stringRedisConn = (StringRedisConnection)connection;
for(int i = 0; i < 10; i++) {
stringRedisConn.rPush("i" + i, String.valueOf(i)) ;
}
return null;
}
});
return results ;
}
}Sample pipeline output is shown in the image below:
4. Redis Lua Scripts and Distributed Locks
Redis 2.6+ supports Lua scripts via EVAL and EVALSHA. Spring Data Redis offers high‑level abstractions for script execution, handling serialization and script caching.
Lock acquisition script (lock.lua):
-- 判断锁key是否存在
if (redis.call('EXISTS', KEYS[1]) == 0) then
-- 不存在
redis.call('HINCRBY', KEYS[1], ARGV[1], 1) ;
redis.call('PEXPIRE', KEYS[1], 300000) ;
return 1;
end ;
-- 判断hash中是否存在ARGV[1]字段(该字段用来统计重入次数,一般使用threadId唯一标识)
if (redis.call('HEXISTS', KEYS[1], ARGV[1]) == 1) then
redis.call('HINCRBY', KEYS[1], ARGV[1], 1) ;
redis.call('PEXPIRE', KEYS[1], 300000) ;
return 1;
end ;
return 0 ;Unlock script (unlock.lua):
-- 判断锁key是否存在
if (redis.call('EXISTS', KEYS[1]) == 0) then
return nil ;
end ;
local counter = redis.call('HINCRBY', KEYS[1], ARGV[1], -1) ;
if (counter > 0) then
redis.call('PEXPIRE', KEYS[1], 30000) ;
return 1 ;
else
redis.call('DEL', KEYS[1]) ;
return 1 ;
end ;
return 0 ;Java wrapper for the lock:
public interface PLock {
boolean lock();
void unlock();
}
public class ProductRedisLock implements PLock {
private StringRedisTemplate stringRedisTemplate ;
private String key ;
private String id ;
public ProductRedisLock(String key, StringRedisTemplate stringRedisTemplate) {
this.key = key ;
this.id = key.hashCode() + ":" ;
this.stringRedisTemplate = stringRedisTemplate ;
}
@Override
public boolean lock() {
long threadId = Thread.currentThread().getId() ;
return this.stringRedisTemplate.execute(RedisScript.of(new ClassPathResource("lock.lua"), Boolean.class),
Arrays.asList(this.key), this.id + threadId) ;
}
@Override
public void unlock() {
long threadId = Thread.currentThread().getId() ;
this.stringRedisTemplate.execute(RedisScript.of(new ClassPathResource("unlock.lua"), Boolean.class),
Arrays.asList(this.key), this.id + threadId) ;
}
}Usage example:
public void updateProduct(Long id) {
String key = "lock:product:" + id ;
PLock lock = new ProductRedisLock(key, stringRedisTemplate) ;
if (lock.lock()) {
try {
System.out.println("更新商品:" + id) ;
this.configProduct(id) ;
} finally {
lock.unlock() ;
}
}
}These code snippets illustrate how to integrate Redis features into a Spring Boot application, covering messaging, transactions, pipelining, and distributed locking.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
