Redis基础

Redis

image-20220430222551442

Redis介绍和安装

redis 是一个非关系型数据库(区别于mysql关系型数据库,关联关系,外键,表),nosql数据库(not only sql:不仅仅是SQL),数据完全内存存储(速度非常快),存数据的形式是key value的形式,

value有五大数据类型:字符串,列表,hash(python中的字典),集合,有序集合

使用redis的优势

"""
(1) 速度快,因为数据存在内存中,类似于字典,字典的优势就是查找和操作的时间复杂度都是O(1)
(2) 支持丰富数据类型,支持string,list,set,sorted set,hash
(3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
(4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
"""

redis 最适合的场景(主要做缓存,所以又叫缓存数据库)

"""
(1)会话缓存(Session Cache)---》存session---》速度快
(2)接口,页面缓存---》把接口数据,存在redis中
(3)队列--->celery使用
(4)排行榜/计数器--->个人页面访问量
(5)发布/订阅
"""

安装

👉官网:Redis

👉官网下载:Download | Redis

windows:作者不支持windwos
本质原因:redis很快,使用了io多路复用中的epoll的网络模型,这个模型不支持win,所以不支持(看到高性能的服务器基本上都是基于io多路复用中的epoll的网络模型,nginx),微软基于redis源码,自己做了个redis安装包,但是这个安装包最新只到3.x,又有第三方组织做到最新5.x的安装包

👉win下载地址:

图形化工具安装

RDB:https://github.com/uglide/RedisDesktopManager/releases

redis服务的启动与关闭

Windows启动服务端:

  1. 找到服务面板,点击启动

  2. 命令启动:

    redis-server redis.windows-service.conf
    # redis-server 配置文件
    

Windows启动客户端:

命令行:redis-cli -p 端口 -h 地址
客户端:rdb连接

image-20220430221924039

image-20220430222220558

注意:一个键最大能存储 512MB


Python连接redis

Python连接redis需要下载模块redis

  • 安装模块:pip install redis

连接本地示例,也可以连接远端

方式一:Redis实例化

	from redis import Redis

# 普通连接
conn = Redis(
    host="localhost", # 连接本地/远端
    port=6379,  # 端口
    db=0,  # 数据库
    password=None, # 密码
)
conn.set('name','HammerZe')
print(conn.get('name')) # b'HammerZe'

方式二:使用连接池

# 连接池连接
# 第一步:创建池
POOL = redis.ConnectionPool(max_connections=10,host="localhost",port=6379, db=0)
# 第二步,使用池,从池中拿一个连接
conn = redis.Redis(connection_pool=POOL)
print(conn.get('name')) # b'HammerZe'
"""
这里需要注意,使用POOL必须是单例模式,也就是说POOL必须是单例的且全局只能有一个实例,无论程序怎么执行,POOL始终是同一个对象!所以建议把构造连接池的代码单独放入一个py文件中,因为导入py文件就是天然的单例模式,同一个py文件是一个对象(原理是导入的时候通过.pyc编译了)
"""

# POOL单例模式
'''redis_pool.py'''
import redis
POOL = redis.ConnectionPool(max_connections=10,host="localhost",port=6379, db=0)
'''导入使用'''
import redis
from redis_pool import POOL
# 第二步,使用池,从池中拿一个连接
conn = redis.Redis(connection_pool=POOL)
print(conn.get('name')) # b'HammerZe'

# 多线程
from threading import Thread
import redis
import time
from redis_pool import POOL  
def get_name():
    conn=redis.Redis(connection_pool=POOL)
    print(conn.get('name'))
for i in range(10):
    t=Thread(target=get_name)
    t.start()

time.sleep(2)
'''
注意:
  py文件作为脚本文件的时候,不能使用相对导入,只能使用绝对导入,不然会报错,要从环境变量中开始导起
  在pycharm中右键运行的脚本所在的目录,就会被加入到环境变量
'''

image-20220501114619370

redis五大数据类型:字符串,列表,hash,集合,有序集合,下面介绍五大数据类型的基本操作:

Redis 键(key)

Redis 键命令用于管理 redis 的键

语法:命令 键名

demo:

127.0.0.1:6379> SET mykey myvalue
OK
127.0.0.1:6379> DEL mykey
(integer) 1
# 在以上实例中 DEL 是一个命令, mykey 是一个键。 如果键被删除成功,命令执行后输出 (integer) 1,否则将输出 (integer) 0

Redis keys 常见命令

