Redis

https://www.cnblogs.com/pyedu/p/12452407.html

 

2. Redis 安装

Windows 下安装

下载地址:https://github.com/tporadowski/redis/releases

Redis 支持 32 位和 64 位。这里我们下载 Redis-x64-xxx.zip压缩包到C盘,解压后,将文件夹重新命名为 redis

 

 

 打开一个 cmd 窗口 使用 cd 命令切换目录到 C:\redis 运行:

redis-server.exe redis.windows.conf

如果想方便的话,可以把 redis 的路径加到系统的环境变量里,这样就省得再输路径了,后面的那个 redis.windows.conf 可以省略,如果省略,会启用默认的。输入之后,会显示如下界面:

 

 

 

这时候另启一个 cmd 窗口,原来的不要关闭,不然就无法访问服务端了。

切换到 redis 目录下运行:

redis-cli.exe -h 127.0.0.1 -p 6379
设置键值对:
set myKey abc
取出键值对:
get myKey

 

 

 在linux上安装redis

要在 Ubuntu 上安装 Redis,打开终端,然后输入以下命令:

$sudo apt-get update
$sudo apt-get install redis-server
这将在您的计算机上安装Redis
启动 Redis


$redis-server
查看 redis 是否还在运行


$redis-cli
这将打开一个 Redis 提示符,如下图所示:

redis 127.0.0.1:6379>
在上面的提示信息中:127.0.0.1 是本机的IP地址,6379是 Redis 服务器运行的端口。现在输入 PING 命令,如下图所示:

redis 127.0.0.1:6379> ping
PONG
这说明现在你已经成功地在计算机上安装了 Redis。

2. redis的连接方式

如果报错: 

ModuleNotFoundError: No module named redis

则需要在terminal中pip install redis.

# 其中,redis是针对python的接口
import
redis # 01 基本连接方式(不推荐使用,简单但效率较低,因为每做一次操作要进行一次连接) r=redis.Redis(host="127.0.0.1",port=6379) r.set("age",99) print(r.get("age")) # b'99' # 02 基于连接池连接(推荐使用,效率高,因为避免重复连接)
pool = redis.ConnectionPool(host='127.0.0.1', port=6379)
r = redis.Redis(connection_pool=pool)

 

3. 操作

1. string类型:
    字符串类型是 Redis 中最为基础的数据存储类型,它在 Redis 中是二进制安全的,也就是byte类型
    最大容量是512M。
2. hash类型:
        hash用于存储对象,对象的结构为属性、值,值的类型为string。
        key:{
            域:值[这里的值只能是字符串],
            域:值,            
            域:值,
            域:值,
            ...
        }
3. list类型:
        列表的元素类型为string。
        key:[ 值1,值2,值3..... ]
4. set类型:
    无序集合,元素为string类型,元素唯一不重复,没有修改操作。
    {值1,值4,值3,值5}
5. zset类型:
    有序集合,元素为string类型,元素唯一不重复,没有修改操作。

3.1 String操作

redis中的String在在内存中按照一个name对应一个value来存储。如图:

 

 

 set(name, value, ex=None, px=None, nx=False, xx=False)

在Redis中设置值,默认,不存在则创建,存在则修改
参数:
     ex,过期时间(秒)
     px,过期时间(毫秒)
     nx,如果设置为True,则只有name不存在时,当前set操作才执行
     xx,如果设置为True,则只有name存在时,岗前set操作才执行
setnx(name, value)
设置值,只有name不存在时,执行设置操作(添加)

setex(name, value, time)
# 设置值
# 参数:
# time,过期时间(数字秒 或 timedelta对象)

psetex(name, time_ms, value)
# 设置值
# 参数:
# time_ms,过期时间(数字毫秒 或 timedelta对象)

mset(*args, **kwargs)
批量设置值
如:
    mset(k1='v1', k2='v2')
    或
    mget({'k1': 'v1', 'k2': 'v2'})
get(name)


获取值
mget(keys, *args)
批量获取
如:
    mget('ylr', 'wupeiqi')
    或
    r.mget(['ylr', 'wupeiqi'])
getset(name, value)


