redis의 bitset은 bit 단위의 값을 저장하고 읽을 때 유용하다. c나 c++ 쪽으로 개발했던 사람이라면 bit opersation은 일종의 flag 연산에 최고의 희열을 느꼈을 만하다.

http://redis.io/commands/setbit

사용법은 아주 간단하다.

setbit <key> <offset> <0 or 1>

 

image

 

jedis를 사용해서 테스트를 진행해본다.  잘 저장되고 잘 받을 수 있다.

private static void test1() throws Exception {
        // put
        List<Boolean> list = new ArrayList<Boolean>();
        Random rand = new Random();
        for (int i = 0 ; i < 100 ; i ++) {
            boolean bool = rand.nextBoolean();
            list.add(bool);
            jedis.setbit("key", i, bool);
        }
       
        // get
        int i = 0;
        Boolean getBoooleanValue;
        for (Boolean boool : list) {
            getBoooleanValue = jedis.getbit("key", i);
            if (boool.compareTo(getBoooleanValue) != 0) {
                throw new Exception("error !!");
            }
            i++;
        }
    }

 

내부를 잠깐 살펴보면..

 

jedis.setbit() 메소드는 key값, offset, boolean 을 넣도록 되어 있다.

public Long setbit(byte[] key, long offset, byte[] value)

public boolean setbit(String key, long offset, boolean value);

 

두 개의 메소드의 원형을 본다.

1. 2. key과 value가 byte[] 타입

BinaryJedis.java
public Long setbit(byte[] key, long offset, byte[] value) {
    client.setbit(key, offset, value);
    return client.getIntegerReply();
}

BinaryClient.java
public void setbit(byte[] key, long offset, byte[] value) {
    sendCommand(SETBIT, key, toByteArray(offset), value);
}

Connection.java
protected Connection sendCommand(final Command cmd, final byte[]... args) {
    connect();
    protocol.sendCommand(outputStream, cmd, args);
    pipelinedCommands++;
    return this;
}

Protocol.java
public void sendCommand(final RedisOutputStream os, final Command command,
        final byte[]... args) {
    sendCommand(os, command.raw, args);
}

private void sendCommand(final RedisOutputStream os, final byte[] command,
        final byte[]... args) {
    try {
        os.write(ASTERISK_BYTE);
        os.writeIntCrLf(args.length + 1);
        os.write(DOLLAR_BYTE);
        os.writeIntCrLf(command.length);
        os.write(command);
        os.writeCrLf();

        for (final byte[] arg : args) {
            os.write(DOLLAR_BYTE);
            os.writeIntCrLf(arg.length);
            os.write(arg);
            os.writeCrLf();
        }
    } catch (IOException e) {
        throw new JedisConnectionException(e);
    }
}

여기까지 write 하기..

read하는 부분이다.

Connection.java
public Long getIntegerReply() {
    flush();
    pipelinedCommands--;
    return (Long) protocol.read(inputStream);
}

Protoocol.java
public Object read(final RedisInputStream is) {
    return process(is);
}


private Object process(final RedisInputStream is) {
    try {
       byte b = is.readByte();
        if (b == MINUS_BYTE) {
            processError(is);
        } else if (b == ASTERISK_BYTE) {
            return processMultiBulkReply(is);
        } else if (b == COLON_BYTE) {
            return processInteger(is);
        } else if (b == DOLLAR_BYTE) {
            return processBulkReply(is);
        } else if (b == PLUS_BYTE) {
            return processStatusCodeReply(is);
        } else {
            throw new JedisConnectionException("Unknown reply: " + (char) b);
        }
    } catch (IOException e) {
        throw new JedisConnectionException(e);
    }
    return null;
}

 

2. key과 value가 각각 String, boolean 타입

public boolean setbit(String key, long offset, boolean value) {
client.setbit(key, offset, value);
return client.getIntegerReply() == 1;
}

Client.java
public void setbit(final String key, final long offset, final boolean value) {
    setbit(SafeEncoder.encode(key), offset, toByteArray(value ? 1 : 0));
}

public class SafeEncoder {
    public static byte[] encode(final String str) {
        try {
            if (str == null) {
                throw new JedisDataException(
                        "value sent to redis cannot be null");
            }
            return str.getBytes(Protocol.CHARSET);
        } catch (UnsupportedEncodingException e) {
            throw new JedisException(e);
        }
    }

public static final byte[] toByteArray(final int value) {
    return SafeEncoder.encode(String.valueOf(value));
}

 

잘 못 사용하는 경우에 “ERR bit is not an integer or out of range” 가 일어날 수 있다.

boolean 값으로 들어갈 0,1 을 제외한 다른 값이 들어오면 “ERR bit is not an integer or out of range”  Data Exception 발생이 된다. 서버에서 ‘–’ 마이너스 값을 던져준다.

jedis.setbit(new byte[] {0}, 1, toByteArray(3));
byte[] a = jedis.get(new byte[]{0});
System.out.println(a[0]);

Exception in thread "main" redis.clients.jedis.exceptions.JedisDataException: ERR bit is not an integer or out of range
    at redis.clients.jedis.Protocol.processError(Protocol.java:54)
    at redis.clients.jedis.Protocol.process(Protocol.java:61)
    at redis.clients.jedis.Protocol.read(Protocol.java:122)
    at redis.clients.jedis.Connection.getIntegerReply(Connection.java:178)
    at redis.clients.jedis.BinaryJedis.setbit(BinaryJedis.java:2965)
    at BitSetTest.main(BitSetTest.java:14)

Posted by '김용환'
,