redis支持5种数据类型,分别是string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。整型值和布尔值并不在其中。存储整型和布尔值时,redis会将其转换为字符串
127.0.0.1:6379> SET test_key 1
OK
127.0.0.1:6379> GET test_key
"1"
127.0.0.1:6379> SET test_key True
OK
127.0.0.1:6379> GET test_key
"True"
127.0.0.1:6379> SET test_key TRUE
OK
127.0.0.1:6379> GET test_key
"TRUE"
127.0.0.1:6379> SET test_key true
OK
127.0.0.1:6379> GET test_key
"true"
但是当我们使用了django-redis
之后发现,我们可以正常存取整型和布尔值,这是怎么做的呢?答案自然是在源码中,直觉告诉我们,set函数
里一定做了什么事情。
# django_redis.client.default.py
def set(self, key, value, timeout=DEFAULT_TIMEOUT, version=None, client=None, nx=False, xx=False):
"""
Persist a value to the cache, and set an optional expiration time.
Also supports optional nx parameter. If set to True - will use redis setnx instead of set.
"""
if not client:
client = self.get_client(write=True)
nkey = self.make_key(key, version=version)
nvalue = self.encode(value)
......
set
方法中调用了类的encode
的方法对value
进行了处理,我们再看encode
:
django_redis.client.default.py
def encode(self, value): """ Encode the given value. """
if isinstance(value, bool) or not isinstance(value, integer_types):
value = self._serializer.dumps(value)
value = self._compressor.compress(value)
return value
return value
看到了吧,如果是布尔值
,那么要调用一个dumps
方法,我们找到这个dumps
方法:
django_redis.serializers.pickle.py
class PickleSerializer(BaseSerializer): ......
def dumps(self, value):
return pickle.dumps(value, self._pickle_version)
def loads(self, value):
return pickle.loads(force_bytes(value))
原来是用了pickle来做了序列化,这样拿出来的时候调用pickle.loads
方法,就可以还原为原来的对象了。这也是django-redis
支持存储任意python
对象的原因,pickle帮了大忙。
至于int
的存取就简单了,我们从get
方法看过去:
django_redis.client.default.py
def get(self, key, default=None, version=None, client=None): """ Retrieve a value from the cache. Returns decoded value if key is found, the default if not. """ if client is None: client = self.get_client(write=False)
key = self.make_key(key, version=version)
try:
value = client.get(key)
except _main_exceptions as e:
raise ConnectionInterrupted(connection=client, parent=e)
if value is None:
return default
return self.decode(value)
可以看到对从redis
中取出来的值调用了decode
方法:
django_redis.client.default.py
def decode(self, value):
"""
Decode the given value.
"""
try:
value = int(value)
except (ValueError, TypeError):
try:
value = self._compressor.decompress(value)
except CompressorError:
# Handle little values, chosen to be not compressed pass
value = self._serializer.loads(value) return value
一目了然了吧,总结的话就是:redis
存的时候还是存的字符串,但是取出来的时候会尝试去int
一下。
大家可能会有这样的疑问,如果值是'123'
这样的字符串怎么办呢?这样并不会有问题啦,因为字符串存到数据库之前会先做pickle.dumps
,这样就可以区分开数字转换成的字符串和真实的字符串了。
另外,将整型
进行pickle
也是可以的,但是我觉得因为数字的存储是很平常的需求,而pickle
之后会带上对象的各种附加信息,会增加空间消耗,所以作者才有了这样的设计。
如果大家需要写自己的redis client
的话,可以参考一下这些设计,会给使用带来很大的方便。
Note: Python3
中pickle
和cPickle
已经合并为pickle