设置新值并获取原来的值
getrange(key, start, end)
# 获取子序列(根据字节获取,非字符)
# 参数:
    # name,Redis 的 name
    # start,起始位置(字节)
    # end,结束位置(字节)
setrange(name, offset, value)

# 修改字符串内容,从指定字符串索引开始向后替换(新值太长时,则向后添加)
# 参数:
    # offset,字符串的索引,字节(一个汉字三个字节)
    # value,要设置的值
strlen(name)

# 返回name对应值的字节长度(一个汉字3个字节)
incr(self, name, amount=1)


# 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。
  
# 参数:
    # name,Redis的name
    # amount,自增数(必须是整数)
  
# 注:同incrby
incrbyfloat(self, name, amount=1.0)


# 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。
  
# 参数:
    # name,Redis的name
    # amount,自增数(浮点型)
decr(self, name, amount=1)


# 自减 name对应的值,当name不存在时,则创建name=amount,否则,则自减。
  
# 参数:
    # name,Redis的name
    # amount,自减数(整数)
append(key, value)


# 在redis name对应的值后面追加内容
  
# 参数:
    key, redis的name
    value, 要追加的字符串
import redis
pool = redis.ConnectionPool(host='127.0.0.1', port=6379)
r = redis.Redis(connection_pool=pool)


r.set("name","alex",5) # 5表示过期时间, 5s, 超过5s就取不出来了, 结果是None
# print(r.get("name"))

# r.set("name","alex",nx=True) # nx=True表示当name不存在时, 当前操作才有效
# r.set("name","alex")


# 批量设置
# r.mset({'k1': 'v1', 'k2': 'v2'})

# 取值操作
# print(r.get("k2")) # b'v2'

# 批量取值
# print(r.mget(["k1","k2"])) # [b'v1', b'v2']


# print(r.getrange("name",0,2))

# r.setrange("name",3,"!!!")

print(r.strlen("name"))


# r.incr("age",2)

# r.append("name","egon")

3.2 Hash操作

hash表现形式上有些像pyhton中的dict,可以存储一组关联性较强的数据 , redis中Hash在内存中的存储格式如下图:  

hset(name, key, value)
# name对应的hash中设置一个键值对(不存在,则创建;否则,修改)
  
# 参数:
    # name,redis的name
    # key,name对应的hash中的key
    # value,name对应的hash中的value
  
# 注:
    # hsetnx(name, key, value),当name对应的hash中不存在当前key时则创建(相当于添加)


hmset(name, mapping)
# 在name对应的hash中批量设置键值对
# 参数:
    # name,redis的name
    # mapping,字典,如:{'k1':'v1', 'k2': 'v2'}
# 如:
    # r.hmset('xx', {'k1':'v1', 'k2': 'v2'})


hget(name,key)
# 在name对应的hash中获取根据key获取value
hmget(name, keys, *args) # 在name对应的hash中获取多个key的值 # 参数: # name,reids对应的name # keys,要获取key集合,如:['k1', 'k2', 'k3'] # *args,要获取的key,如:k1,k2,k3 # 如: # r.mget('xx', ['k1', 'k2']) # # print r.hmget('xx', 'k1', 'k2')
hgetall(name) # 获取name对应hash的所有键值
hlen(name)  # 获取name对应的hash中键值对的个数
hkeys(name) # 获取name对应的hash中所有的key的值

hvals(name) # 获取name对应的hash中所有的value的值

hexists(name, key) # 检查name对应的hash是否存在当前传入的key

hdel(name,*keys) # 将name对应的hash中指定key的键值对删除

hincrby(name, key, amount=1) # 自增name对应的hash中的指定key的值,不存在则创建key=amount # 参数: # name,redis中的name # key, hash对应的key # amount,自增数(整数)

hincrbyfloat(name, key, amount=1.0) # 自增name对应的hash中的指定key的值,不存在则创建key=amount # 参数: # name,redis中的name # key, hash对应的key # amount,自增数(浮点数)

hscan_iter(name, match=None, count=None) # 利用yield封装hscan创建生成器,实现分批去redis中获取数据 # 参数: # match,匹配指定key,默认None 表示所有的key # count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数 # 如: # for item in r.hscan_iter('xx'): # print item

 

import redis
pool = redis.ConnectionPool(host='127.0.0.1', port=6379)
r = redis.Redis(connection_pool=pool)

#
# r.hset("infos","name","alex")
# print(r.hget("infos","name")) # b'alex'

# r.hmset("infos",{"age":1000,"gender":"male"})
# print(r.hmget("infos",["name","age"])) # [b'alex', b'1000']

# print(r.hgetall("infos")) # {b'name': b'alex', b'age': b'1000', b'gender': b'male'}

# print(r.hlen("infos")) # 3

# print(r.hkeys("infos")) # [b'name', b'age', b'gender']
# print(r.hvals("infos")) # [b'alex', b'1000', b'male']
#
# print(r.hexists("infos","names"))

# r.hdel("infos","gender")
# print(r.hgetall("infos")) # {b'name': b'alex', b'age': b'1000'}

# r.hincrby("infos","age",2)

# print(r.hgetall("infos")) # {b'name': b'alex', b'age': b'1002', b'gender': b'male'}

# print(r.hscan_iter("infos"))
# <generator object Redis.hscan_iter at 0x000001FBF4CAE0C0>

# for i in r.hscan_iter("infos"):
#     print(i)
# (b'name', b'alex')
# (b'age', b'1004')
# (b'gender', b'male')

# r.hmset("abc",{"a1":"b1","a2":"b2","a3":"b3","x1":"y1"})


# for i in r.hscan_iter("abc",match="a*"):
#     print(i)

# (b'a1', b'b1')
# (b'a2', b'b2')
# (b'a3', b'b3')

 3.3 List操作

List操作,redis中的List在在内存中按照一个name对应一个List来存储。如图:  

lpush(name,values)
# 在name对应的list中添加元素,每个新的元素都添加到列表的最左边
  
# 如:
    # r.lpush('oo', 11,22,33)
    # 保存顺序为: 33,22,11
  
# 扩展:
    # rpush(name, values) 表示从右向左操作

lpushx(name,value) # 在name对应的list中添加元素,只有name已经存在时,值添加到列表的最左边 # 更多: # rpushx(name, value) 表示从右向左操作

llen(name) # name对应的list元素的个数

linsert(name, where, refvalue, value)) # 在name对应的列表的某一个值前或后插入一个新值 # 参数: # name,redis的name # where,BEFORE或AFTER # refvalue,标杆值,即:在它前后插入数据 # value,要插入的数据

r.lset(name, index, value) # 对name对应的list中的某一个索引位置重新赋值 # 参数: # name,redis的name # index,list的索引位置 # value,要设置的值

r.lrem(name, value, num)  # 在name对应的list中删除指定的值 # 参数: # name,redis的name # value,要删除的值 # num, num=0,删除列表中所有的指定值; # num=2,从前到后,删除2个; # num=-2,从后向前,删除2个

lpop(name) # 在name对应的列表的左侧获取第一个元素并在列表中移除,返回值则是第一个元素 # 更多: # rpop(name) 表示从右向左操作

lindex(name, index) # 在name对应的列表中根据索引获取列表元素

lrange(name, start, end) # 在name对应的列表分片获取数据 # 参数: # name,redis的name # start,索引的起始位置 # end,索引结束位置

ltrim(name, start, end) # 在name对应的列表中移除没有在start-end索引之间的值 # 参数: # name,redis的name # start,索引的起始位置 # end,索引结束位置
import redis
pool = redis.ConnectionPool(host='127.0.0.1', port=6379)
r = redis.Redis(connection_pool=pool)

# r.lpush("scores",56,78,99,65,34)
#
# r.rpush("new_scores",23,56,77)

# print(r.lrange("scores",0,-1)) # [b'34', b'65', b'99', b'78', b'56']
# print(r.lrange("new_scores",0,-1)) # [b'23', b'56', b'77']

# r.lpushx("scores",100)
#
# print(r.llen("scores"))  # 6
#
# r.linsert("scores","AFTER","34","44")
# print(r.lrange("scores",0,-1))
#  [b'100', b'34', b'44', b'65', b'99', b'78', b'56']

