一、简单介绍

redis是一个key-value存储系统,和Memcached类似。它支持存储的value类型相对更多,包括string(字符串)、list(链表、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。

这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。

与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

二、软件包


yum install  epel-release   # 保证安装 epel 源
yum install redis



命令列表



redis-server                # Redis 服务器
redis-cli                   # Redis 自带的客户端
redis-benchmark             # Redis 性能测试工具
redis-check-aof             # AOF 文件修复工具



三、 启动服务



[root@kube-master ~]# systemctl start redis



检查服务状态




redis的操作是不是原子操作 redis哪些操作是原子的_setnx是原子操作吗


检查端口

Redis 默认监听 6379 端口


redis的操作是不是原子操作 redis哪些操作是原子的_redis_02


四、基本使用

1. 连接到 redis-server


使用默认端口连接到本地 redis 服务
[root@kube-master ~]# redis-cli
127.0.0.1:6379>


退出输入客户端 exit

2. 增删改查数据

增加

set


127.0.0.1:6379> help set
SET key value [EX seconds] [PX milliseconds] [NX|XX]


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

Example


127.0.0.1:6379> set name shark EX 10
OK


获取

get


127.0.0.1:6379> get name
"shark"
111


查询过期时间

ttl

查看一个 key 的过期时间


127.0.0.1:6379> ttl name
(integer) 2   # 距离过期时间,剩余的秒数


  • -1 永不过期
  • -2 已经过期

指定过期时间

expire

设置一个 key 的过期时间(单位: 秒)


127.0.0.1:6379> set age 18
OK
127.0.0.1:6379> ttl age
(integer) -1
127.0.0.1:6379> EXPIRE age 20
(integer) 1
127.0.0.1:6379> ttl age
(integer) 18
127.0.0.1:6379> ttl age
(integer) 14
127.0.0.1:6379>


删除

del


[root@kube-master ~]# redis-cli set name shark
OK
[root@kube-master ~]# redis-cli get name
"shark"
[root@kube-master ~]# redis-cli del name
(integer) 1
[root@kube-master ~]# redis-cli get name
(nil)


3. 配置文件

路径 /etc/redis.conf


# 设置监听地址
bind 127.0.0.1

# 设置监听端口
port 6379

# 设置密码为 foo
requirepass foo


4. 其他连接方式(扩展自修)


redis的操作是不是原子操作 redis哪些操作是原子的_setnx是原子操作吗_03


三、Python 操作 Redis

没必要全讲,所见即所得的东西,讲一部分,其他学生按照笔记看看。不要求都掌握。

1. 安装包


pip3 install redis


2. 连接


In [1]: import redis

In [2]: r = redis.Redis(host='10.18.42.174', port=6379)

In [3]: r.set("QF", "www.qfedu.com")
Out[3]: True

In [4]: r.get("QF")
Out[4]: b'www.qfedu.com'

In [5]: ret = r.get("QF")

In [6]: str(ret, encoding='utf-8')
Out[6]: 'www.qfedu.com'


3. 连接池

redis-py使用connection pool来管理对一个redis server的所有连接,避免每次建立、释放连接的开销。

默认,每个Redis实例都会维护一个自己的连接池。

可以直接建立一个连接池,然后作为参数传给Redis,这样就可以实现多个 Redis 实例共享一个连接池


In [12]: pool = redis.ConnectionPool(host='172.16.153.134', port=6379)

In [13]: rs = redis.Redis(connection_pool=pool)

In [14]: rs.set("foo", "bar")
Out[14]: True

In [15]: rs.get("foo")
Out[15]: b'bar'


4. 基本操作

  • String 操作

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


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

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

setnx(name, value)

  # 设置值,只有name不存在时,执行设置操作(添加)

setex(name, value, time)

"""
 设置值
 参数:
     time,过期时间(数字秒)
"""

psetex(name, time_ms, value)

"""
 设置值
 参数:
     time_ms,过期时间(数字毫秒)
"""

mset(*args, **kwargs)

"""
批量设置值:
    mset(QF='www.qfedu.com', yangge='杨哥')
    或
    mget({'k1': 'v1', 'k2': 'v2'})
"""

get(name)

    # 获取值

mget(keys, *args)
"""
批量获取:
    mget('QF', 'foo')
    或
    mget(['QF', 'foo'])
"""


getset(name, value)

# 设置新值并获取原来的值

getrange(key, start, end)

# 以字节的方式获取一部分值(一个汉字等于三个字节)

setrange(name, offset, value)
"""
修改字符串内容,从指定字符串索引开始向后替换(新值太长时,则向后添加)
参数:
   offset  字符串的索引,字节方式
"""

incr(self, name, amount=1)

"""
 自增 name对应的值,当name不存在时,则创建 name=amount,否则,则自增。

 参数:
     name,Redis的name
     amount,自增数(必须是整数)
"""

decr(self, name, amount=1)
# 自减

append(key, value)

# 在redis name对应的值后面追加内容


---

-------- 以下不讲,到第三阶段之后,再回来自修----------

  • List 操作

rpush(name, value1[,vlue2]) 向列表中追加元素


In [36]: r.rpush('l1',1,2)                                       
Out[36]: 2

In [37]: r.lrange('l1',0,-1)                                     
Out[37]: [b'1', b'2']

In [38]: r.rpush('l1',3)                                         
Out[38]: 3

In [39]: r.lrange('l1',0,-1)                                     
Out[39]: [b'1', b'2', b'3']



lpush(name,value1[, value2]) 向列表的头部插入元素


In [39]: r.lrange('l1',0,-1)                                     
Out[39]: [b'1', b'2', b'3']

In [40]: r.lpush('l1', 10,20)                                    
Out[40]: 5

In [41]: r.lrange('l1',0,-1)                                     
Out[41]: [b'20', b'10', b'1', b'2', b'3']


llen(name) 列表中元素的个数


In [43]: r.llen('l1')                                            
Out[43]: 5



lindex(name, index) 根据索引号获取列表中的元素

索引号从 0 开始,支持 负数的索引号 列表中第一个元素的索引号是 0, 最后元素的索引号可以是 -1


lrange(name, start, end) 切片, 根据索引返回获取元素

  • name,redis的name
  • start,索引的起始位置
  • end,索引结束位置, 这个不同于 python 的切片,这个会包含结束索引号对应的位置。
In [53]: r.llen('l1')                                            
Out[53]: 5

In [54]: r.lrange('l1', 0, -1)               // 获取所有的值                    
Out[54]: [b'10', b'hello', b'1', b'2', b'QF']

In [55]: r.lrange('l1', 1, 1)                                    
Out[55]: [b'hello']

In [56]: r.lrange('l1', 1, 2)                                    
Out[56]: [b'hello', b'1']



linsert(name, where, refvalue, value)) 向一个元素的前面或者后面插入一个值

  • name 列表名
  • where 的值可以是 "BEFORE" 或者 "AFTER"
  • refvalue 一个已存在于列表中的值,加入值不存在于列表中,返回 0 ,操作无效
  • value 插入的值
