Post

[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));
        }
    }

이렇게하면redisKeyuserKey 가 중복될일이 없다. 하지만 나는 이렇게 수정하고도 에러가 발생했는데 그 이유는 redis 또한 DB이기에 키가 저장된채로 남아있었다. 그래서 redis-cli 에 들어가 key를 전부 지워주고서야 정상적으로 작동했다.

redis 잘 사용하자..!

Reference

https://hnev.tistory.com/11
https://pinggoopark.tistory.com/281
https://wildeveloperetrain.tistory.com/243

This post is licensed under CC BY 4.0 by the author.