# r.lset("scores",1,98)
# print(r.lrange("scores",0,-1))
# [b'100', b'98', b'44', b'65', b'99', b'78', b'56']

# r.lrem("scores",count=0,value=98) # count=0表示全删除, 即把所有的98都删除. count=2从左向右删除前2个. count=-2表示从右向左删除前2个.
# print(r.lrange("scores",0,-1))
# #  [b'100', b'44', b'65', b'99', b'78', b'56']

# print(r.lpop("scores"))
# b'100', 删除了100
# print(r.lrange("scores",0,-1))
# [b'44', b'65', b'99', b'78', b'56']

# print(r.lindex("scores",3))
# b'78'

# print(r.lrange("scores",0,-1))
# print(r.lrange("scores",1,3))


# r.ltrim("scores",1,3) # 把1-3范围内的值保留下来, 删除其他值
# print(r.lrange("scores",0,-1))
# [b'65', b'99', b'78']

 2.4 Set操作

Set操作,Set集合就是不允许重复的列表

sadd(name,values)
# name对应的集合中添加元素

scard(name)
# 获取name对应的集合中元素个数

sdiff(keys, *args)
# 在第一个name对应的集合中且不在其他name对应的集合的元素集合

sinter(keys, *args)
# 获取多一个name对应集合的交集

sismember(name, value)
# 检查value是否是name对应的集合的成员

smembers(name)
# 获取name对应的集合的所有成员

spop(name)
# 从集合中随机移除一个成员,并将其返回

srandmember(name, numbers)
# 从name对应的集合中随机获取 numbers 个元素

srem(name, values)
# 在name对应的集合中删除某些值

sunion(keys, *args)
# 获取多一个name对应的集合的并集

sscan_iter(name, match=None, count=None)
# 同字符串的操作,用于增量迭代分批获取元素,避免内存消耗太大
import redis
pool = redis.ConnectionPool(host='127.0.0.1', port=6379)
r = redis.Redis(connection_pool=pool)



# r.sadd("score_set01",1,2,3,4,5,6,6)
# r.sadd("score_set02",4,5,6,7,8)


# print(r.smembers("score_set01"))
# {b'6', b'1', b'3', b'4', b'5', b'2'}
# print(r.smembers("score_set02"))
#{b'6', b'4', b'5', b'7', b'8'}

# print(r.scard("score_set01"))
# 6
# print(r.scard("score_set02"))
# 5
# print(r.sinter("score_set01","score_set02")) # 交集
#  {b'4', b'5', b'6'}
# print(r.sunion("score_set01","score_set02"))  # 并集
#  {b'3', b'1', b'4', b'8', b'2', b'6', b'7', b'5'}
# print(r.sdiff("score_set01","score_set02"))  # 差集
# {b'3', b'2', b'1'}
#
#
# print(r.sismember("score_set01",6))
# True
# print(r.sismember("score_set01",7))
# False

# print(r.spop("score_set01"))
# b'2' 随机删除
# print(r.smembers("score_set01"))
# {b'1', b'4', b'5', b'6', b'3'}
# print(r.srandmember("score_set01",3))
# [b'1', b'3', b'5'] 随机取3个元素

# r.srem("score_set01",5)
# print(r.smembers("score_set01"))
# {b'4', b'3', b'1', b'6'}

# print(r.sscan_iter("score_set01"))
# <generator object Redis.sscan_iter at 0x000002786EFAF048>

# for i in r.sscan_iter("score_set01"):
#     print(i)
# b'1'
# b'3'
# b'4'
# b'6'

2.5 sort set操作

有序集合,在集合的基础上,为每元素排序;元素的排序需要根据另外一个值来进行比较,所以,对于有序集合,每一个元素有两个值,即:值和分数,分数专门用来做排序。

zadd(name, *args, **kwargs)
# 在name对应的有序集合中添加元素
# 如:print(r.zadd("zz",{"n1":1,"n2":2,"n3":3,"n4":4}))<br><br># 查看:print(r.zscan("zz"))

zcard(name) # 获取name对应的有序集合元素的数量

