[TROUBLE_SHOOTING] REDIS - WRONGTYPE Operation against a key holding the wrong kind of value
발생 상황
Redis 적용중에 다음과 같은 에러가 발생했다.
io.lettuce.core.rediscommandexecutionexception: wrongtype operation against a key holding the wrong kind of value’
해석해보면 타입이 안맞아서 그렇다고 한다. 그래서 해당 에러를 검색해봤더니 주로 지정된 타입에 다른 명령어로 조회를 요청할 시 일어난다고 한다. 예를 들면 “String” 자료형이면 “get” 으로 호출해야 된다. “List” 자료형이면 “RPOP”, “LPOP” 으로 호출해야된다. 그러니 “List” 자료형인데 “get” 으로 호출하면 해당 에러가 발생하는것이다.
발생 원인
하지만 내 코드에서는 자료형이 잘못되는 경우가 없었다. 코드는 다음과 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public void countView(Long id) {
String info = "view";
String redisKey = id.toString() + info;
String userKey = userService.getAuthenticatiedUser().getId().toString() + info;
if (!redisDao.getValuesList(userKey).contains(redisKey)) {
redisDao.setValuesList(userKey, redisKey);
int view = NumberUtils.toInt(redisDao.getValues(redisKey));
redisDao.setValues(redisKey, String.valueOf(view + 1));
}
}
public int getView(Long id) {
String info = "view";
String redisKey = id.toString() + info;
return NumberUtils.toInt(redisDao.getValues(redisKey));
}
public void setValues(String key, String data) {
ValueOperations<String, String> values = redisTemplate.opsForValue();
values.set(key, data);
}
public void setValuesList(String key, String data) {
redisTemplate.opsForList().rightPushAll(key, data);
}
public List<String> getValuesList(String key) {
Long len = redisTemplate.opsForList().size(key);
return len == 0 ? new ArrayList<>() : redisTemplate.opsForList().range(key, 0, len - 1);
}
public String getValues(String key) {
ValueOperations<String, String> values = redisTemplate.opsForValue();
return values.get(key);
}
코드는 간단히 설명하자면 조회수 로직이다. 조회수를 올려주고, 만약 중복된 사용자면 조회수를 올리지않은 필터과정이 있다. 결과적으로 발생이유는 redisKey
의 키값이 “view1” 이고, userKey
의 키값이 “view1” 으로 서로 동일한 이름이 키값인게 문제였다. 해당 키값으로 저장되는 자료형이 “String” 이든 “List” 이든 고유한 키값 이어야 된다! 에러가 발생한 원인은 redisDao.setValuesList(userKey, redisKey);
여기서 “List” 자료형이 “view1” 키로 저장되어서 나중에 getView()
함수의 return NumberUtils.toInt(redisDao.getValues(redisKey));
에서 “view1” 키로 “String” 값을 가져올려고 해서 자료형이 틀렸다고 에러가 발생한것이었다.
해결
키값이 동일하지 않게하기위해 코드를 다음과 같이 수정했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
public void countView(Long id) {
String info = "view";
String filter = "filter";
String redisKey = id.toString() + info;
String userKey = userService.getAuthenticatiedUser().getId().toString() + info + filter;
if (!redisDao.getValuesList(userKey).contains(redisKey)) {
redisDao.setValuesList(userKey, redisKey);
int view = NumberUtils.toInt(redisDao.getValues(redisKey));
redisDao.setValues(redisKey, String.valueOf(view + 1));
}
}
이렇게하면redisKey
와 userKey
가 중복될일이 없다. 하지만 나는 이렇게 수정하고도 에러가 발생했는데 그 이유는 redis 또한 DB이기에 키가 저장된채로 남아있었다. 그래서 redis-cli 에 들어가 key를 전부 지워주고서야 정상적으로 작동했다.
redis 잘 사용하자..!
Reference
https://hnev.tistory.com/11
https://pinggoopark.tistory.com/281
https://wildeveloperetrain.tistory.com/243