首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Spring Data Redis 整合實戰:redisTemplate 配置與 Lua 腳本原子操作

     編輯:Java 綜合教學

Maven 依賴引入

項目已引入其他 Spring 依賴,為避免版本衝突,排除傳遞性依賴:

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>1.6.2.RELEASE</version>
    <exclusions>
        <exclusion><artifactId>spring-context</artifactId><groupId>org.springframework</groupId></exclusion>
        <exclusion><artifactId>spring-aop</artifactId><groupId>org.springframework</groupId></exclusion>
        <exclusion><artifactId>spring-context-support</artifactId><groupId>org.springframework</groupId></exclusion>
        <exclusion><artifactId>spring-tx</artifactId><groupId>org.springframework</groupId></exclusion>
        <exclusion><artifactId>spring-core</artifactId><groupId>org.springframework</groupId></exclusion>
        <exclusion><artifactId>slf4j-api</artifactId><groupId>org.slf4j</groupId></exclusion>
    </exclusions>
</dependency>

Spring XML 配置詳解

1. 連接池與連接工廠

<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
    <property name="maxIdle" value="${redis.maxIdle}" />
    <property name="maxActive" value="${redis.maxActive}" />
    <property name="maxWait" value="${redis.maxWait}" />
    <property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean>

<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
      p:hostName="${redis.url}" p:port="${redis.port}"
      p:password="${redis.password}" p:pool-config-ref="poolConfig"/>

2. 序列化策略

<bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
<bean id="jsonSerializer" class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>

Key/HashKey 使用 String 序列化,Value/HashValue 使用 JSON 序列化,兼容可讀性與複雜對象存儲。

3. redisTemplate 定義

<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
      p:connection-factory-ref="jedisConnectionFactory"
      p:keySerializer-ref="stringRedisSerializer"
      p:hashKeySerializer-ref="stringRedisSerializer"
      p:valueSerializer-ref="jsonSerializer"
      p:hashValueSerializer-ref="jsonSerializer"/>

Lua 腳本整合:保證操作原子性

腳本聲明(DefaultRedisScript)

<bean id="updateAvailableSavingsCard" class="org.springframework.data.redis.core.script.DefaultRedisScript">
    <property name="location" value="classpath:META-INF/lua/updateAvailableSavingsCard.lua"/>
    <property name="resultType" value="org.meibaobao.ecos.basecomponent.common.Result"/>
</bean>
<bean id="initAvailableSavingsCard" class="org.springframework.data.redis.core.script.DefaultRedisScript">
    <property name="location" value="classpath:META-INF/lua/initAvailableSavingsCard.lua"/>
    <property name="resultType" value="java.lang.Boolean"/>
</bean>

Lua 腳本示例:可用儲蓄卡庫存原子增減

-- updateAvailableSavingsCard.lua
redis.call('INCRBY', KEYS[1], ARGV[1])
local current = redis.call('GET', KEYS[1])
local result = {}
result["@class"] = "org.meibaobao.ecos.basecomponent.common.Result"
if tonumber(current) < 0 then
    redis.call('DECRBY', KEYS[1], ARGV[1])
    current = redis.call('GET', KEYS[1])
    result["success"] = false
    result["data"] = current
    local encodestr = cjson.encode(result)
    print(encodestr)
    return encodestr
end
result["success"] = true
result["data"] = current
local encodestr = cjson.encode(result)
print(encodestr)
return encodestr

腳本邏輯:庫存增減後檢查是否小於 0,若是則回滾並返回失敗,否則返回成功。整個過程在 Redis 服務端原子執行,避免併發競態。

屬性配置文件

#redis
redis.url=10.72.82.124
redis.port=6379
redis.password=
redis.maxIdle=200
redis.maxActive=1024
redis.maxWait=1000
redis.testOnBorrow=true

業務服務層調用

@Service
public class StoredCardInventoryServiceImpl {
    @Resource(name = "updateAvailableSavingsCard")
    private RedisScript<Result> updateAvailableSavingsCard;

    @Resource(name = "initAvailableSavingsCard")
    private RedisScript<Boolean> initAvailableSavingsCard;

    @Autowired
    private RedisTemplate<String, Integer> redisTemplate;

    public static final String PREFIX = "availableStotredCard_";

    public Result availableStotredCardInventory(String schemeNo, int num) {
        return redisTemplate.execute(updateAvailableSavingsCard,
                Collections.singletonList(PREFIX + schemeNo), num);
    }

    public Boolean initAvailableStotredCardInventory(String schemeNo, int num) {
        return redisTemplate.execute(initAvailableSavingsCard,
                Collections.singletonList(PREFIX + schemeNo), num);
    }
}

JUnit 單元測試驗證

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:development/xml/spring/applicationContext.xml"})
public class StotredCardInventoryServiceImplTest {
    @Resource private StoredCardInventoryServiceImpl service;
    @Autowired private RedisTemplate<String, Object> redisTemplate;

    @Test
    public void test() throws InterruptedException {
        System.out.println(service.initAvailableStotredCardInventory("test1111112", -10));
        System.out.println(service.initAvailableStotredCardInventory("test1111112", -10));
        redisTemplate.opsForValue().set("test99999", "123asd");
        System.out.println(redisTemplate.opsForValue().get("test99999"));

        // Hash 操作驗證
        String key = "hashtest11";
        redisTemplate.opsForHash().put(key, "name1", "11");
        redisTemplate.opsForHash().put(key, "name2", "22");
        redisTemplate.opsForHash().put(key, "name3", "33");
        System.out.println(redisTemplate.opsForHash().get(key, "name2"));
        redisTemplate.opsForHash().delete(key, "name2");
        System.out.println(redisTemplate.opsForHash().get(key, "name2"));
        System.out.println(redisTemplate.opsForHash().get(key, "name1"));
        Map<Object, Object> opsMap = redisTemplate.opsForHash().entries(key);
        if (opsMap != null) {
            opsMap.forEach((k, v) -> System.out.println(k + "," + v));
        }
    }
}

總結

通過 Spring Data Redis 的 redisTemplate 統一序列化策略,結合 DefaultRedisScript 載入 Lua 腳本,可實現複雜業務邏輯的原子化執行,有效解決高併發下的數據一致性問題。實戰中靈活運用 Hash、String 等數據結構,配合管道與事務,可進一步提升 Redis 操作效能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved