Databases 10 min read

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.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering Redis with Spring Boot: Pub/Sub, Transactions, Pipelines, and Lua Locks

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

redisSpring Bootdistributed-lockLuaTransactionspub/subpipelining
Spring Full-Stack Practical Cases
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.