序号 命令及描述
1 DEL key 该命令用于在 key 存在时删除 key。
2 DUMP key 序列化给定 key ,并返回被序列化的值。
3 EXISTS key 检查给定 key 是否存在。
4 EXPIRE key seconds 为给定 key 设置过期时间,以秒计。
5 EXPIREAT key timestamp EXPIREAT 的作用和 EXPIRE 类似,都用于为 key 设置过期时间。 不同在于 EXPIREAT 命令接受的时间参数是 UNIX 时间戳(unix timestamp)。
6 PEXPIRE key milliseconds 设置 key 的过期时间以毫秒计。
7 PEXPIREAT key milliseconds-timestamp 设置 key 过期时间的时间戳(unix timestamp) 以毫秒计
8 KEYS pattern 查找所有符合给定模式( pattern)的 key 。
9 MOVE key db 将当前数据库的 key 移动到给定的数据库 db 当中。
10 PERSIST key 移除 key 的过期时间,key 将持久保持。
11 PTTL key 以毫秒为单位返回 key 的剩余的过期时间。
12 TTL key 以秒为单位,返回给定 key 的剩余生存时间(TTL, time to live)。
13 RANDOMKEY 从当前数据库中随机返回一个 key 。
14 RENAME key newkey 修改 key 的名称
15 RENAMENX key newkey 仅当 newkey 不存在时,将 key 改名为 newkey 。
16 [SCAN cursor MATCH pattern] [COUNT count] 迭代数据库中的数据库键。
17 TYPE key 返回 key 所储存的值的类型。

Redis字符串操作

语法:命令 键名

Redis 字符串命令

下表列出了常用的 redis 字符串命令:

序号 命令及描述
1 SET key value 设置指定 key 的值。
2 GET key 获取指定 key 的值。
3 GETRANGE key start end 返回 key 中字符串值的子字符
4 GETSET key value 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。
5 GETBIT key offset 对 key 所储存的字符串值,获取指定偏移量上的位(bit)。
6 [MGET key1 key2..] 获取所有(一个或多个)给定 key 的值。
7 SETBIT key offset value 对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。
8 SETEX key seconds value 将值 value 关联到 key ,并将 key 的过期时间设为 seconds (以秒为单位)。
9 SETNX key value 只有在 key 不存在时设置 key 的值。
10 SETRANGE key offset value 用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始。
11 STRLEN key 返回 key 所储存的字符串值的长度。
12 [MSET key value key value ...] 同时设置一个或多个 key-value 对。
13 [MSETNX key value key value ...] 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。
14 PSETEX key milliseconds value 这个命令和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间,而不是像 SETEX 命令那样,以秒为单位。
15 INCR key 将 key 中储存的数字值增一。
16 INCRBY key increment 将 key 所储存的值加上给定的增量值(increment) 。
17 INCRBYFLOAT key increment 将 key 所储存的值加上给定的浮点增量值(increment) 。
18 DECR key 将 key 中储存的数字值减一。
19 DECRBY key decrement key 所储存的值减去给定的减量值(decrement) 。
20 APPEND key value 如果 key 已经存在并且是一个字符串, APPEND 命令将指定的 value 追加到该 key 原来值(value)的末尾。

demo

# 设置值
127.0.0.1:6379> set name lxz
OK
# 获取值
127.0.0.1:6379> get name
"lxz"
# 获取子字符串
127.0.0.1:6379> getrange name 0 1
"lx"
# 设置新值,返回旧值
127.0.0.1:6379> getset name HammerZe
"lxz"
# 获取多个值
127.0.0.1:6379> mget name age
1) "HammerZe"
2) "18"

Python操作redis,使用redis字符串命令

"""
1 set(name, value, ex=None, px=None, nx=False, xx=False)
     ex,过期时间(秒)
     px,过期时间(毫秒)
     nx,如果设置为True,则只有name不存在时,当前set操作才执行,值存在,就修改不了,执行没效果
     xx,如果设置为True,则只有name存在时,当前set操作才执行,值存在才能修改,值不存在,不会设置新值
2 setnx(name, value)
2 setex(name, value, time)
3 psetex(name, time_ms, value)
4 mset(*args, **kwargs)
5 get(name)
5 mget(keys, *args)
6 getset(name, value)
7 getrange(key, start, end)
8 setrange(name, offset, value)
9 setbit(name, offset, value)
10 getbit(name, offset)
11 bitcount(key, start=None, end=None)
12 bitop(operation, dest, *keys)
13 strlen(name)
14 incr(self, name, amount=1)
15 incrbyfloat(self, name, amount=1.0)
16 decr(self, name, amount=1)
17 append(key, value)
"""
import redis
conn = redis.Redis(host="localhost",port=6379,db=0, )

