本主题主要是Redis的基本入门,包含内容:
1. Redis的安装与配置
2.Redis的命令行操作
3.Redis Python编程操作编程
一、下载与安装
1. 下载
1.1. 下载地址:
1.2. 下载的是tar压缩文件
redis-5.0.3.tar
下面是下载的截图
2. 安装
2.1. 解压缩指令
使用tar压缩工具解压缩:
tar -xf redis-5.0.3.tar
yangqiangdeMacBook-Pro:Downloads yangqiang$ tar -xf redis-5.0.3.tar
编译
进入解压缩目录,使用make执行编译:
2.2. 安装
执行make install实现安装
二、配置
1. 配置服务目录与文件
1.1. 配置目录
在/usr/local/目录下(也可以在其他目录下)创建redis服务器目录,我在我的默认用户的住目录下建立服务器目录:
创建三个目录:
| - bin:用来存放执行文件,包含服务器程序,客户程序等。
| - etc:用来存放配置文件。
| - db:用来存放数据库文件。
1.2. 拷贝执行与配置文件到服务目录
cp ~yangqiang/Downloads/redis-5.0.3/src/mkreleasehdr.sh ./bin
cp ~yangqiang/Downloads/redis-5.0.3/src/redis-benchmark ./bin/
cp ~yangqiang/Downloads/redis-5.0.3/src/redis-cli ./bin/
cp ~yangqiang/Downloads/redis-5.0.3/src/redis-server ./bin/
cp ~yangqiang/Downloads/redis-5.0.3/redis.conf ./etc/
2. 修改配置文件
2.1. 配置IP地址
2.2. 配置端口
2.3. 配置后台模式
2.4. 配置进程锁文件
2.5. 配置数据库数量
默认是0,可以使用命令行语句select < dbid > 选择数据库。
2.6. 数据库更新保存条件
其中第一个是秒数,第一个是改变次数,满足这些条件的任何一个就会自定保存或者更新。
2.7. 配置数据库数据是否压缩
2.8. 配置数据库文件名
2.9. 配置数据库的存放目录
2.10. 配置登录口令
三、启动服务器
启动指令:
./bin/redis-server ./etc/redis.conf
前面是服务器程序,参数是配置文件。
控制台下启动效果如下:
四、客户端使用
1. 客户端命令行帮助
2. 启动客户端
3. 获取客户端使用帮助
连接上服务器后,使用help指令,可以获取使用帮助
注意:
其中help <tab>是help后不停按tab键,会依次出现各种命令。
help @命令分组 获取每个命令组的帮助。
命令与SQL指令一样,不区分大小写。
为了方便,下面介绍常见的操作。
4. 客户端指令分组
4.1. 连接操作相关的命令
命令 | 说明 |
---|---|
ping | 测试连接是否存活如果正常会返回pong |
echo | 打印 |
select | 切换到指定的数据库,数据库索引号 index 用数字值指定,以 0 作为起始索引值 |
quit | 关闭连接(connection) |
auth | 简单密码认证 |
4.2. 服务端相关命令
命令 | 说明 |
---|---|
time | 返回当前服务器时间 |
client list | 返回所有连接到服务器的客户端信息和统计数据 参见http://redisdoc.com/server/client_list.html |
client kill ip:port | 关闭地址为 ip:port 的客户端 |
save | 将数据同步保存到磁盘 |
bgsave | 将数据异步保存到磁盘 |
lastsave | 返回上次成功将数据保存到磁盘的Unix时戳 |
shundown | 将数据同步保存到磁盘,然后关闭服务 |
info | 提供服务器的信息和统计 |
config resetstat | 重置info命令中的某些统计数据 |
config get | 获取配置文件信息 |
config set | 动态地调整 Redis 服务器的配置(configuration)而无须重启,可以修改的配置参数可以使用命令 CONFIG GET * 来列出 |
config rewrite | Redis 服务器时所指定的 redis.conf 文件进行改写 |
monitor | 实时转储收到的请求 |
slaveof | 改变复制策略设置 |
4.3. 发布订阅相关命令
命令 | 说明 |
---|---|
psubscribe | 订阅一个或多个符合给定模式的频道 例如psubscribe news.* tweet.* |
publish | 将信息 message 发送到指定的频道 channel 例如publish msg "good morning" |
pubsub channels | 列出当前的活跃频道 例如PUBSUB CHANNELS news.i* |
pubsub numsub | 返回给定频道的订阅者数量 例如PUBSUB NUMSUB news.it news.internet news.sport news.music |
pubsub numpat | 返回客户端订阅的所有模式的数量总和 |
punsubscribe | 指示客户端退订所有给定模式。 |
subscribe | 订阅给定的一个或多个频道的信息。例如 subscribe msg chat_room |
unsubscribe | 指示客户端退订给定的频道。 |
4.4. 对KEY操作的命令
命令 | 说明 |
---|---|
exists(key) | 确认一个key是否存在 |
del(key) | 删除一个key |
type(key) | 返回值的类型 |
keys(pattern) | 返回满足给定pattern的所有key |
randomkey | 随机返回key空间的一个 |
keyrename(oldname, newname) | 重命名key |
dbsize | 返回当前数据库中key的数目 |
expire | 设定一个key的活动时间(s) |
ttl | 获得一个key的活动时间 |
move(key, dbindex) | 移动当前数据库中的key到dbindex数据库 |
flushdb | 删除当前选择数据库中的所有key |
flushall | 删除所有数据库中的所有key |
4.5. 对String操作的命令
命令 | 说明 |
---|---|
set(key, value) | 给数据库中名称为key的string赋予值value |
get(key) | 返回数据库中名称为key的string的value |
getset(key, value) | 给名称为key的string赋予上一次的value |
mget(key1, key2,…, key N) | 返回库中多个string的value |
setnx(key, value) | 添加string,名称为key,值为value |
setex(key, time, value) | 向库中添加string,设定过期时间time |
mset(key N, value N) | 批量设置多个string的值 |
msetnx(key N, value N) | 如果所有名称为key i的string都不存在 |
incr(key) | 名称为key的string增1操作 |
incrby(key, integer) | 名称为key的string增加integer |
decr(key) | 名称为key的string减1操作 |
decrby(key, integer) | 名称为key的string减少integer |
append(key, value) | 名称为key的string的值附加value |
substr(key, start, end) | 返回名称为key的string的value的子串 |
4.6. 对List操作的命令
命令 | 说明 |
---|---|
rpush(key, value) | 在名称为key的list尾添加一个值为value的元素 |
lpush(key, value) | 在名称为key的list头添加一个值为value的 元素 |
llen(key) | 返回名称为key的list的长度 |
lrange(key, start, end) | 返回名称为key的list中start至end之间的元素 |
ltrim(key, start, end) | 截取名称为key的list |
lindex(key, index) | 返回名称为key的list中index位置的元素 |
lset(key, index, value) | 给名称为key的list中index位置的元素赋值 |
lrem(key, count, value) | 删除count个key的list中值为value的元素 |
lpop(key) | 返回并删除名称为key的list中的首元素 |
rpop(key) | 返回并删除名称为key的list中的尾元素 |
blpop(key1, key2,… key N, timeout) | lpop命令的block版本。 |
brpop(key1, key2,… key N, timeout) | rpop的block版本。 |
rpoplpush(srckey, dstkey) | 返回并删除名称为srckey的list的尾元素,并将该元素添加到名称为dstkey的list的头部 |
4.7. 对Set操作的命令
命令 | 说明 |
---|---|
sadd(key, member) | 向名称为key的set中添加元素member |
srem(key, member) | 删除名称为key的set中的元素member |
spop(key) | 随机返回并删除名称为key的set中一个元素 |
smove(srckey, dstkey, member) :移到集合元素
scard(key) |返回名称为key的set的基数
sismember(key, member) |member是否是名称为key的set的元素
sinter(key1, key2,…key N) |求交集
sinterstore(dstkey, (keys)) |求交集并将交集保存到dstkey的集合
sunion(key1, (keys)) |求并集
sunionstore(dstkey, (keys)) |求并集并将并集保存到dstkey的集合
sdiff(key1, (keys)) |求差集
sdiffstore(dstkey, (keys)) |求差集并将差集保存到dstkey的集合
smembers(key) |返回名称为key的set的所有元素
srandmember(key) |随机返回名称为key的set的一个元素
4.8. 对Hash操作的命令
命令 | 说明 |
---|---|
hset(key, field, value) | 向名称为key的hash中添加元素field |
hget(key, field) | 返回名称为key的hash中field对应的value |
hmget(key, (fields)) | 返回名称为key的hash中field i对应的value |
hmset(key, (fields)) | 向名称为key的hash中添加元素field |
hincrby(key, field, integer) | 将名称为key的hash中field的value增加integer |
hexists(key, field) | 名称为key的hash中是否存在键为field的域 |
hdel(key, field) | 删除名称为key的hash中键为field的域 |
hlen(key) | 返回名称为key的hash中元素个数 |
hkeys(key) | 返回名称为key的hash中所有键 |
hvals(key) | 返回名称为key的hash中所有键对应的value |
hgetall(key) | 返回名称为key的hash中所有的键(field)及其对应的value |
5. 设置密码
设置密码的命令是属于服务器组,其中密码设置属于redis.conf文件中的配置。
可以使用config get指令得到配置参数。
也可以使用config set指令修改配置参数。(当然可以直接编辑redis.conf文件来设置登录密码)
下面首先查看登录密码,然后设置登录密码,然后再登录,登录后就可以查看登录密码了。
强烈推荐登录方式不要使用命令行参数,而是使用AUTH指令。
6. 选择当前数据库
使用select 指令,指定数据库编号即可。
7. 数据库操作
7.1. 数据类型
Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及sorted set(有序集合)。
7.2. string数据操作
string 是 redis 最基本的类型,一个 key 对应一个 value;string 类型的值最大能存储 512MB。
string 类型是二进制安全的,就是redis 的 string 可以包含任何数据。比如jpg图片或者序列化的对象。
127.0.0.1:6379> help SET
SET key value [expiration EX seconds|PX milliseconds] [NX|XX]
summary: Set the string value of a key
since: 1.0.0
group: string
SET参数说明:
| - EX指定过期时间单位为:秒
| - PX指定过期时间单位为:毫秒
| - NX key不存在才执行指令
| - XX key存在才会更新
过期时间一到,从内存数据消失。
7.3. Hash数据操作
Redis hash 是一个键值(key=>value)对集合。
Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。
每个 hash 可以存储 232 -1 键值对(40多亿)。
127.0.0.1:6379> help hmset
HMSET key field value [field value ...]
summary: Set multiple hash fields to multiple values
since: 2.0.0
group: hash
127.0.0.1:6379>
获取所有keys与values:
7.4. List数据操作
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
列表最多可存储 232 - 1 元素 (4294967295, 每个列表可存储40多亿)。
7.5. Set数据操作
Redis的Set是string类型的无序集合。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
7.6. sorted set数据操作
Redis zset(sorted set) 和 set 一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
zset的成员是唯一的,但分数(score)却可以重复。
7.7. 查看key并删除key
使用keys命令与del命令
7.8. 事务处理
事务命令 | 说明 |
---|---|
DISCARD | 取消事务,放弃执行事务块内的所有命令。 |
EXEC | 执行所有事务块内的命令。 |
MULTI | 标记一个事务块的开始。 |
UNWATCH | 取消 WATCH 命令对所有 key 的监视。 |
WATCH key [key ...] | 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。 |
五、python访问Redis数据库
1. 安装redis模块
使用命令
yangqiangdeMacBook-Pro:bin yangqiang$ pip install redis
Collecting redis
Downloading https://files.pythonhosted.org/packages/f1/19/a0282b77c23f9f9dbcc6480787a60807c78a45947593a02dbf026636c90d/redis-3.1.0-py2.py3-none-any.whl (63kB)
100% |████████████████████████████████| 71kB 103kB/s
Installing collected packages: redis
Successfully installed redis-3.1.0
2. redis模块帮助
使用help可以获取redis模块帮助文档
3. redis模块核心API
Redis类负责用户客户端调用的封装,其成员基本上对用Redis客户端的所有操作指令。
Connection负责连接Redis服务器。
其他还有负责分布式锁的Lock等,Sentinel负责集群客户端调用封装等。
4. 单一连接
直接使用Redis连接Redis服务器,redis.client.Redis类构造器如下:
__init__(self, host='localhost', port=6379, db=0, password=None, socket_timeout=None, socket_connect_timeout=None, socket_keepalive=None, socket_keepalive_options=None, connection_pool=None, unix_socket_path=None, encoding='utf-8', encoding_errors='strict', charset=None, errors=None, decode_responses=False, retry_on_timeout=False, ssl=False, ssl_keyfile=None, ssl_certfile=None, ssl_cert_reqs='required', ssl_ca_certs=None, max_connections=None)
# coding = utf-8
import redis.client
redis = redis.client.Redis(host='127.0.0.1', port=6379, db=0, password='yq123456')
print(redis.keys(pattern='*'))
[b'telno', b'key2', b'friends', b'courses', b'key1']
5. 连接池
连接池redis.connection.ConnectionPool的构造器:
__init__(self, connection_class=<class 'redis.connection.Connection'>, max_connections=None, **connection_kwargs)
# coding = utf-8
import redis.client
import redis.connection
pools = redis.connection.ConnectionPool(
connection_class=redis.connection.Connection,
max_connections=10,
host='127.0.0.1',
port=6379,
db=0,
password='yq123456')
redis = redis.client.Redis(connection_pool=pools)
print(redis.keys(pattern='*'))
[b'telno', b'key2', b'friends', b'courses', b'key1']
6. 数据库操作
数据库操作基本上对应redis-cli客户端的指令。
# coding = utf-8
import redis.client
redis = redis.client.Redis(host='127.0.0.1', port=6379, db=0, password='yq123456')
if redis.set(name='py_user', value='Jack'):
print('执行成功')
if redis.save():
print('执行成功')
执行成功
执行成功
7. 事务操作
一般来说,事务有四个性质称为ACID,分别是原子性,一致性,隔离性和持久性。
a)原子性atomicity:redis事务保证事务中的命令要么全部执行要不全部不执行。有些文章认为redis事务对于执行错误不回滚违背了原子性,是偏颇的。
b)一致性consistency:redis事务可以保证命令失败的情况下得以回滚,数据能恢复到没有执行之前的样子,是保证一致性的,除非redis进程意外终结。
c)隔离性Isolation:redis事务是严格遵守隔离性的,原因是redis是单进程单线程模式,可以保证命令执行过程中不会被其他客户端命令打断。
d)持久性Durability:redis事务是不保证持久性的,这是因为redis持久化策略中不管是RDB还是AOF都是异步执行的,不保证持久性是出于对性能的考虑。
在Redis中事务有专门的指令,但在Python实现中,通过Pipeline类实现事务相关指令。
__init__(self, connection_pool, response_callbacks, transaction, shard_hint)
获取通过Redis对象的ipieline函数返回Pipeline对象
Redis.pipeline(self, transaction=True, shard_hint=None)
pipeline 也可以保证缓冲的命令组做为一个原子操作。缺省就是这种原子模式。
要使用命令缓冲,但禁止pipeline 的原子操作属性,可以关掉 transaction=False。
7.1. 提交事务
# coding = utf-8
import redis.client
redis = redis.client.Redis(host='127.0.0.1', port=6379, db=0, password='yq123456')
pipeline = redis.pipeline(transaction=True) # 参数指定是否每个指令后自动提交事务。
pipeline.multi()
pipeline.set(name='py_user', value='Jack')
if pipeline.execute():
print('提交事务成功')
提交事务成功
# coding = utf-8
import redis.client
import redis.connection
pool = redis.connection.ConnectionPool(max_connections=10, host='127.0.0.1', port=6379, db=0, password='yq123456')
pipe = redis.client.Pipeline(
connection_pool=pool,
transaction=True,
shard_hint=None,
response_callbacks=[]
)
pipe.multi()
pipe.set(name='py_user', value='Jack')
pipe.set(name='linux_user',value='root')
if pipe.execute(): # 提交事务
print('事务提交成功!')
事务提交成功!
7.2. 回滚(放弃)事务
注意:
Redis与mysql中事务不同,在redis事务遇到执行错误的时候,不会进行回滚,而是简单的越过了,并保证其他的命令正常执行。这点可以通过上面的程序观察到这个设计效果。
在Python代码中,执行错误的命令的异常会被扑捉,并在execute函数的返回值列表中。
# coding = utf-8
import redis.client
import redis.connection
pool = redis.connection.ConnectionPool(max_connections=10, host='127.0.0.1', port=6379, db=0, password='yq123456')
pipe = redis.client.Pipeline(
connection_pool=pool,
transaction=True,
shard_hint=None,
response_callbacks=[]
)
pipe.multi()
pipe.set(name='py_user', value='Jack')
pipe.set(name='linux_user',value='root')
# pipe.execute()
pipe.reset() # 取消事务
注意:
reset会释放占用的连接,如果使用连接池,则连接会返回到连接池。
7.3. watch与unwatch的使用
在redis-cli终端中,WATCH 命令可以为 Redis 事务提供 check-and-set (CAS)行为。被 WATCH 的键会被监视,并会发觉这些键是否被改动过了。 如果有至少一个被监视的键在 EXEC 执行之前被修改了, 那么整个事务都会被取消, EXEC 返回空多条批量回复(null multi-bulk reply)来表示事务已经失败。
如果在 WATCH 执行之后, EXEC 执行之前, 有其他客户端修改了 WATCH中指定的key的值, 那么程序的事务就会抛出异常 redis.WatchError。抛出异常事务自动取消,需要重新调用MULTI指令。
下面例子运行的时候,在睡眠20秒后指定EXEC,在这期间内,可以在另外客户端修改key为py_user的值,当睡眠20秒后,会抛出异常。(在睡眠中,不会中断睡眠抛出异常,这个与其他编程有差异)
一般对事务处理模式,最好开启监听机制,这样可以提供更好的原子操作。一旦异常抛出,可以反复执行,直到成功为止。
# coding = utf-8
import redis
import redis.connection
import time
pool = redis.connection.ConnectionPool(max_connections=10, host='127.0.0.1', port=6379, db=0, password='yq123456')
pipe = redis.client.Pipeline(
connection_pool=pool,
transaction=True,
shard_hint=None,
response_callbacks=[]
)
try:
pipe.watch('py_user',)
pipe.multi()
pipe.set(name='py_user', value='Jack')
time.sleep(20)
pipe.set(name='linux_user', value='root')
pipe.execute()
except redis.WatchError as e:
print(e)
# 异常抛出的时候,事务被自动取消(注意睡眠期间不会触发异常事件)
pipe.reset()
Watched variable changed.
7.4. Redis类中transaction函数的使用
上面使用watch来观察key的值被其他客户改变,一般为了防止数据脏,会采用循环方式,Redis类提供了transaction函数来处理这种麻烦。
下面代码,可以在其他客户端改变,会看到print("睡眠")执行两次,直到处理并事务提交成功。
# coding = utf-8
import redis.connection
import time
r = redis.client.Redis(host='127.0.0.1', port=6379, db=0, password='yq123456')
def my_cb(pipe_):
pipe_.multi()
pipe_.set(name='py_user', value='Jack-Hello')
print("睡眠")
time.sleep(5)
pipe_.set(name='linux_user', value='root')
if pipe_.execute():
print("提交成功")
else:
print("提交失败")
r.transaction(my_cb, 'py_user')
睡眠
睡眠
提交成功
[]
8. Pipeline类命令执行回调实现
Pipeline类的构造器中可以指定对命令执行结果返回的回调,这样可以每个命令被执行就可以及时响应,而不是处理execute的返回结果。
回调函数至少传递一个参数,如果有withscores这样的参数,可能返回多个参数。
# coding = utf-8
import redis
import redis.connection
import time
# 返回每个命名执行的结果
def my_call(*p):
print(*p)
pool = redis.connection.ConnectionPool(max_connections=10, host='127.0.0.1', port=6379, db=0, password='yq123456')
pipe = redis.client.Pipeline(
connection_pool=pool,
transaction=True,
shard_hint=None,
response_callbacks={'SET': my_call}
)
pipe.multi()
pipe.set(name='py_user', value='Jack')
pipe.set(name='linux_user', value='root')
pipe.execute() # 因为有回调,所以,这个函数返回是None。
b'OK'
b'OK'
[None, None]
9. 订阅与发布
订阅与发布是一种消息通信机制。包含两个行为:
| - 订阅:SUBSCRIBE channel [channel ...]
| - 发布:PUBLISH channel message
当然可以退订。
|- 退订:UNSUBSCRIBE [channel [channel ...]]
同时提供模式匹配指令:
|- PSUBSCRIBE pattern [pattern ...]
|- PUNSUBSCRIBE [pattern [pattern ...]]
查看订阅状态:
|- PUBSUB subcommand [argument [argument ...]]
9.1. 订阅消息
使用SUBSCRIBE命令订阅消息:
9.2. 发布消息
使用PUBLISH发布消息:
9.3. 查看订阅发布状态
9.3.1. 获取帮助
使用PUBSUB HELP命令获取完整的帮助:
9.3.2. 查看订阅状态
注意:
NUMPAT是采用模式订阅的数量。
NUMSUB是多某个频道德订阅数量。
9.4. 使用python程序订阅消息
订阅消息需要使用订阅发布对象class 'redis.client.PubSub'。
调用handle_message可以把订阅的响应结构解析成一个字典结构。
# coding = utf-8
import redis.client
redis = redis.client.Redis(host='127.0.0.1', port=6379, db=0, password='yq123456')
sub_pub = redis.pubsub()
print(sub_pub, type(sub_pub))
sub_pub.subscribe('mychannel')
result = sub_pub.parse_response()
print('订阅操作:', result)
result = sub_pub.parse_response()
message = sub_pub.handle_message(result)
print('订阅的消息:', result, message['data'])
<redis.client.PubSub object at 0x106c5e940> <class 'redis.client.PubSub'>
订阅操作: [b'subscribe', b'mychannel', 1]
订阅的消息: [b'message', b'mychannel', b'this is a test message'] b'this is a test message'
9.5. 使用python程序发布消息
# coding = utf-8
import redis.client
redis = redis.client.Redis(host='127.0.0.1', port=6379, db=0, password='yq123456')
redis.publish('mychannel','this is a test message')
4
六、附录
Redis还有一些其他连接快捷方式,我们采用比较常规的方式介绍。
Redis还支持集群,后面单独介绍集群的配置与Python编程的一些基本概念。