当前位置:   article > 正文

redis(python操作、redis原理及redis缓存穿透,缓存雪崩,缓存击穿)_python redis

python redis

一、redis数据库连接

import redis


# decode_responses=True将二进制转换成字符串存储;decode_responses=False就是二进制储存

redis_pool = redis.ConnectionPool(
    host='127.0.0.1',
    port=6379,
    db=0,
    decode_responses=True)
r0 = redis.Redis(connection_pool=redis_pool)

print(r0)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

二、redis数据类型

1、字符串
直接存的键值对,根据key来获取value,在存键值对的时候加上过期时间,就不会将数据永久保存,加强的利用率

a = r0.set('string1', 's1', ex=60)
print(a)
b = r0.get('string1')
print(b)

# nx:nx为True,则只有key不存在时,操作才会执行
# xx:xx为True,则只有key存在时,操作才会执行
a = r0.set('string1', 's1', xx=True)
print(a)
a = r0.set('string1', 's11', nx=True)
print(a)
# setrange(key, start, end) 修改字符串内容,从指定字符串索引开始向后替换
# getrange(key, start, end) 获取子序列(根据字节获取,非字符)
r0.setrange('key6', 2, '222')
print(r0.get('key6'))
print(r0.getrange('key6', 1, 3))
# strlen(key) 返回key对应值的字节长度
# 删除键
r0.delete('key6')
print(r0.get('key6'))

# 清空数据库
# r0.flushdb()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

2、hash
以表,键,值的方式储存,存或者取的时候需要知道表和键,才能获取到存的值。

r0.hset('table1', 'k1', 'v1')
r0.hset('table1', 'k2', 'v2')
r0.hset('table1', 'k3', 'v3')
a = r0.hget('table1', 'k1')
print(a)
a = r0.hmget('table1', 'k1', 'k2', 'k3')
print(a)

# hgetall(name) 取出所有的键值对
r0.hdel('table1', 'k1', 'v1')
print(r0.hgetall('table1'))

# r0.flushdb()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

3、list
以列表的方式储存。

# 从左边插入
r0.lpush('list2', 1111111, 222222, 33333333)
从右边插入
r0.rpush('list1', 11, 22, 33)
a = r0.lrange('list1', 0, -1)
print(a)

# linsert(name, where, refvalue, value)) 新增(固定索引号位置插入元素)
# 参数:where - BEFORE或AFTER; refvalue - 标杆值,即:在它前后插入数据; value - 要插入的数据
r0.linsert('list1', 'before', '1', '666')
r0.linsert('list1', 'after', '2', '777')
a = r0.lrange('list1', 0, -1)
print(a)

# r.lset(name, index, value) 修改(指定索引号进行修改)
r0.lset('list1', 1, '0000')
a = r0.lrange('list1', 0, -1)
print(a)

# r.lrem(name, count, value) 删除(指定值进行删除)
# 参数:value - 要删除的值; count=0,删除列表中所有的指定值; count=2 - 从前到后,删除2个, count=1,从前到后,删除左边第1个; count=-2 - 从后向前,删除2个
r0.lrem("list1", -1, '1')  # 将列表中所有的"22"删除
a = r0.lrange('list1', 0, -1)
print(a)

# lpop(name) 删除并返回; rpop(name) 表示从右向左操作
b = r0.lpop('list1')
print(b)
a = r0.lrange('list1', 0, -1)
print(a)
a = r0.lrange('list2', 0, -1)
print(a)

# lindex(name, index) 取值(根据索引号取值)
r = r0.lindex('list1', 2)
print(r)

# rpoplpush(src, dst) 移动 元素从一个列表移动到另外一个列表 移动list2的右边的一个元素,从左边插入到list1
r0.rpoplpush('list2', 'list1')
a = r0.lrange('list1', 0, -1)
print(a)
a = r0.lrange('list2', 0, -1)
print(a)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

4、无序set
以集合的方式储存,且是无序的。

r0.sadd('set1', 1, 2, 3)
r0.sadd('set2', 1, 22, 33)

a = r0.smembers('set1')
print(a)
b = r0.smembers('set2')
print(b)
c = a - b
print(c)

# sdiff(keys, *args) 差集
print(r0.sdiff('set1', 'set2'))  # 在集合set1但是不在集合set2中
print(r0.sdiff("set2", "set1"))  # 在集合set2但是不在集合set1中
# sdiffstore(dest, keys, *args) 差集--差集存在一个新的集合中
r0.sdiffstore("set3", "set1", "set2")  # 在集合set1但是不在集合set2中
b = r0.smembers('set3')
print(b)

# sinter(keys, *args) 交集
print(r0.sinter('set1', 'set2'))  # 取2个集合的交集
# sinterstore(dest, keys, *args) 交集--交集存在一个新的集合中
r0.sinterstore('set4', 'set1', 'set2')
print(r0.smembers('set4'))

# sunion(keys, *args) 并集
print(r0.sunion("set1", "set2"))  # 取2个集合的并集
# sunionstore(dest,keys, *args) 并集--并集存在一个新的集合
r0.sunionstore("set5", "set1", "set2")  # 取2个集合的并集
print(r0.smembers("set5"))

# sismember(name, value) 判断是否是集合的成员 类似in
print(r0.sismember('set1', 1))

# smove(src, dst, value) 将某个成员从一个集合中移动到另外一个集合
r0.smove("set1", "set2", 2)
print(r0.smembers("set1"))
print(r0.smembers("set2"))

# srem(name, values) 指定值删除
print(r0.srem("set2", 33))  # 从集合中删除指定值 1
print(r0.smembers("set2"))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

5、有序set(暂无,需要可百度其他地方)

三、redis集群

