最近在写代码的时候,因为redis的编码的问题,debug了一天,最后终于搞清楚了python-redis中遇到的那些坑。在这里记录一下:
python中如何连接redis:
在python中,我们使用python-redis库来连接redis数据库。我们采用如下方式连接redis:
import redis
pool = redis.ConnectionPool(host='localhost', port=6379)
r = redis.Redis(connection_pool=pool)
我们尝试向redis中添加两个键值对:
r.set('bob','123456')
r.set('alice','987654')
print(r.keys())
'''output
[b'alice', b'bob']
'''
我们会发现,打印出的key数组,每个字符串前面加了一个b
。在python3.x中,这表示这个字符串是bytes。
如果我们希望判断'bob'
是否在数据库中,并打印bob
对应的值,通常我们会这样做:
if 'bob' in r.keys():
print(r.get('bob'))
else:
print('No such key!')
print(r.get('bob'))
'''output
No such key!
b'123456'
'''
我们会发现,明明我们已经在redis中存储了('bob','123456')
键值,但是我们使用get()方法却无法获取到。这是因为:
例如:
r.flushall()
r.set('bob','123456')
r.set('alice','987654')
for k in r.keys(): # r.get() 可以接受bytes()类型的输入
print(k,r.get(k))
if 'bob'.encode('utf-8') in r.keys():
print(r.get('bob'))
else:
print('No such key!')
k = 'bob'
print("Encode:",k,r.get(k).decode('utf-8')) # r.get()函数也可以直接传入str
'''output
b'alice' b'987654'
b'bob' b'123456'
b'123456'
Encode: bob 123456
'''
总结:
如果我们总是使用encode和decode来编码-解码键值对,会非常的麻烦。在python中,我们可以通过声明redis连接池的decode_responses
字段来对键值对进行默认编码:
import redis
pool = redis.ConnectionPool(host='localhost', port=6379,decode_responses=True)
r = redis.Redis(connection_pool=pool)
r.flushall()
r.set('Monday','Sunny')
r.set('Tuesday','Rainy')
for k in r.keys():
print(k,r.get(k))
'''output
Tuesday Rainy
Monday Sunny
'''
我们发现,当我们声明了decode_responses=True
之后,我们从redis中使用get()和keys()获得的键值对就都是str类型的了。
那么我们是不是一直使用decode_responses=True
就好了呢?为什么redis要将decode_responses
默认设置成False呢?。我们分别在两种情况下进行如下操作:
不使用decode_responses
import redis
import pickle
pool = redis.ConnectionPool(host='localhost', port=6379)
r = redis.Redis(connection_pool=pool)
r.flushall() # 清空redis
d = {'bob':'123456789','alice':'987654321'}
r.set('my_dict',pickle.dumps(d))
new_d = pickle.loads(r.get('my_dict'))
print(new_d)
'''output
{'bob': '123456789', 'alice': '987654321'}
'''
使用decode_responses
import redis
import pickle
pool = redis.ConnectionPool(host='localhost', port=6379,decode_responses=True)
r = redis.Redis(connection_pool=pool)
r.flushall() # 清空redis
d = {'bob':'123456789','alice':'987654321'}
r.set('my_dict',pickle.dumps(d))
new_d = pickle.loads(r.get('my_dict'))
print(new_d)
'''output
Traceback (most recent call last):
File "", line 9, in
new_d = pickle.loads(r.get('my_dict'))
File "C:\ProgramData\Anaconda3\lib\site-packages\redis\client.py", line 1264, in get
return self.execute_command('GET', name)
File "C:\ProgramData\Anaconda3\lib\site-packages\redis\client.py", line 775, in execute_command
return self.parse_response(connection, command_name, **options)
File "C:\ProgramData\Anaconda3\lib\site-packages\redis\client.py", line 789, in parse_response
response = connection.read_response()
File "C:\ProgramData\Anaconda3\lib\site-packages\redis\connection.py", line 637, in read_response
response = self._parser.read_response()
File "C:\ProgramData\Anaconda3\lib\site-packages\redis\connection.py", line 332, in read_response
response = self.encoder.decode(response)
File "C:\ProgramData\Anaconda3\lib\site-packages\redis\connection.py", line 133, in decode
value = value.decode(self.encoding, self.encoding_errors)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 0: invalid start byte
'''
这是因为,我们使用pickle存储的时候,pickle会将对象dumps成bytes类型的字符串,并且该字符串无法使用utf-8进行decode。由于我们使用了decode_responses
, 执行r.get(‘my_dict’)的时候会尝试将pickle dumps的字符串deocde为utf8,所以会抛出异常。
总结:究竟我们什么时候需要使用decode_responses
呢?
decode_responses
来避免在代码手动decodedecode_responses