// 向元素 1 前面插入元素 hello
In [45]: r.linsert('l1', "BEFORE", 1, 'hello')                   
Out[45]: 6           // 返回值是列表改变后的元素个数

// 向 元素 2 的后面插入元素 QF
In [47]: r.linsert('l1', "AFTER", 2, 'QF')                       
Out[47]: 7

In [48]: r.lrange('l1',0,-1)                                     
Out[48]: [b'20', b'10', b'hello', b'1', b'2', b'QF', b'3']



r.lset(name, index, value) 对列表中的某一个索引位置重新赋值


r.lrem(name, value, num) 删除列表中的某个元素

  • value 被删除的元素
  • num 是删除的个数,因为在列表中可以存在多个相同的元素
  • num=0,删除列表中所有指定的值
  • num=2,从前到后,删除2个
  • num=-2,从后向前,删除2个

lpop(name) 删除列表中的第一个元素,并返回这个元素


rpop(name) 删除最后一个元素,并返回这个元素


ltrim(name, start, end)只要索引号范围内的元素,其他的删除

  • name 列表名
  • start 起始索引号
  • end 结束索引号
In [62]: r.lrange('l1',0,-1)                                     
Out[62]: [b'2', b'QF', b'2', b'3', b'4', b'5', b'6']

In [63]: r.ltrim('l1',2,4)                                       
Out[63]: True

In [64]: r.lrange('l1',0,-1)                                     
Out[64]: [b'2', b'3', b'4']



rpoplpush(src_list, dst_list) 从一个列表取出最后的元素,同时将其添加至另一个列表的最前面

  • src_list,要取数据的列表名
  • dst_list,要添加数据的列表名

