redis数据库的使用
一、安装
-
下载-----打开redis官方网站,推荐下载稳定版本(stable)
wget https://download.redis.io/releases/redis-6.2.1.tar.gz
-
解压
tar -zxvf redis-6.2.1.tar.gz
-
改名并移动到/usr/local目录下(推荐)
sudo mv redis-6.2.1 redis sudo mv ./redis /usr/local/
-
编译安装
cd /usr/local/redis make && sudo make install
二、配置
/usr/local/redis目录下,文件redis.conf为配置文件
- 需要远程访问,可将此行注释:
bind 127.0.0.1
- 默认端口:6379,可以修改
port 6379
- 是否以守护进程运行
daemonize no|yes
如果以守护进程运行,则不会堵塞命令行,否则当前终端被阻塞,推荐设为yes
- 设置密码
注释掉:requirepass foobared #设置密码 requirepass 需要设置的密码
- 数据文件名
dbfilename dump.rdb
数据库中添加键值后,会发现在当前运行的目录下创建了一个文件:dump.rdb,这个文件用于将数据持久化存储
- 数据文件存储路径
dir ./ #推荐修改为 dir /var/lib/redis/
三、运行
- 修改配置文件存放目录
sudo mkdir /etc/redis sudo cp /usr/local/redis/redis.conf /etc/redis/redis.conf
一般配置文件都存放在/etc/目录下
- 配置文件启动(方式一)
sudo redis-server /etc/redis/redis.conf
直接运行redis-server会阻塞当前终端
- 后台启动服务(方式二)
redis-server & #执行命令后,按两下回车键
- 关闭服务
ps -aux | grep redis sudo kill -9 进程号(pid)
- 启动redis客户端
redis-cli 或者 redis-cli -a 密码
四、数据库以及键的命令
-
数据库命令
- 切换数据库
select 数据库名 #0-15共16个数据库,默认数据库为0
- 清空当前数据库数据
flushdb
- 清空所有数据库数据
flushall
- 切换数据库
-
键命令
- 查找键
keys pattern keys * #查找所有 keys n* #查找以n开头的键
- 判断键是否存在
exists key [key ...]
不存在就返回0,如果判断多个,则返回存在的个数,比如判断3个键,只有2个存在,则返回2
- 查看键对应的值类型
type key
- 删除键及对应的value
del key [key ...]
- 设置过期时间,以秒为单位
expire key seconds
创建时没有设置过期时间则一直存在,直到使用del移除
- 查看有效时间,以秒为单位
ttl key
- 查找键
五、数据操作
redis是key-value的数据,所以每个数据都是一个键值对
键的类型以及元素的类型均为字符串
值的类型可分为以下5种:
- 字符串string
- 哈希hash
- 列表list
- 集合set
- 有序集合zset
string
string是redis最基本的类型,是二进制安全的,即可以为任何数据,比如数字、图片、序列化对象等,最大能存储512MB数据
设置:
- 设置键值
set key value
- 设置键值及过期时间,以秒为单位
setex key seconds value
- 设置多个键值
mset key value [key value ...]
获取:
- 根据键获取值,如果不存在此键则返回nil
get key
- 根据多个键获取多个值
mget key [key ...]
- 获取值长度
strlen key
运算:(值必须是数字)
- 键对应的值加1
incr key
- 值加整数
incrby key increment
- 值减1
decr key
- 值减整数
decrby key decrement
其他:
- 追加值
append key value
例如原有的键对应值是"hello",append key "world"后,值变成"helloworld"
hash
用于存储对象,对象的格式为键值对
设置:
- 设置单个属性
hset key field value
注:hset现在也能设置多个属性
- 设置多个属性
hmset key field value [field value ...]
获取:
- 获取单个属性的值
hget key field
- 获取多个属性的值
hmget key field [field ...]
- 获取所有属性以及值
hgetall key
- 获取所有属性
hkeys key
- 获取所有值
hvals key
- 获取属性个数
hlen key
- 获取某个属性值的长度
hstrlen key field
其他:
- 判断某个属性是否存在
hexists key field
- 删除属性和值
hdel key field [field ...]
list
列表的元素类型为string,在列表的头部或者尾部添加元素,按照插入顺序排序
插入:
- 头部插入数据
lpush key element [element ...]
如果插入多个数据,后插入的数据会在前面,比如lpush list1 'a' 'b' 'c',结果list1中的数据顺序是'c' ,'b', 'a'
- 头部插入数据(键不存在,则不执行命令)
lpushx key element [element ...]
- 尾部插入数据
rpush key element [element ...]
- 尾部插入数据(键不存在,则不执行命令)
rpushx key element [element ...]
- 在指定元素的前面或者后面插入某个元素
linsert key BEFORE|AFTER pivot element
修改:
- 修改指定索引的元素值,索引从0开始,也可以使用负数,-1表示最后一个
lset key index element
获取:
- 移除并获取第一个元素
lpop key
- 移除并获取最后一个元素
rpop key
- 获取指定索引对应的元素
lindex key index
- 获取指定索引范围内的元素(包含stop索引元素)
lrange key start stop
- 获取列表长度
llen key
删除
- 根据参数count的值,移除列表中与参数element相等的元素。
lrem key count element
count=0,移除列表中所有相等的元素;count>0,从前向后搜索并移除count个相等的元素;count<0,从后向前搜索并移除|count|个相等的元素。
其他:
- 裁剪列表
ltrim key start stop
裁剪后,成为原列表的一个子集
- 移动某一个列表尾部数据到另一个列表头部(也可以是同一个列表)
rpoplpush source destination
set
无序集合,元素为string类型,具有唯一性,不重复
添加:
- 添加1个或多个元素,并返回成功添加的元素个数
sadd key member [member ...]
获取:
- 随机返回指定个数的元素,不指定个数,默认返回一条数据,指定个数,则返回数组形式
srandmember key [count]
- 返回集合所有元素
smembers key
- 返回集合元素个数
scard key
移除
- 删除并返回指定个数的元素
spop key [count]
count不指定,默认移除1个,指定1个,则返回的是数组
- 删除指定的元素,并返回被删除的元素个数
srem key member [member ...]
有则删除,没有则不做任何操作
其他:
- 移动一个集合中的元素到另一个集合中,操作成功则返回1,否则返回0
smove source destination member
- 交集
sinter key [key ...]
集合共有的部分
- 差集
sdiff key [key ...]
注意先后顺序,前面集合所独有的部分
- 合集
sunion key [key ...]
- 判断元素是否在集合中
sismember key member
zset
有序集合(sorted set),元素类型为string,具有唯一性,不重复,每个元素都会关联一个double类型的score,表示权重,通过权重将元素从小到大排序,score值可以相同。
设置:
- 添加
zadd key score member [score member ...]
如果元素已经存在,则表示更新操作,更新该元素score值
获取:
- 升序获取指定元素排名(从0开始)
zrank key value
- 降序获取指定元素排名(从0开始)
zrevrank key value
- 升序获取指定索引范围内的元素
zrange key min max
- 降序获取指定索引范围内的元素
zrevrange key min max
- 升序获取指定分值范围内的元素
zrangebyscore key min max
- 降序获取指定分值范围内的元素
zrevrangebyscore key max min WITHSCORES 【会带有分值】 zrevrangebyscore key max min 【不带分值】
- 对指定元素分值进行增减操作
zincrby key amount value
amount为正数,增加操作,负数为减少操作
- 删除指定的元素
zrem key value
- 升序删除指定索引范围内的元素
zremrangebyrank key min max
- 升序删除指定分值范围内的元素
zremrangebyscore key min max
- 删除并获取分值最大的指定个数元素
zpopmax key count
返回值带上分数,比如[(value1, score1), (value2, score2)...]
- 删除并获取分值最小的指定个数元素
zpopmin key count
- 获取集合元素个数
zcard key
- 统计权重值在某一范围内的元素个数
zcount key min max
- 获取某一个元素的权重值
zscore key member
六、发布订阅
客户端发到频道的消息,将会被推送到所有订阅此频道的客户端。
客户端不需要主动去获取消息,只需要订阅频道,这个频道的内容就会被推送过来。
-
发布
publish channel message
-
订阅
subscribe channel [channel ...]
-
取消订阅
unsubscribe [channel [channel ...]]
如果不写参数,表示取消订阅所有频道
七、主从配置
一个主服务器可以拥有多个从服务器,一个从服务器又可以拥有多个从服务器,如此下去,形成了强大的多级服务器集群架构。
将ip为192.168.1.10的机器作为主服务器,将ip为192.168.1.11的机器作为从服务器,进行演示。
-
主服务器配置
bind 192.168.1.10 # requirepass abc123 可选,设置redis密码
修改配置文件(/etc/redis/redis.conf文件)
-
从服务器配置
bind 192.168.1.11 slaveof 192.168.1.10 6379 # masterauth abc123 可选,指定Master节点的密码,仅在Master节点设置了requirepass时设置
注意:在slaveof后面写主机ip,再写端口,而且端口必须写
-
主服务器上写数据
set hello world
-
从服务器上读取数据
get hello
-
查看主从节点
info Replication
在客户端命令行输入上述命令,就会显示当前节点角色具体信息
-
主服务器挂掉如何解决?
-
手动方案
# 一、选某个从节点执行命令,使其成为主节点 slaveof no one # 二、其它节点设为该新主节点的从(包括恢复后的原主节点) slaveof ip port
-
哨兵模式
- 安装redis-sentinel
sudo apt install redis-sentinel
- 配置文件sentinel.conf
# ------1、相同部分------- port 26379 # 端口 # 该哨兵监听的主节点(哨兵会基于主节点找到各从节点):其中mymaster为监控对象起的服务器名称,2:判定主节点客观下线所需要的哨兵数量 # 当主节点挂了,哨兵会自动找到下一个主节点,所以ip地址写第一个主节点即可 sentinel monitor mymaster 47.94.174.89 6379 2 # 如果master有密码,则需要添加该配置(redis集群需要配置相同的密码,哨兵会自动监听连接到主节点的从节点) sentinel auth-pass mymaster 密码 # ------2、不同部分------ # 以1主2从3哨兵模式演示: # a 节点指定为 47.94.174.89 # b 节点指定为 47.93.39.238 # c 节点指定为 47.93.235.147 sentinel announce-ip <当前服务器公网ip>
注意点:
1、各redis集群密码需要相同
2、各redis集群服务器redis.conf中需要指定:masterauth 主节点密码
3、哨兵集群是基于Redis的订阅/发布模式建立
4、具体哪个从点会成为主节点,会按照规则:(优先级、偏移量、runid)
哨兵模式缺点:
复制延时问题 - 启动
redis-sentinel sentinel.conf
对于3个redis集群,则需要在3个服务器上分别启动哨兵
- 安装redis-sentinel
-
八、与python交互
-
安装redis包
sudo pip3 install redis
-
引入redis模块
import redis
-
创建数据库连接对象
try: r = redis.StrictRedis(host='ip地址',port=6379) except Exception as e: print(e) # 不需要调用r.close()方法来关闭连接对象,因为内部__del__()方法中已实现了self.close()
-
哨兵模式操作
from redis.sentinel import Sentinel #生成哨兵连接 sentinel = Sentinel([('localhost', 26379)], socket_timeout=0.1) #初始化master连接 master = sentinel.master_for('mymaster', socket_timeout=0.1, db=0) slave = sentinel.slave_for('mymaster',socket_timeout=0.1, db=0) #使用redis相关命令 master.set('mymaster', 'yes') print(slave.get('mymaster'))
-
连接池操作
-
方式一:不用自己实现连接池(内部自动生成)-- 更高效简便
import redis # 内部已经自动维护了一个连接池 con = redis.StrictRedis(host=REDIS_HOST, port=REDIS_PORT, db=REDIS_DATABASE, password=REDIS_PASSWORD,decode_responses=True) def test(): for i in range(50): con.get('hello') # 5个线程操作 t_list = [] for i in range(5): t = threading.Thread(target=test) t.start() t_list.append(t) for t in t_list: t.join() # 连接池中的可用连接数 print(len(con.connection_pool._available_connections)) # 5 # con.close() 不需要执行该步操作,因为con.__del__()方法中已经实现了
-
方式二:自己手动实现连接池
import redis redis_pool = redis.ConnectionPool(host=REDIS_HOST, port=REDIS_PORT, db=REDIS_DATABASE, password=REDIS_PASSWORD, decode_responses=True) def test(): for i in range(50): con = redis.StrictRedis(connection_pool=redis_pool) con.get('hello') # 5个线程操作 t_list = [] for i in range(5): t = threading.Thread(target=test) t.start() t_list.append(t) for t in t_list: t.join() # 连接池中的可用连接数 print(len(redis_pool._available_connections)) # 5
-
-
数据库操作
-
方式一:根据数据类型,调用相应方法
#string类型演示 r.set(key,value) r.get(key) #注意返回的是bytes类型,没有则返回None,redis中存储的数值也都是字符串类型 #hash类型 r.hset(key,field,value) #设置单个属性和值,可以执行多次 r.hmset(key,{field1:value1,...}) #设置多个属性和值 r.hget(key,field) #获取单个属性的值
r.hmget(key,field1,...) #获取多个属性的值,返回的是属性的值构成的列表,bytes类型 r.hgetall(key) #获取所有属性和值,dict类型,其中的键和值都是bytes类型,例如{b'name':b'laowang',b'age':b'28'} r.expire(key,seconds) #设置过期时间 r.hdel(key,field1,...) #删除属性和值 r.delete(key) #删除键,注意是delete而不是del -
方式二:利用pipeline缓冲多条命令,然后一次性执行,减少服务器-客户端之间TCP数据库包,从而提高效率
# 仅使用管道(不开启事务) with r.pipeline(transaction=False) as pipe: pipe.set('key1','value1') pipe.set('key2','value2') pipe.set('key3','value3') data = pipe.execute() # 返回的是一个有序列表,元素是上面每条命令执行的返回值[True,True,True] print(data) # 开启事务(原子性操作,要么所有命令全部执行,要么全部不执行) with r.pipeline() as pipe: # transaction参数默认为True pipe.multi() # 开启事务 pipe.set('key1','value1') pipe.set('key2','value2') pipe.set('key3','value3') data = pipe.execute() # 返回的是一个有序列表,同上 print(data)
-