set相关操作

# conn.set('name','Hans') # 存值
# conn.setex('age',5,18) # 设置过期时间5秒
# conn.psetex('age',5000,18) # 设置过期时间为5000毫秒
# print(conn.mget('name','age'))  # 取值
# conn.setnx('text','python')
# conn.setnx('text','redis') # nx为true,text存在无法修改值,不存在可以修改
# conn.set('text','redis1',xx=True) # xx为true,text不存之无法修改,存在可以修改
# conn.mset({'text':'Linux','text1':'Python'}) # 批量设置
# conn.setrange('hobby',0,'ball')  # 从0位置开始设置ball

get相关操作

# print(conn.get('text')) # b'Linux'
# print(conn.mget(['text','text1'])) # [b'Linux', b'Python']
# print(conn.mget('text','text1'))   # [b'Linux', b'Python']
# print(conn.getset('name','monkey')) # 获取原来的name,并设置成新值,b'Hans'
# print(conn.get('name')) # b'monkey'
'''获取字节'''
# print(conn.getrange('name',0,1)) # b'mo'
# conn.set('name1','大帅逼')
# print(conn.getrange('name1',0,2).decode('utf8')) # 大

其他操作

"""统计字符串长度"""
# 英文monkey
# print(conn.strlen('name')) # 6
# 汉字大帅逼
# print(conn.strlen('name1')) # 9
"""增值age+=1"""
# print(conn.incr('age'))
"""减值age-=1"""
# print(conn.decr('age'))
"""尾部追加"""
# print(conn.append('name','nb'))  # monkeynb

Redis 哈希操作

Redis hash 是一个 string 类型的 field(字段) 和 value(值) 的映射表,hash 特别适合用于存储对象,类似python的字典;

Redis 中每个 hash 可以存储 232 - 1 键值对(40多亿)

demo:设置描述name的信息到哈希表test中

127.0.0.1:6379> hmset test name "hash" description "hashtable for test"
OK
127.0.0.1:6379> hgetall test
1) "name"
2) "hash"
3) "description"
4) "hashtable for test"
127.0.0.1:6379>

Hash常用方法

序号 命令及描述
1 [HDEL key field1 field2] 删除一个或多个哈希表字段
2 HEXISTS key field 查看哈希表 key 中,指定的字段是否存在。
3 HGET key field 获取存储在哈希表中指定字段的值。
4 HGETALL key 获取在哈希表中指定 key 的所有字段和值
5 HINCRBY key field increment 为哈希表 key 中的指定字段的整数值加上增量 increment 。
6 HINCRBYFLOAT key field increment 为哈希表 key 中的指定字段的浮点数值加上增量 increment 。
7 HKEYS key 获取所有哈希表中的字段
8 HLEN key 获取哈希表中字段的数量
9 [HMGET key field1 field2] 获取所有给定字段的值
10 [HMSET key field1 value1 field2 value2 ] 同时将多个 field-value (域-值)对设置到哈希表 key 中。
11 HSET key field value 将哈希表 key 中的字段 field 的值设为 value 。
12 HSETNX key field value 只有在字段 field 不存在时,设置哈希表字段的值。
13 HVALS key 获取哈希表中所有值。
14 [HSCAN key cursor MATCH pattern] [COUNT count] 迭代哈希表中的键值对。

demo

127.0.0.1:6379> hdel test name
(integer) 1
127.0.0.1:6379> hget test name
(nil)
127.0.0.1:6379> hgetall test
1) "description"
2) "hashtable for test"
127.0.0.1:6379> hget test description
"hashtable for test"
127.0.0.1:6379> hkeys test
1) "description"
127.0.0.1:6379> hlen test
(integer) 1
127.0.0.1:6379> hmget test description
1) "hashtable for test"
127.0.0.1:6379> hmset test name "HammerZe" age 18
OK
127.0.0.1:6379> hmget test name description age
1) "HammerZe"
2) "hashtable for test"
3) "18"
127.0.0.1:6379> hset test name HammerZEZE
(integer) 0
127.0.0.1:6379> hvals test
1) "hashtable for test"
2) "HammerZEZE"
3) "18"

Python操作redis,使用redis哈希命令

常用方法,都以h开头,存入多行数据无序