zcount(name, min, max) # 获取name对应的有序集合中分数 在 [min,max] 之间的个数

zincrby(name, value, amount) # 自增name对应的有序集合的 name 对应的分数

zrange( name, start, end, desc=False, withscores=False, score_cast_func=float) # 按照索引范围获取name对应的有序集合的元素 aa=r.zrange("zset_name",0,1,desc=False,withscores=True,score_cast_func=int) print(aa) '''参数: name redis的name start 有序集合索引起始位置 end 有序集合索引结束位置 desc 排序规则,默认按照分数从小到大排序 withscores 是否获取元素的分数,默认只获取元素的值 score_cast_func 对分数进行数据转换的函数''' zscore(name, value) #获取name对应有序集合中 value 对应的分数

zrank(name, value) # 获取某个值在 name对应的有序集合中的排行(从 0 开始) # 更多: # zrevrank(name, value),从大到小排序

zrem(name, values) # 删除name对应的有序集合中值是values的成员 # 如:zrem('zz', ['s1', 's2'])

zremrangebyrank(name, min, max) # 根据排行范围删除

zremrangebyscore(name, min, max) # 根据分数范围删除

zinterstore(dest, keys, aggregate=None) # 获取两个有序集合的交集,如果遇到相同值不同分数,则按照aggregate进行操作 # aggregate的值为: SUM MIN MAX

zunionstore(dest, keys, aggregate=None) # 获取两个有序集合的并集,如果遇到相同值不同分数,则按照aggregate进行操作 # aggregate的值为: SUM MIN MAX
import redis
pool = redis.ConnectionPool(host='127.0.0.1', port=6379)
r = redis.Redis(connection_pool=pool)


# r.zadd("z",{"n1":1,"n2":2,"n3":5,"n4":3})

# print(r.zscan("z"))
# (0, [(b'n1', 1.0), (b'n2', 2.0), (b'n4', 3.0), (b'n3', 5.0)]) 0指游标, 1.0, 2.0等指的是分数
# print(r.zcard("z"))
# 4

# print(r.zcount("z",1,3))
# 3 分数在1和3之间的有n1, n2, n4三个元素
# print(r.zcount("z",1,4))
# 3
# print(r.zcount("z",1,5))
# 4

# r.zincrby("z",2,"n3")
# print(r.zscan("z"))
# (0, [(b'n1', 1.0), (b'n2', 2.0), (b'n4', 3.0), (b'n3', 7.0)])
# 对分数自增, 对n3对应的分数值自增2

# print(r.zrange("z",0,2))
# 0,2对应起始和终止索引
# [b'n1', b'n2', b'n4']

# print(r.zrange("z",0,3))
# [b'n1', b'n2', b'n4', b'n3']

# print(r.zscore("z","n4"))
# 3.0 获取分数
# print(r.zrank("z","n4"))
# 2  获取n4的排行


# r.zrem("z","n2")
#  删除n2

# r.zremrangebyrank("z",0,1)  # 按照排名进行删除
# r.zremrangebyscore("z",1,7) # 按照分数进行删除
# print(r.zscan("z"))
# (0, [])



# r.zadd("z1",{"n1":1,"n2":2,"n3":3,"x":100})
# r.zadd("z2",{"n3":4,"n5":5,"n6":6,"x":100})

# r.zunionstore("z3",("z1","z2"))
# print(r.zscan("z3"))
# 并集, 值相同时分数相加(sum, 如果aggregate是max, min则采用最大值或最小值).
# (0, [(b'n1', 1.0), (b'n2', 2.0), (b'n5', 5.0), (b'n6', 6.0), (b'n3', 7.0), (b'x', 200.0)])

# r.zinterstore("z4",("z1","z2"))
# print(r.zscan("z4"))
# 交集, 后面的分数会根据aggregate的设置取值.
# (0, [(b'n3', 7.0), (b'x', 200.0)])

2.6 其他常用操作

delete(*names)
# 根据删除redis中的任意数据类型

exists(name)
# 检测redis的name是否存在

keys(pattern='*')
# 根据模型获取redis的name
  
