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.
<code>@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())) ;
}
}
</code> <code>@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;
}
}
</code> <code>@Component
public class MessageSender {
@Autowired
private StringRedisTemplate stringRedisTemplate;
public void sendMessage(String channel, String message) {
stringRedisTemplate.convertAndSend(channel, message);
}
}
</code>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.
<code>@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) ;
}
}
}
</code>Declarative transaction support can be enabled by configuring the template:
<code>@Bean
StringRedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate(redisConnectionFactory) ;
// 手动开启事务功能
template.setEnableTransactionSupport(true);
return template;
}
</code> <code>@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) ;
}
</code>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.
<code>@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 ;
}
}
</code>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):
<code>-- 判断锁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 ;
</code>Unlock script (unlock.lua):
<code>-- 判断锁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 ;
</code>Java wrapper for the lock:
<code>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) ;
}
}
</code>Usage example:
<code>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() ;
}
}
}
</code>These code snippets illustrate how to integrate Redis features into a Spring Boot application, covering messaging, transactions, pipelining, and distributed locking.
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.