Python Redis

Python Redis

安装

pip install redis

连接

redis 提供两个类 RedisStrictRedis, StrictRedis 用于实现大部分官方的命令,Redis 是 StrictRedis 的子类,用于向后兼用旧版本。

redis 取出的结果默认是字节,我们可以设定 decode_responses=True 改成字符串。

实例化连接

r = redis.Redis(host='localhost',port=6379,decode_responses=True)

连接池(推荐)

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

pool = redis.ConnectionPool(host='localhost', port=6379, decode_responses=True)
r1 = redis.Redis(connectionPool=pool)

r2 = redis.Redis(connectionPool=pool)

一个服务可以被多个客户端对象连接

  • ex:超时(秒)
  • px:超时(毫秒)
  • xx:若存在则插入,否则不插入
  • nx:若不存在则插入,否则不插入

设置

  1. set()
  2. setnx() = set(nx=True)
  3. setxx() = set(xx=True)
  4. setex(name,number,value) = set(name,value,ex=number)
  5. setpx(name,number,value) = set(name,value,px=number)
  6. mset():批量设置
  7. hset(),hsetnx()
  8. setrange(name,offset,value):从指定字符串的索引开始向后添加
  9. append():在列表中追加值
  10. lpush(),lpushx(),rpush(),rpushx(),linsert()

获取

  1. get()
  2. mget():批量获取
  3. getset():将key的值设置成新的的值,但是获取到的是旧的值
  4. getrange(name,start,end):获取从start到end的值(0,-1)是拿所有
  5. 哈希
    1. hget(),hkeys()(取所有键),hvals()(取所有值),hmget(),hgetall()(取出指定键中的所有值)
    2. hscan()(获取指定键的所有数据)
  6. 列表
    1. llen(),lrange(),lindex()
    2. lrem(),lpush(),ltrim(),rpop()
    3. rpoplpush()(从一个列表移动到另一个列表)
  7. 集合
    1. sadd(),smove(),spop()(随机删),sremove()
    2. scard(),smembers(),sscan_iter(),sscan(),sismember()
    3. sdiff(),sdiffstrore(),sinter(),sinterstore(),sunion(),sunionstore()
  8. 有序集合
    1. zadd(),zrem(),zremrangebyrank(),zremrangebyscore()
    2. zcard(),zrange(),zrevrange()(逆序),zrangebyscore(),zrevreangebyscore(),zcount(),zscore()
    3. zscan(),zscan_iter(),zrank()
    4. zincrby()

自增

  1. incr()
  2. incrby()
  3. incrbyfloat()
  4. 应用场景
    1. 记录点击次数:点赞,网页点击,访问数

自减

  1. decr()
  2. decrby()
  3. decrbyfloat()

删除

  1. hdel()

其他

  1. strlen()
  2. 设置的值为xxx:yyyy:zzzz格式的说明是形成目录结果的值,冒号前面是目录,最后的为key
  3. delete()
  4. exists()
  5. rename()
  6. dbsize()
  7. keys()
  8. expire()
  9. randomkey()
  10. type()
  11. flushdb()

事务

  1. pipe = xxx.pipline():获取流水线对象,默认启用事务
  2. pipe.multi():通过管道开启事务
    1. 使用的时候用with xxx.pipeline(transaction=true) as pipe:
  3. pipe.reset():回滚
  4. pipe.get().hset().set()...可以写到一起
  5. pipe.execute():批量执行命令
  6. Redis主要考虑的是缓存的事务

主从复用-读写分离(高可用)

(要关防火墙:systemctl stop iptables sysyemctl stop firewalld.service)

高可用方案:主从搭建+哨兵

  • master可读可写,slaves只能读(降低master读的压力,提高可用性)

目标:消除基础结构中的单点故障

Linux命令行实现主从

# 服务端
redis-server --port port --slaveof master_ip master_port --masterauth master_password

redis命令实现主从

>slave IP PORT	成为谁的从
>slave no one   成为主

配置文件实现主从(主流)

  1. 创建三个目录,分别存放数据文件、日志文件、配置文件
  2. 赋值redis.conf到配置文件目录下,把它当作公共的配置文件(重命名为redis-common.conf)
  3. 修改公共配置文件
    1. 注释绑定的IP
    2. 关闭保护模式
    3. 注释port
    4. 修改为后台启动:daemonize yes
    5. 注释进程编号记录文件
    6. 注释公共配置文件
    7. 注释公共数据文件、修改数据文件路径
    8. 添加从服务器访问主服务器认证:masterauth password
    9. 添加访问认证:require passwrod
    10. 关闭追加: no以及注释appendfilename
    11. 从服务器只读开启:slave-read-only yes
    12. 添加服务器的私有配置文件(一定要是奇数,)
      1. 为什么用奇数台服务器?
        1. 服务器大于等于一半挂掉后不可用
        2. 为了节省服务器
        3. 多一个需要做哨兵
  4. 私有配置文件
    1. redis-端口号.conf
    2. 引用公共配置文件:include xxxx/redis-common.conf
    3. 进程编号记录文件:pidfile xxx/redis-端口号.pid
    4. port 端口号
    5. 日志记录文件:logfile "xxx/redis-端口号.log"
    6. 数据记录文件:dbfilename dump-端口号.rdb
    7. 追加文件名称(aof文件:存储记录指令用的):appendfilename "appendonly-端口号.aof"
    8. 从服务器配置文件要多加一句:slave of 主服务器IP 端口号
  5. 运行redis进程
    1. redis-server redis-xxx.conf
  6. 查看主从状态
    1. info replication
      1. role
      2. slave
      3. masterhost