#1、至少需要六台服务器,才能搭建集群
#2、一个 Redis 集群包含 16384 个插槽(hash slot), 数据库中的每个键都属于这 16384 个插槽的其中一个
#3、连接redis集群的时候,会将集群的所有端口都链接上,去获取数据
#4、当主机挂掉后,从机立马会接替主机的工作,继续提供服务(从机就会变成主机,后续之前的主机修复好会变成从机)

集群连接

from rediscluster import RedisCluster


def cmd_cluster():
    try:
        startup_nodes = [
            {'host': "127.0.0.1", 'port': 6379},
            {'host': "127.0.0.1", 'port': 6380},
            {'host': "127.0.0.1", 'port': 6381},
            {'host': "127.0.0.1", 'port': 6382},
            {'host': "127.0.0.1", 'port': 6383},
            {'host': "127.0.0.1", 'port': 6384},
        ]

        # 连接redis集群
        redis_conn = RedisCluster(startup_nodes=startup_nodes, password='111111').connection

        if not redis_conn:
            print("连接redis集群失败")
            exit()
        else:
            print("连接redis集群成功")
    except Exception as e:
        print(e)
        print("错误,连接redis 集群失败")


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

四、redis缓存穿透,缓存雪崩,缓存击穿

1、 redis穿透:
概念:大量请求一个不存在的key
缓存穿透产生的原因:请求根本不存在的资源(DB不存在,redis也不存在)
举例:客户端发送大量不可响应的请求
在这里插入图片描述

当大量的客户端发送出该图的请求是,就可能导致缓存穿透的情况出现。
因为数据库中没有该数据,redis中也没有该数据,那么这些请求在redis得不到响应,就会去DB,导致DB压力过大而卡死或死机。

#解决方式:
#1、对空值进行缓存:
假如有多个请求的时候,redis就会对该数据进行缓存(key=0, value=null),当请求到达redis的时候就会返回一个null给客户端,避免了大量无法访问的数据直接请求DB。
这样的话,假如出现很多个不同存在的请求,譬如id=-1,id=-2,…,将空值进行缓存会占用很多redis空间,所以要给key设置过期时间,而且时间不能太长
此解决方法炜被动防御方式,假如黑客暴力请求到很多不存在的数据的时候,会写很多空值到redis数据库中,可能会导致内存不足的情况

#2、实时监控:
对redis进行实时监控,发现redis命中率下降的时候,对访问对象和访问数据进行原因的排查和分析,将其拉入黑名单,禁止服务(有可能是黑客攻击)。

#3、使用布隆过滤器
将所有可以访问的资源放入到布隆过滤器,当一个请求来的时候,先进性布隆过滤器的判断,如果有再放行,否则就直接拦截。
布隆过滤器的特点是:判断不存在是一定不存在,判断存在不一定存在,所以其判断有一定的误差;所以需要做一些其他操作来解决其特点,譬如进行接口限流,权限校验等

#4、进行接口校验
类似与用户拦截之类的,对于无效的访问直接进行拦截,不允许这些访问到数据库(redis,DB)上

2、redis缓存雪崩:
概念:redis中大量的key过期
缓存雪崩产生的原因:大量并发来请求这些过期的key(DB有数据,redis没有数据)

#解决方式:
#1、将失效的时间分隔开:
使用自动随机数生成key过期时间,防止集体过期

#2、设置缓存标记
记录缓存数据是否过期,假如过期会通知后台去更新实际的key

#3、使用多级架构去缓存数据
使用nginx缓存+redis缓存+其他缓存,不同层使用不同的缓存,可靠性更强

#4、使用锁或者队列的形式
如果查不到就加上排他锁,其他的请求只能等待

3、redis缓存击穿:
概念:redis中的热点key过期
产生原因:大量的请求这一个过期的key(DB有数据,redis没有数据)

#举例:
对于热搜,这时候大量用户都在访问该热点事件,但是可能优于某种原因,redis的这个热点key过期了,那么这时候大量高并发对于该key的请求就得不到redis的响应,那么就会将请求直接打在DB服务器上,导致整个DB瘫痪死机。

#解决方式:
#1、提前对热点的key进行设置
对于这些热点数据,预先设置到redis中

#2、监控数据,进行调整
监控哪些数据是热门数据,然后对key的过期时间进行调整

#3、使用锁机制
当数据不存在时,只有一个请求可以获取到互斥锁,然后将DB中的数据写入到redis数据库中,之后的所有请求就会从redis数据库响应。

五、redis原理

1、内存储存
Redis将数据存储在内存中,这样可以快速读取和写入数据。同时,Redis还支持将内存中的数据持久化到磁盘中,实现数据的持久化存储。

RDB和AOF储存
RDB快照形式:是直接把内存中的数据保存到一个dump文件中,定时保存。有一段时间的数据没有
AOF储存:在配置中开启appendonly yes,redis每执行一条修改数据的命令,都会被记下来,添加到AOF文件中;所以其文件体积比RDB储存大很多。

#redis默认的是RDB快照储存

2、单线程模型
Redis采用单线程模型来处理客户端请求。在单线程模型下,Redis通过事件轮询机制来处理客户端的请求。这种方式可以避免多线程之间的竞争和锁的开销,提高了数据处理的效率。

3、命令解析和执行:
当客户端发送请求给Redis服务器时,Redis会根据请求的类型和参数来解析请求,并执行相应的命令。命令执行过程中,会涉及到对数据的读取、写入和计算等操作。

4、网络通信:
Redis使用TCP/IP协议进行客户端和服务器之间的通信。客户端通过发送请求到Redis服务器来获取数据或修改数据。
TCP协议:负责在网络中建立稳定连接,来确保数据传送的可靠性
IP协议:负责数据传输,将数据从一个节点传输到另一个节点

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/670148
推荐阅读
相关标签
  

闽ICP备14008679号