前言: 業(yè)務(wù)中胳蛮,有個hash value是json右核,多個更新操作可能會造成覆蓋慧脱,所以,考慮加個version字段蒙兰,通過樂觀鎖來重試解決覆蓋的問題磷瘤。記錄下實現(xiàn)芒篷。
相關(guān)知識:EVAL – Redis
代碼:
public class RedisAtomicCommit extends RedisTemplate<String,Object>{
RedisScript<Boolean> redisScript;
//lua腳本
String script = "local jstr = redis.call('HGET', KEYS[1],KEYS[2])\n" +
"if jstr == false\n" +
"then redis.call('HSET', KEYS[1],KEYS[2],ARGV[1])\n" +
"\treturn true\n" +
"end\n" +
"local json = cjson.decode(jstr)\n" +
"if tostring(json.version) == ARGV[2]\n" +
"then \n" +
"\tredis.call('HSET', KEYS[1],KEYS[2],ARGV[1])\n" +
"\treturn true\n" +
"end\n" +
"return false ";
public RedisAtomicCommit() {
redisScript = RedisScript.of(script, Boolean.class);
}
public Boolean checkAndSet(String key, String hashKey, Entity value){
Long version = value.getVersion();
value.setVersion(System.currentTimeMillis());
return execute(redisScript, asList(key,hashKey),value,version);
}
}
必要說明
redis lua腳本里有很多反直覺的問題,這里做下解釋采缚。
if jstr == false
這一行其實是判空针炉,照理說應(yīng)該是 == nil ,但是實測不行扳抽,必須用false
if tostring(json.version) == ARGV[2]\n"
這一行就比較好理解篡帕,主要是取出來的是數(shù)字,需要做轉(zhuǎn)換
其他的贸呢,關(guān)于KEYS[]和ARGV[]镰烧,對應(yīng)的是execute()方法的list參數(shù)和object...參數(shù),具體可以看redis官方文檔
后記
最后項目中沒用這種方案楞陷,因為確實比較慢怔鳖,每次都要解析json,推薦還是用hashkey直接存數(shù)據(jù)固蛾,不要存json结执,或者用repository,封裝的很好艾凯。
單元測試
@SpringJUnitConfig
public class RedisAtomicCommitTest {
@Configuration
static class Config extends RedisConfig {
@Bean
RedisConnectionFactory redisConnectionFactory(){
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration("127.0.0.1", 6178);
config.setPassword("Data");
return new LettuceConnectionFactory(config);
}
}
@Test
public void test(@Autowired RedisTemplate<String,Object> redisTemplate){
String key = "activity:test";
String hKey = "test-DrawUserDto";
DrawUserDto value = new DrawUserDto();
System.out.println(JSON.toJSONString(value));
System.out.printf("if the key %s exist %s %n",hKey,redisTemplate.opsForHash().get(key,hKey));
redisTemplate.opsForHash().delete(key, hKey);
Assertions.assertTrue(redisTemplate.checkAndSet(key, hKey, value));
Long current = value.getVersion();
System.out.println("current is :" + current);
Assertions.assertTrue(redisTemplate.checkAndSet(key, hKey, value));
Assertions.assertEquals(redisTemplate.opsForHash().get(key,hKey),value);
value.setVersion(System.currentTimeMillis());
Assertions.assertFalse(redisTemplate.checkAndSet(key, hKey, value));
List<DrawUserDto> list = new ArrayList<>();
list.add(value);
redisTemplate.opsForHash().put(key,"test-list",list);
Assertions.assertEquals(redisTemplate.opsForHash().get(key,"test-list"),list);
}