主备切换环境配置(哨兵)

当主节点被监听到挂掉了,则会将某个从节点变成主节点

Sentinel会不断检查Mastre和Slave是否正常,可以监控任意多个,自动切换

  1. 安装 sudo apt install redis-sentinel

    1. sudo /etc/init.d/redis-sentinel stop验证哨兵有没有启动
  2. sentinel.conf需要自己创建

  3. 配置公共配置文件sentinel-common.conf

    port 16379
    sentinel monitor mymaster IP port 2		// 2为大于服务器数一半以上,为了防止网络延迟导致哨兵频繁的切换主节点   
    sentinel down-after-milliseconds mymaster 5000  //心跳的超时时间
    sentinel parallel-syncs mymaster 1  
    sentinel failover-timeout mymaster 15000  // 迁移超时时间
    sentinel auth-pass mymaster password
    protected-mode no
    daemonize yes
    
  4. 添加哨兵的私有文件

    1. 复制n个slave的配置文件,命名:sentinel-2端口号.conf

    2. 这些私有文件都是监听主节点

      include xxxx/sentinel-common.conf
      port 2端口号
      pidfile xxxx/sentinel-2端口号.pid
      logfile "xxx/sentinel-2端口号.log"
      
  5. 启用服务

redis-sentinel xxx/sentinel-xxxx.conf
或者
redis-server sentinel-xxx.conf --sentinel
  1. python操作哨兵

    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('myslave',socket_timeout=0.1,db=0')
    

Django配置和使用Redis实现缓存

  1. 性能
    1. 碰到需要执行耗时特别久,且结果不频繁变动的SQL,就特别适合将结果放入缓存。后面的请求就从缓存读取,使得请求能够迅速响应
    2. 一瞬间:0.36s,一刹那:0.018s,一弹指:7.2s
  2. 并发
    1. 在大并发下请求直接访问数据库,数据库会出现异常连接
    2. 需要使用redis做一个缓存操作,让请求先访问到redis
    3. 实现方案
  3. django配置redis
CACHES = {
    "default":{
        "BACKEND":"django_redis.cache.RedisCache",
        "LOCATION":"redis://IP:port/0",   #ip/端口/库
        "OPTIONS":{
            "CLIENT_CLASS":"django_redis.client.DefaultClient",
            "CONNECTION_POOL_KWARGS":{"max_connections":100},
            "PASSWORD":"password",
            "PICKLE_VERSION":-1,    #指定pickle的序列化版本
            "SOCKET_CONNECT_TIMEOUT":2,	  #连接超时时间
            "SOCKET_TIMEOUT":2    #执行命令超时时间
        }
    }
}
SESSINO_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'default'
  1. 测试缓存是否生效

    1. python manange.py shell
    2. from django.core.cache import cache
    3. cache.set()
    4. cache.haskey()或cache.keys()
    5. cache.get()
  2. 实现缓存(主要使函数缓存)

    from django.core.cache import cache
    
    '''
    	由于该功能可以带条件查询,所以key必须多样化
    	无条件查询
    		customer:name_:khno_:state_
    	有条件查询
    		customer:name_zhangsan:khno_2018:state_1
    '''
    
    name = request.GET.get('name','')
    khno = request.GET.get('khno','')
    state = request.GET.get('state','')
    
    # 判断Redis中是否有数据,有数据就从缓存中拿,没有就在数据库中拿
    redis_key = 'customer:name_'+name+':khno_2018:'+khno+':state_'+state
    redis_value = cache.get(redis_key)
    object_list=None
    if redis_value and len(redis_value)>0:
        object_list = redis_value
    else:
        object_list = XXX.objects.all()
        # 如果有条件则过滤查询
        if name:
            object_list = object_list.filter(name=name)
        if name:
            object_list = object_list.filter(khno=khno)
        if name:
            object_list = object_list.filter(state=state)
        cache.set(redis_key,object_list)
        
        
    '''
    更新,删除,创建之前都要清除缓存,让缓存实时更新
    '''    
    redis_key = cache.keys('customer*')
    for k in redis_key:
        cache.delete(k)
    
    1. 缓存穿透

      1. 黑客故意请求缓存中不存在的数据,导致所有的请求都怼到数据库上
      2. 解决方案
        1. 利用互斥锁,缓存失效的时候先获得锁,再去请求数据库。没得到锁,则休眠一段时间重试
        2. 采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存如果过期,异步一个线程去读数据,更新缓存。需要做缓存预热操作
        3. 提供一个能迅速判断请求是否有效拦截的机制
    2. 缓存雪崩

      1. 即缓存同一时间大面积失效,此时又来了一波请求,结果请求都对到数据库上,从而导致数据库一场连接
      2. 解决方案:
        1. 给缓存失效时间加上一个随机值,避免集体失效
        2. 使用互斥锁,但是该方案吞吐量明显下降
        3. 双缓存。缓存A失效时间为20分钟,缓存B不设失效时间。自己做缓存预热操作
          1. 从缓存A读数据操作,有则直接返回
          2. A没有数据,直接从B读数据,直接返回,并且异步启动一个更新线程
          3. 更新线程同时更新缓存A和缓存B
posted @ 2021-12-07 17:53  注入灵魂  阅读(104)  评论(0)    收藏  举报