redis 自定义一个列表生成器 (扩展自修)

由于redis类库中没有提供对列表元素的增量迭代,假如想要循环name对应的列表的所有元素,那么就需要: 1、获取name对应列表的所以元素 2、循环列表 但是,假如列表非常大,那么就有可能在第一步时就将分配给 redis 的内存撑满,从而导致程序卡死,所以有必要自定义一个列表生成器函数,这个函数返回会一个生成器对象,用于对列表进行迭代的功能:


def list_iter(name):
    """
    自定义redis列表增量迭代
    :param name: redis中的name
    :return: yield 返回 列表元素
    """
    list_count = r.llen(name)
    for index in range(list_count):
        yield r.lindex(name, index)

# 使用
for item in list_iter('list_name'):
    print(item)


---

  • Hash 操作


redis的操作是不是原子操作 redis哪些操作是原子的_setnx是原子操作吗_04


hset(name, key, value)
#*************************
In [20]: rs.hset("hs1","k1", "v1")
Out[20]: 0

#*************************

"""
 name 对应的 hash 中设置一个键值对(不存在,则创建;否则,修改)

 参数:
     name,redis 中 hash的name
     key,name对应的hash中的key
     value,name对应的hash中的value

 注意:
    # hsetnx(name, key, value),当name对应的hash中不存在当前key时则创建(相当于添加)
"""

hmset(name, mapping)
#*************************
In [6]: rs.hmset('hs1', {'k1':'v1', 'k2': 'v2'})
Out[6]: True
#*************************

"""
在name对应的hash中批量设置键值对,没有的 key 就创建,已存在的 key 修改

 参数:
     mapping,字典,栗子:{'k1':'v1', 'k2': 'v2'}
"""

hget(name,key)
# 在name对应的hash中获取根据key获取value

#*************************
In [8]: rs.hget('hs1','k1')
Out[8]: b'v1'
#*************************


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'])
     或
     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,redis中的name
     key, hash对应的key
     amount,自增数(浮点数)
"""

 # 更多 http://redisdoc.com/key/scan.html#scan


---

  • Set 操作

和 Python 中的集合类似,其内的元素不允许重复,元素没有顺序之分

sadd(name,values) 向集合中添加元素


In [65]: r.sadd('s1', 1,2,3)                                     
Out[65]: 1

In [66]: r.sadd('s2', 2,3,4,5)                                   
Out[66]: 4



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


In [65]: r.sadd('s1', 1,2,3)                                     
Out[65]: 1

In [66]: r.sadd('s2', 2,3,4,5)                                   
Out[66]: 4



smembers(name) 获取集合中所有的元素


In [65]: r.sadd('s1', 1,2,3)                                     
Out[65]: 1

In [66]: r.sadd('s2', 2,3,4,5)                                   
Out[66]: 4



集合运算

sunion(set1, set2, set3) 并集

获取到所有集合的元素,并去重


In [71]: r.sunion('s1', 's2')                                    
Out[71]: {b'1', b'2', b'3', b'4', b'5'}

In [72]: r.sadd('s3', 4,5,6)                                     
Out[72]: 3

In [73]: r.sunion('s1','s2','s3')                                
Out[73]: {b'1', b'2', b'3', b'4', b'5', b'6'}



sunionstore(dst_set, set1,set2) 获取到并集的元素,并将其存放到另外一个集合中。


In [73]: r.sunion('s1','s2','s3')                                
Out[73]: {b'1', b'2', b'3', b'4', b'5', b'6'}

In [74]: r.sunionstore('s4', 's1','s2','s3')                     
Out[74]: 6

In [75]: r.smembers('s4')                                        
Out[75]: {b'1', b'2', b'3', b'4', b'5', b'6'}



sinter(set1,set2) 计算交集

获取所有集合中共同的元素


In [83]: r.sinter('s1','s2')                                     
Out[83]: {b'2', b'3'}



自修 sinterstore(dest, keys, *args) 获取并集后的元素,将其加入到另一个集合中


sdiff(keys, *args) 集合运算 差集

只存在于 第一个集合中的元素


In [79]: r.smembers('s1')                                        
Out[79]: {b'1', b'2', b'3'}

In [80]: r.smembers('s2')                                        
Out[80]: {b'2', b'3', b'4', b'5'}

In [81]: r.sdiff('s1','s2')                                      
Out[81]: {b'1'}

In [82]: r.sdiff('s2','s1')                                      
Out[82]: {b'4', b'5'}



自修

sdiffstore(dest, keys, *args) 获取差集后的元素,将其添加到另一个集合中

  • dest 另一个集合的名字
  • keys 进行差集运算的集合名字

sismember(name, value) 成员判断

检查value 是不是集合 name 的成员


In [84]: r.sismember('s1', 1)                                    
Out[84]: True

In [85]: r.sismember('s1', '1')                                  
Out[85]: True



以下扩展自修


smove(src, dst, value)
# 将某个成员从一个集合中移动到另外一个集合

spop(name)
# 从集合的右侧(尾部)移除一个成员,并将其返回

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

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

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

sunionstore(dest,keys, *args)
# 获取多一个name对应的集合的并集,并将结果保存到dest对应的集合中


  • Sort Set 有序集合的操作

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

```python zadd(name, args, *kwargs) """ 在 name 对应的有序集合中添加元素 例如: zadd('something', 'n1', 1, 'n2', 2) 或 zadd('something', n1=11, n2=22) """

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

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

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

r.zrange( name, start, end, desc=False, withscores=False, score_cast_func=float) """ 按照索引范围获取有序集合的元素,并进行排序

参数: name,redis的name start,有序集合索引起始位置(非分数) end,有序集合索引结束位置(非分数) desc,为 True 时,按照分数从大到小进行排序,默认按照分数从小到大排序 withscores,是否获取元素的分数,默认只获取元素的值 score_cast_func,对分数进行数据转换的函数

更多: 从大到小排序 zrevrange(name, start, end, withscores=False, score_cast_func=float)


按照分数范围获取name对应的有序集合的元素
   zrangebyscore(name, min, max, start=None, num=None, withscores=False, score_cast_func=float)
   从大到小排序
   zrevrangebyscore(name, max, min, start=None, num=None, withscores=False, score_cast_func=float)


"""

zrank(name, value) """ 获取某个值在 name对应的有序集合中的排行(从 0 开始)

更多: zrevrank(name, value),从大到小排序 """

zrangebylex(name, min, max, start=None, num=None) """ 当有序集合的所有成员都具有相同的分值时,有序集合的元素会根据成员的 值 (lexicographical ordering)来进行排序,而这个命令则可以返回给定的有序集合键 key 中, 元素的值介于 min 和 max 之间的成员 对集合中的每个成员进行逐个字节的对比(byte-by-byte compare), 并按照从低到高的顺序, 返回排序后的集合成员。 如果两个字符串有一部分内容是相同的话, 那么命令会认为较长的字符串比较短的字符串要大

参数: name,redis的name min,左区间(值)。 + 表示正无限; - 表示负无限; ( 表示开区间; [ 则表示闭区间 min,右区间(值) start,对结果进行分片处理,索引位置 num,对结果进行分片处理,索引后面的num个元素

例子: ZADD myzset 0 aa 0 ba 0 ca 0 da 0 ea 0 fa 0 ga r.zrangebylex('myzset', "-", "[ca") 结果为:['aa', 'ba', 'ca']

扩展: 从大到小排序 zrevrangebylex(name, max, min, start=None, num=None) """

zrem(name, values) # 删除name对应的有序集合中值是values的成员

# 如:zrem('zz', ['s1', 's2'])

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

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

zremrangebylex(name, min, max) # 根据值返回删除

zscore(name, value) # 获取name对应有序集合中 value 对应的分数

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='172.16.153.136',
                            port=6379,
                            max_connections=10)
conn = redis.Redis(connection_pool=pool)
conn.publish('fm101', '我要上头条')


订阅者


import redis
pool = redis.ConnectionPool(host='172.16.153.10',
                            port=6379,
                            max_connections=10)
conn = redis.Redis(connection_pool=pool)

# 创建订阅对象
pub = conn.pubsub()

# 订阅频道
pub.subscribe('fm101')

# 循环接收订阅频道的发出的信息
while True:
    # 开始接收信息
    data = pub.parse_response()

    # 得到的信息是一个列表
    msg, fm, content = data
    try:
        # 判断是否是字符串
        content.isdigit()
    except Exception as e:
        pass  # 假如抛出异常,说明不是字符串或者二进制的字符,就略过
    else:
        # 没有发生异常时,说明是字符串或者是二进制的字符
        # 就可以转换成普通的文本
        print(str(content, encoding='utf-8'))