Python Redis
Python Redis
安装
pip install redis
连接
redis 提供两个类 Redis 和 StrictRedis, 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:若不存在则插入,否则不插入
设置
- set()
- setnx() = set(nx=True)
- setxx() = set(xx=True)
- setex(name,number,value) = set(name,value,ex=number)
- setpx(name,number,value) = set(name,value,px=number)
- mset():批量设置
- hset(),hsetnx()
- setrange(name,offset,value):从指定字符串的索引开始向后添加
- append():在列表中追加值
- lpush(),lpushx(),rpush(),rpushx(),linsert()
获取
- get()
- mget():批量获取
- getset():将key的值设置成新的的值,但是获取到的是旧的值
- getrange(name,start,end):获取从start到end的值(0,-1)是拿所有
- 哈希
- hget(),hkeys()(取所有键),hvals()(取所有值),hmget(),hgetall()(取出指定键中的所有值)
- hscan()(获取指定键的所有数据)
- 列表
- llen(),lrange(),lindex()
- lrem(),lpush(),ltrim(),rpop()
- rpoplpush()(从一个列表移动到另一个列表)
- 集合
- sadd(),smove(),spop()(随机删),sremove()
- scard(),smembers(),sscan_iter(),sscan(),sismember()
- sdiff(),sdiffstrore(),sinter(),sinterstore(),sunion(),sunionstore()
- 有序集合
- zadd(),zrem(),zremrangebyrank(),zremrangebyscore()
- zcard(),zrange(),zrevrange()(逆序),zrangebyscore(),zrevreangebyscore(),zcount(),zscore()
- zscan(),zscan_iter(),zrank()
- zincrby()
自增
- incr()
- incrby()
- incrbyfloat()
- 应用场景
- 记录点击次数:点赞,网页点击,访问数
自减
- decr()
- decrby()
- decrbyfloat()
删除
- hdel()
其他
- strlen()
- 设置的值为xxx:yyyy:zzzz格式的说明是形成目录结果的值,冒号前面是目录,最后的为key
- delete()
- exists()
- rename()
- dbsize()
- keys()
- expire()
- randomkey()
- type()
- flushdb()
事务
- pipe = xxx.pipline():获取流水线对象,默认启用事务
- pipe.multi():通过管道开启事务
- 使用的时候用with xxx.pipeline(transaction=true) as pipe:
- pipe.reset():回滚
- pipe.get().hset().set()...可以写到一起
- pipe.execute():批量执行命令
- 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 成为主
配置文件实现主从(主流)
- 创建三个目录,分别存放数据文件、日志文件、配置文件
- 赋值redis.conf到配置文件目录下,把它当作公共的配置文件(重命名为redis-common.conf)
- 修改公共配置文件
- 注释绑定的IP
- 关闭保护模式
- 注释port
- 修改为后台启动:daemonize yes
- 注释进程编号记录文件
- 注释公共配置文件
- 注释公共数据文件、修改数据文件路径
- 添加从服务器访问主服务器认证:masterauth password
- 添加访问认证:require passwrod
- 关闭追加: no以及注释appendfilename
- 从服务器只读开启:slave-read-only yes
- 添加服务器的私有配置文件(一定要是奇数,)
- 为什么用奇数台服务器?
- 服务器大于等于一半挂掉后不可用
- 为了节省服务器
- 多一个需要做哨兵
- 为什么用奇数台服务器?
- 私有配置文件
- redis-端口号.conf
- 引用公共配置文件:include xxxx/redis-common.conf
- 进程编号记录文件:pidfile xxx/redis-端口号.pid
- port 端口号
- 日志记录文件:logfile "xxx/redis-端口号.log"
- 数据记录文件:dbfilename dump-端口号.rdb
- 追加文件名称(aof文件:存储记录指令用的):appendfilename "appendonly-端口号.aof"
- 从服务器配置文件要多加一句:slave of 主服务器IP 端口号
- 运行redis进程
- redis-server redis-xxx.conf
- 查看主从状态
- info replication
- role
- slave
- masterhost
- info replication
主备切换环境配置(哨兵)
当主节点被监听到挂掉了,则会将某个从节点变成主节点
Sentinel会不断检查Mastre和Slave是否正常,可以监控任意多个,自动切换
-
安装 sudo apt install redis-sentinel
- sudo /etc/init.d/redis-sentinel stop验证哨兵有没有启动
-
sentinel.conf需要自己创建
-
配置公共配置文件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 -
添加哨兵的私有文件
-
复制n个slave的配置文件,命名:sentinel-2端口号.conf
-
这些私有文件都是监听主节点
include xxxx/sentinel-common.conf port 2端口号 pidfile xxxx/sentinel-2端口号.pid logfile "xxx/sentinel-2端口号.log"
-
-
启用服务
redis-sentinel xxx/sentinel-xxxx.conf
或者
redis-server sentinel-xxx.conf --sentinel
-
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实现缓存
- 性能
- 碰到需要执行耗时特别久,且结果不频繁变动的SQL,就特别适合将结果放入缓存。后面的请求就从缓存读取,使得请求能够迅速响应
- 一瞬间:0.36s,一刹那:0.018s,一弹指:7.2s
- 并发
- 在大并发下请求直接访问数据库,数据库会出现异常连接
- 需要使用redis做一个缓存操作,让请求先访问到redis
- 实现方案
- 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'
-
测试缓存是否生效
- python manange.py shell
- from django.core.cache import cache
- cache.set()
- cache.haskey()或cache.keys()
- cache.get()
-
实现缓存(主要使函数缓存)
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)-
缓存穿透
- 黑客故意请求缓存中不存在的数据,导致所有的请求都怼到数据库上
- 解决方案
- 利用互斥锁,缓存失效的时候先获得锁,再去请求数据库。没得到锁,则休眠一段时间重试
- 采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存如果过期,异步一个线程去读数据,更新缓存。需要做缓存预热操作
- 提供一个能迅速判断请求是否有效拦截的机制
-
缓存雪崩
- 即缓存同一时间大面积失效,此时又来了一波请求,结果请求都对到数据库上,从而导致数据库一场连接
- 解决方案:
- 给缓存失效时间加上一个随机值,避免集体失效
- 使用互斥锁,但是该方案吞吐量明显下降
- 双缓存。缓存A失效时间为20分钟,缓存B不设失效时间。自己做缓存预热操作
- 从缓存A读数据操作,有则直接返回
- A没有数据,直接从B读数据,直接返回,并且异步启动一个更新线程
- 更新线程同时更新缓存A和缓存B
-

浙公网安备 33010602011771号