hset(name, key, value,mapping,items)  # 设置值/多个值
hmset(name, mapping)  # 批量设置多个值
hget(name,key)   # 获取值
hmget(name, keys, *args)  # 获取多个值
hgetall(name)  # 获取所有值
hlen(name)  # 获取长度(键值对个数)
hkeys(name)  # 获取所有key
hvals(name)  # 获取所有value值
hexists(name, key) # 判断key是否存在于哈希表中
hdel(name,*keys) # 删除一个值/多个值
hincrby(name, key, amount=1) # 累加,通过指定amount的值
hincrbyfloat(name, key, amount=1.0) # 累加浮点数
hscan(name, cursor=0, match=None, count=None) # 获取部分值
'''
cursor:游标,cursor=0,代表从位置0开始取
match:匹配规则
count:取多少条数据
注意:如果取100条,那么下次取会基于这次的游标位置继续往下取
'''
hscan_iter(name, match=None, count=None) # 取部分值,优化于hscan,可以一次取所有,也优化于hgetall设置count的值一般不会撑爆内存,内部使用的生成器yield

demo

import redis

conn = redis.Redis(host="localhost",port=6379,db=1)
# conn.hset('test','name','HammerZe') # 设置一个值
# conn.hset('test',mapping={'age':19,'hobby':'ball'})  # 设置多个值
# print(conn.hget('test', 'name'))  # 获取值---->b'HammerZe'
# print(conn.hmget('test',['name','age'])) # [b'HammerZe', b'19']
# print(conn.hgetall('test'))  # 获取所有值---》{b'name': b'HammerZe', b'age': b'19', b'hobby': b'ball'}
# print(conn.hlen('test'))  # 获取键值对个数---->3
# print(conn.hkeys('test'))  # 获取所有key--->[b'name', b'age', b'hobby']
# print(conn.hvals('test'))  # 获取所有value---->[b'HammerZe', b'19', b'ball']
# print(conn.hexists('test','name'))  # 判断该哈希表中是否有该key---->True
# print(conn.hdel('test','name'))  # 删除值
# print(conn.hincrby('test','age',amount=2))  # 累加,每次加2岁  --->21
# print(conn.hincrbyfloat('test','age',amount=2.2))  # 累加浮点数  --->23.2

# hscan获取部分值
# 写入值
# for i in range(1000):
#     conn.hset('test1',f"key-->{i}",i)
# hscan获取值
# res=conn.hscan('test1', cursor=0, count=10)
# print(res)
# print(len(res[1]))  # 100

# hscan_iter: #全取出所有值,分批取,不是一次性全取回来,减小内存占用
# res =conn.hscan_iter('test1',count=10)
# print(res)  # <generator object ScanCommands.hscan_iter at 0x0000015B14BD2D58>
# for i in res:
#     print(i)

Redis 列表操作

Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)

一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。

Redis 列表命令

下表列出了列表相关的基本命令:

序号 命令及描述
1 [BLPOP key1 key2 ] timeout 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
2 [BRPOP key1 key2 ] timeout 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
3 BRPOPLPUSH source destination timeout 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
4 LINDEX key index 通过索引获取列表中的元素
5 LINSERT key BEFORE|AFTER pivot value 在列表的元素前或者后插入元素
6 LLEN key 获取列表长度
7 LPOP key 移出并获取列表的第一个元素
8 [LPUSH key value1 value2] 将一个或多个值插入到列表头部
9 LPUSHX key value 将一个值插入到已存在的列表头部
10 LRANGE key start stop 获取列表指定范围内的元素
11 LREM key count value 移除列表元素
12 LSET key index value 通过索引设置列表元素的值
13 LTRIM key start stop 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
14 RPOP key 移除列表的最后一个元素,返回值为移除的元素。
15 RPOPLPUSH source destination 移除列表的最后一个元素,并将该元素添加到另一个列表并返回
16 [RPUSH key value1 value2] 在列表中添加一个或多个值
17 RPUSHX key value 为已存在的列表添加值

demo

127.0.0.1:6379> lpush testlist redis
(integer) 1
127.0.0.1:6379> lpush testlist mysql
(integer) 2
127.0.0.1:6379> lpush testlist python
(integer) 3
127.0.0.1:6379> lrange testlist 0 3
1) "python"
2) "mysql"
3) "redis"

# 弹出值,如果没有10秒后返回nil
127.0.0.1:6379> blpop testlist 10
1) "testlist"
2) "python"
127.0.0.1:6379> blpop testlist 10
1) "testlist"
2) "mysql"
127.0.0.1:6379> blpop testlist 10
1) "testlist"
2) "redis"
127.0.0.1:6379> blpop testlist 10
(nil)
(10.07s)

Python操作redis,使用redis哈希命令

常见方法