# 更多:
    # KEYS * 匹配数据库中所有 key 。
    # KEYS h?llo 匹配 hello , hallo 和 hxllo 等。
    # KEYS h*llo 匹配 hllo 和 heeeeello 等。
    # KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo

expire(name ,time)
# 为某个redis的某个name设置超时时间

rename(src, dst)
# 对redis的name重命名为

randomkey()
# 随机获取一个redis的name(不删除)

type(name)
# 获取name对应值的类型

scan_iter(match=None, count=None)
# 同字符串操作,用于增量迭代获取key
import redis
pool = redis.ConnectionPool(host='127.0.0.1', port=6379)
r = redis.Redis(connection_pool=pool)

# print(r.keys(pattern="k*"))
# [b'k1', b'k2']
# print(r.delete("name"))
# 0, 返回删除的元素的索引
# print(r.keys())
# 获取所有的key
# [b'k1', b'myKey', b'z3', b'z1', b'scores', b'score_set02', b'score_set01', b'z2', b'infos', b'age', b'k2', b'abc', b'z4', b'new_scores']

# print(r.exists("name"))
# 0 不存在
# print(r.exists("naem"))
# 0 不存在

# r.expire("z2",10)
# print(r.keys())
# 10s后打印不出来z2

# print(r.randomkey())
# 随机获取
# print(r.type("infos"))
# b'hash'

# for i in r.scan_iter(match="k*"):
#     print(i)
# b'k1'
# b'k2'

 3. 使用场景

针对各种数据类型使用场景如下:

复制代码
(一)String

这个其实没啥好说的,最常规的set/get操作,value可以是String也可以是数字。一般做一些复杂的计数功能的缓存,比如减少库存。


(二)hash


这里value存放的是结构化的对象,比较方便的就是操作其中的某个字段。博主在做单点登录的时候,就是用这种数据结构存储用户信息,以cookieId作为key,设置30分钟为缓存过期时间,能很好的模拟出类似session的效果。


(三)list


使用List的数据结构,可以做简单的消息队列的功能。另外还有一个就是,可以利用lrange命令,做基于redis的分页功能,性能极佳,用户体验好。本人还用一个场景,很合适---取行情信息。就也是个生产者和消费者的场景。LIST可以很好的完成排队,先进先出的原则。


(四)set


因为set堆放的是一堆不重复值的集合。所以可以做全局去重的功能。

另外,就是利用交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的喜好等功能。


(五)sorted set


sorted set多了一个权重参数score,集合中的元素能够按score进行排列。可以做排行榜应用,取TOP N操作。

4. 管道

redis-py默认在执行每次请求都会创建(连接池申请连接)和断开(归还连接池)一次连接操作,如果想要在一次请求中指定多个命令,则可以使用pipline实现一次请求指定多个命令,并且默认情况下一次pipline 是原子性操作。

import redis
  
pool = redis.ConnectionPool(host='10.211.55.4', port=6379)
r = redis.Redis(connection_pool=pool)
# pipe = r.pipeline(transaction=False)
pipe = r.pipeline(transaction=True)
 # 以下两个操作要么同时成功, 要么同时失败.
pipe.set('name', 'alex')
pipe.set('role', 'sb')
  
pipe.execute()

5. 发布订阅

订阅者:

import redis
 
r=redis.Redis(host='127.0.0.1')
pub=r.pubsub()
 
pub.subscribe("fm104.5")
pub.parse_response()
 
while 1:
    msg = pub.parse_response()
    print(msg) 

发布者:

import redis
 
r=redis.Redis(host='127.0.0.1')
r.publish("fm104.5", "Hi,yuan!")

发布订阅的特性用来做一个简单的实时聊天系统再适合不过了,当然这样的东西开发中很少涉及到。再比如在分布式架构中,常常会遇到读写分离的场景,在写入的过程中,就可以使用redis发布订阅,使得写入值及时发布到各个读的程序中,就保证数据的完整一致性。再比如,在一个博客网站中,有100个粉丝订阅了你,当你发布新文章,就可以推送消息给粉丝们拉。

posted @ 2021-01-25 10:37  自由者妍  阅读(54)  评论(0)    收藏  举报