PS:图形界面看到的 上面是左,下面是右
lpush(name,values)   # 从列表左边插入
lpushx(name,value)   # 从列表左边插入,如果存在,才能操作
llen(name)          # 求长度
linsert(name, where, refvalue, value) # 指定位置插入值 
r.lset(name, index, value)  # 对name对应的list中的某一个索引位置重新赋值
r.lrem(name, count,value)  # 删除 
'''
name:list名
value:要删除的值
num=0:要删除所有指定的值
num=1:删除一个
num=2:删除两个
num=-2:从后往前删2个
'''
lpop(name)  # 从左侧弹出
rpop(name)  # 从右侧弹出
lindex(name, index)  # 获取索引位置为index的值lindex('list',0)
lrange(name, start, end) # 范围取,全闭区间
ltrim(name, start, end)   # 保留start-end范围内的值,移除其他值
rpoplpush(src, dst) # 从一个列表取出最右边的元素,同时将其添加到另外一个列表的最左边
blpop(keys, timeout) # 从左边弹出值,如果没有timeout秒后返回nil(生产者消费者模型,消息队列)
brpoplpush(src, dst, timeout=0)

demo

import redis

conn = redis.Redis(host="localhost",port=6379,db=2)

# conn.lpush('mylist','Hammer1','Hammer2')
# conn.rpush('mylist','Hammer3')
# conn.lpushx('mylist','Hammer4')  # 存在name,从左边插入成功
# print(conn.llen('mylist'))  # 4
# conn.linsert('mylist','before','Hammer4','Hammer5') # 在谁前插入
# conn.linsert('mylist','after','Hammer5','Hammer6')  # 在谁后插入
# conn.lset('mylist',0,'位置0重新赋值')
# conn.lrem('mylist',1,'位置0重新赋值')  # 从左侧删除第一个值
# conn.lrem('mylist',-1,'Hammer3')  # 从右侧删除第一个值
# conn.lrem('mylist','0','Hammer')  # 从0开始,后面Hammer全删
# res = conn.lpop('mylist') # 从左往外弹出
# print(res) # b'Hammer5'
# print(conn.lindex('mylist',1))  # 获取索引为1的值---> b'Hammer2'
# print(conn.lrange('mylist',0,1))  # 获取范围内的值,[b'Hammer4', b'Hammer2']
# conn.ltrim('mylist',0,1) # 除了范围内的,都删除
# conn.blpop('mylist',timeout=2)  # 阻塞式弹出,如果没有值,会一直阻塞
# 作用,可以实现分布式的系统---》分布式爬虫
# 爬网页,解析数据,存入数据库一条龙,一个程序做
# 写一个程序,专门爬网页---》中间通过redis的列表做中转
# 再写一个程序专门解析网页存入数据库

Python内使用Redis公共操作

公共操作与类型无关

常用方法

delete(*names)  # 删除单个name或者多个name
exists(name)  # 判断是否存在name
keys(pattern='*')  # 获取匹配的key,不写默认获取所有,支持模糊匹配
'''
模糊查询:
?代表匹配单个
*代表匹配任意长度
[ab]匹配ab:hel[ab]lo
'''
expire(name ,time)  # 设置过期时间
rename(src, dst)  # name重命名,src原来的,dst新的
move(name, db)  # 把指定的name移动到指定db下
randomkey()  # 随机返回key值
type(name)   # 查看value类型

demo

import  redis
conn = redis.Redis(host="localhost",port=6379,db=0)

# conn.delete('age','test')  # 删除age和test
# print(conn.exists('age'))  # 0
# print(conn.exists('name','name1'))  # 返回存在的个数--->2
# conn.expire('name',3)  # 设置3秒后,name过期
# conn.rename('hobby','myhobby')  # 重命名
# conn.move('text',db=1)  # 移动text到db1下
# print(conn.randomkey())  # 随机返回key
# print(conn.type('text1'))  # b'string'

Redis 管道

Redis是非关系型数据库,本身是不支持事务,redis中的管道可以实现事务的支持,要么都成功要么都失败,那么实现的原理就是放入一个管道中一次性执行;

import redis

pool = redis.ConnectionPool() # 创建池
conn = redis.Redis(connection_pool=pool)  # 实例化

pipe = conn.pipeline(transaction=True)
pipe.multi()  # 管道等待放入多条命令
# 以后用pipe代替conn操作
pipe.set('name', 'HammerZe')

pipe.set('role', 'nb')

# 到此是往管道中放了命令,还没执行,直到下面的命令
pipe.execute() # 一次性执行管道中的所有命令

"""
如果是集群环境,不支持管道,数据过于分散,"锁不住"
"""

【待续】

posted @ 2022-05-02 01:14  HammerZe  阅读(501)  评论(2编辑  收藏  举报