Redis相关
redis:
# 是一个单进程单线程应用
# mysql是一个软件,帮助开发者对一台机器的硬盘进行操作
# redis是一个软件,帮助开发者对一台机器的内存进行操作
关键字: 缓存
# 优先去redis中获取,如果没有就是数据库
可以做持久化操作(两种策略)
- AOF # 定时保存
- RDB # 同步保存
下载: https://redis.io/download
- redis 软件
- yum install redis (Linux)
run服务端:
- redis-server /etc/redis.conf
- $ wget http://download.redis.io/releases/redis-5.0.4.tar.gz (官网下载后进行以下操作)
$ tar xzf redis-5.0.4.tar.gz
$ cd redis-5.0.4
$ make
-mac: 根据终端提示
make test # 编译测试
run服务端:
/src/redis-server redis.conf
redis.conf:
bind 0.0.0.0
port 6379
requirepass 密码
配置:
拷贝 redis-4.0.9/redis.conf 到 /etc 目录
修改 redis.conf 配置文件
requirepass yourpassword 添加密码,在第500行;
daemonize yes,设置后台启动,在第136行;
Mac OS 下
- brew install redis
# 在装有homebrew 情况下使用
需要 使用brew重启服务端
更改相关配置文件 redis.conf
usr/local/etc/redis.conf
bind 0.0.0.0
# requirepass yourpassword 添加密码,在第500行;
daemonize yes,设置后台启动,在第136行;
启动服务端
$redis-cil
python常用命令:
命令 | 用途 |
set key value | 设置key的值 |
get key | 获取key的值 |
exists('name') | 查看此key是否存在 |
keys() | 查看所有的key |
keys('name') | 查询关于name的值 name中可以增加通配符进行模糊查找 |
scan(cursor=0,match=None,count=none) | 同字符串操作,用于迭代key |
scan_iter('namee',count=num) | 当需要模糊查询且数量庞大需要使用scan_iter后循环conn.getall(item)等类似操作name中可以增加通配符进行模糊查找 |
flushakk() | 消除所有的key |
delete('key') | 删除name为key的值 支持放多个name内部遍历 |
expire(name,time) | 为某个redis的某个name设置超时时间 |
rename(src,time) | 对redis的name重命名为 |
move(name,db) | 将redis的某个值移动到指定的db下 |
randomkey() | 随机获取一个redis的name |
type(name) | 获取name对应值得类型 |
- python连接redis模块
下载: pip3 install redis
import redis
创建单连接:
conn = redis.Redis(host = 'ip' , port = 端口 , auth = '密码') # 创建连接
conn.set('key','value',ex=num) # 发送数据通过缓存存储 ex超时时间
val = conn.get('key') # 获取value
创建连接池 # 推荐 pool 实现单例模式 # 创建连接池只用创建一次 不必反复创建 (本质:维护一个已经和服务端连接成功的socket,以后再次发送数据时,直接使用已创建的socke直接send数据)
pool = redis.ConnectionPool(host = 'ip' , port = 端口 , password = '密码' , max_connections = num) # 创建连接池 max_connections 设置最大连接数
conn = redis.Redis(connection_pool = pool) # 去连接池中获取连接
conn.set('key','value')
val = conn.get('key') # 获取value
-----------------
django插件实现连接池连接
// 与本身自己创建单例的连接池好处
提供了全栈缓存,单独视图缓存,局部视图缓存
redis 在Django使用
下载 pip3 install django-redis
# 不用自己写连接池等
在 Django settings中增加配置内容
# redis 配置 CACHES = { "name1": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "CONNECTION_POOL_KWARGS": {"max_connections": 100} # 最大连接数 # "PASSWORD": "密码", } } }
在视图中使用(手动写入)
from django_redis import get_redis_connection conn = get_redis_connection("name")
PS:
如果在run的过程中报错:'django.core.exceptions.ImproperlyConfigured: Requested setting CACHES, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings.'
解决办法
需要在pycharm中设置
1、Run --> EditConfigures
2、找到python一项 具体名字是 Python tests(注意不是django那一个),然后修改里面的Environment variables 添加一项。名称是DJANGO_SETTINGS_MODULE 值是 你的settings,比如 mysite.settings 。
全栈缓存
使用中间件,经过一系列的认证等操作,如果内容在缓存中存在,则使用FetchFromCacheMiddleware获取内容并返回给用户,当返回给用户之前,判断缓存中是否已经存在,如果不存在则UpdateCacheMiddleware会将缓存保存至缓存,从而实现全站缓存
MIDDLEWARE = [ 'django.middleware.cache.UpdateCacheMiddleware', # 其他中间件... 'django.middleware.cache.FetchFromCacheMiddleware', ] CACHE_MIDDLEWARE_ALIAS = "" CACHE_MIDDLEWARE_SECONDS = "" CACHE_MIDDLEWARE_KEY_PREFIX = ""
单独视图缓存
方式一:
from django.views.decorators.cache import cache_page @cache_page(60 * 15) def my_view(request): ...
方式二:
from django.views.decorators.cache import cache_page urlpatterns = [ url(r'^foo/([0-9]{1,2})/$', cache_page(60 * 15)(my_view)), ]
局部视图使用
a. 引入TemplateTag
{% load cache %}
b. 使用缓存
{% cache 5000 缓存key %}
缓存内容
{% endcache %}
---------
redis 五大数据类型 # 只有第一层value支持这五大数据类型; 不支持嵌套 如果要嵌套需要自己序列化
redis = {
k1 : '123', # 字符串
k2 : [1,2,3], # 列表
k3 : {1,2,3}, # 集合
k4 : {name:value,name2:value2}, # 字典 hash
k5 : {(name,num),(name2,num2)} # 有序集合 根据num大小排序
}
-------------str操作
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,结束位置(字节)
# 如: "武沛齐" ,0-3表示 "武"
setrange(name, offset, value)
# 修改字符串内容,从指定字符串索引开始向后替换(新值太长时,则向后添加)
# 参数:
# offset,字符串的索引,字节(一个汉字三个字节)
# value,要设置的值
setbit(name, offset, value)
# 对name对应值的二进制表示的位进行操作
# 参数:
# name,redis的name
# offset,位的索引(将值变换成二进制后再进行索引)
# value,值只能是 1 或 0
# 注:如果在Redis中有一个对应: n1 = "foo",
那么字符串foo的二进制表示为:01100110 01101111 01101111
所以,如果执行 setbit('n1', 7, 1),则就会将第7位设置为1,
那么最终二进制则变成 01100111 01101111 01101111,即:"goo"
# 扩展,转换二进制表示:
# source = "武沛齐"
source = "foo"
for i in source:
num = ord(i)
print bin(num).replace('b','')
特别的,如果source是汉字 "武沛齐"怎么办?
答:对于utf-8,每一个汉字占 3 个字节,那么 "武沛齐" 则有 9个字节
对于汉字,for循环时候会按照 字节 迭代,那么在迭代时,将每一个字节转换 十进制数,然后再将十进制数转换成二进制
11100110 10101101 10100110 11100110 10110010 10011011 11101001 10111101 10010000
武 沛 齐
getbit(name, offset)
# 获取name对应的值的二进制表示中的某位的值 (0或1)
bitcount(key, start=None, end=None)
# 获取name对应的值的二进制表示中 1 的个数
# 参数:
# key,Redis的name
# start,位起始位置
# end,位结束位置
bitop(operation, dest, *keys)
# 获取多个值,并将值做位运算,将最后的结果保存至新的name对应的值
# 参数:
# operation,AND(并) 、 OR(或) 、 NOT(非) 、 XOR(异或)
# dest, 新的Redis的name
# *keys,要查找的Redis的name
# 如:
bitop("AND", 'new_name', 'n1', 'n2', 'n3')
# 获取Redis中n1,n2,n3对应的值,然后讲所有的值做位运算(求并集),然后将结果保存 new_name 对应的值中
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, 要追加的字符串
------------- 字典的操作
- 存储
conn.hset('k','name','value')
conn.hset('k','name1','value1')
# 相当于 redis = {'k':{'name':'value','name1','value1'}}
- 获取
conn.hget('k','name') # 获取到 'value'
conn.hgetall('k') # 获取到 {'name':'value','name1','value1'}
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的所有键值
# 慎重使用hgetall 优先使用 hscan_iter
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,自增数(浮点数)
# 自增name对应的hash中的指定key的值,不存在则创建key=amount
hscan(name, cursor=0, match=None, count=None)
# 增量式迭代获取,对于数据大的数据非常有用,hscan可以实现分片的获取数据,并非一次性将数据全部获取完,从而放置内存被撑爆
# 参数:
# name,redis的name
# cursor,游标(基于游标分批取获取数据)
# match,匹配指定key,默认None 表示所有的key
# count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
# 如:
# 第一次:cursor1, data1 = r.hscan('xx', cursor=0, match=None, count=None)
# 第二次:cursor2, data1 = r.hscan('xx', cursor=cursor1, match=None, count=None)
# ...
# 直到返回值cursor的值为0时,表示数据已经通过分片获取完毕
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
------------- 列表的操作
补充: 列表没有类似scan_iter方法帮助我们分段遍历取到大量数据 需要自己实现
自定义增量迭代
# 由于redis类库中没有提供对列表元素的增量迭代,如果想要循环name对应的列表的所有元素,那么就需要:
# 1、获取name对应的所有列表
# 2、循环列表
# 但是,如果列表非常大,那么就有可能在第一步时就将程序的内容撑爆,所有有必要自定义一个增量迭代的功能:
def list_iter(name):
"""
自定义redis列表增量迭代
:param name: redis中的name,即:迭代name对应的列表
:return: yield 返回 列表元素
"""
list_count = r.llen(name)
for index in xrange(list_count):
yield r.lindex(name, index)
# 使用
for item in list_iter('pp'):
print item
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,索引结束位置
rpoplpush(src, dst)
# 从一个列表取出最右边的元素,同时将其添加至另一个列表的最左边
# 参数:
# src,要取数据的列表的name
# dst,要添加数据的列表的name
blpop(keys, timeout)
# 将多个列表排列,按照从左到右去pop对应列表的元素
# 参数:
# keys,redis的name的集合
# timeout,超时时间,当元素所有列表的元素获取完之后,阻塞等待列表内有数据的时间(秒), 0 表示永远阻塞
# 更多:
# r.brpop(keys, timeout),从右向左获取数据
brpoplpush(src, dst, timeout=0)
# 从一个列表的右侧移除一个元素并将其添加到另一个列表的左侧
# 参数:
# src,取出并要移除元素的列表对应的name
# dst,要插入元素的列表对应的name
# timeout,当src对应的列表中没有数据时,阻塞等待其有数据的超时时间(秒),0 表示永远阻塞
-------------集合的操作
Set操作,Set集合就是不允许重复的列表
sadd(name,values)
# name对应的集合中添加元素
scard(name)
# 获取name对应的集合中元素个数
sdiff(keys, *args)
# 在第一个name对应的集合中且不在其他name对应的集合的元素集合
sdiffstore(dest, keys, *args)
# 获取第一个name对应的集合中且不在其他name对应的集合,再将其新加入到dest对应的集合中
sinter(keys, *args)
# 获取多一个name对应集合的并集
sinterstore(dest, keys, *args)
# 获取多一个name对应集合的并集,再讲其加入到dest对应的集合中
sismember(name, value)
# 检查value是否是name对应的集合的成员
smembers(name)
# 获取name对应的集合的所有成员
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对应的集合中
sscan(name, cursor=0, match=None, count=None)
sscan_iter(name, match=None, count=None)
# 同字符串的操作,用于增量迭代分批获取元素,避免内存消耗太大
-------------有序集合的操作
有序集合,在集合的基础上,为每元素排序;元素的排序需要根据另外一个值来进行比较,所以,对于有序集合,每一个元素有两个值,即:值和分数,分数专门用来做排序。
zadd(name, *args, **kwargs)
# 在name对应的有序集合中添加元素
# 如:
# zadd('zz', 'n1', 1, 'n2', 2)
# 或
# zadd('zz', 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对应的有序集合的元素
# 参数:
# name,redis的name
# start,有序集合索引起始位置(非分数)
# end,有序集合索引结束位置(非分数)
# desc,排序规则,默认按照分数从小到大排序
# 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
zscan(name, cursor=0, match=None, count=None, score_cast_func=float)
zscan_iter(name, match=None, count=None,score_cast_func=float)
# 同字符串相似,相较于字符串新增score_cast_func,用来对分数进行操作
--------------------
redis 事务操作
pipe = conn.pipeline(transaction = True) # 创建事务
pipe.multi() # 创建可同时发送多条数据
pipe.set('k1','value1') # 发送数据操作示例
pipe.hset('k2','value2',1) # 发送数据操作示例
pipe.lpush('k3','value3') # 发送数据操作示例
pipe.execute() # 执行
==========Redis发布订阅
发布订阅三个角色
1.发布者
2.订阅者
3.频道
# Redis 通过 PUBLISH 、 SUBSCRIBE 等命令实现了订阅与发布模式。
发布者:
PUBLISH channel msg
将信息 message 发送到指定的频道 channel
订阅者:
SUBSCRIBE channel [channel ...]
订阅频道,可以同时订阅多个频道
UNSUBSCRIBE [channel ...]
取消订阅指定的频道, 如果不指定频道,则会取消订阅所有频道
PSUBSCRIBE pattern [pattern ...]
订阅一个或多个符合给定模式的频道,每个模式以 * 作为匹配符,比如 it* 匹配所 有以 it 开头的频道( it.news 、 it.blog 、 it.tweets 等等), news.* 匹配所有 以 news. 开头的频道( news.it 、 news.global.today 等等),诸如此类
PUNSUBSCRIBE [pattern [pattern ...]]
退订指定的规则, 如果没有参数则会退订所有规则
PUBSUB subcommand [argument [argument ...]]
查看订阅与发布系统状态
注意:使用发布订阅模式实现的消息队列,当有客户端订阅channel后只能收到后续发布到该频道的消息,之前发送的不会缓存,必须Provider和Consumer同时在线。
==========Redis 持久化
redis持久化
Redis是一种内存型数据库,一旦服务器进程退出,数据库的数据就会丢失,为了解决这个问题,Redis提供了两种持久化的方案,将内存中的数据保存到磁盘中,避免数据的丢失。
redis持久化之RDB
# RDB(持久化)
# 内存数据保存到磁盘
# 在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot)
# 优点:速度快,适合做备份,主从复制就是基于RDB持久化功能实现
# rdb通过再redis中使用save命令触发 rdb
RDB主要conf主要参数体现
dir /data/6379/ #定义持久化文件存储位置 # 如果报错需要手动创建
dbfilename dbmp.rdb #rdb持久化文件
save 900 1 #rdb机制 每900秒 有1个修改记录触发
save 300 10 #每300秒 10个修改记录触发
save 60 10000 #每60秒内 10000修改记录触发
1.启动redis服务端,准备配置文件
# 自己创建xxx.conf文件
daemonize yes # 后台启动
port 6379 # redis启动端口
logfile redis.log # 日志文件存放
dir /data/6379 #定义持久化文件存储位置 # 如果报错 mkdir data/端口号(~/data下)
dbfilename dbmp.rdb #rdb持久化文件
bind 10.0.0.10 127.0.0.1 #redis绑定地址
requirepass redhat #redis登录密码
save 900 1 #rdb机制 每900秒 有1个修改记录触发
save 300 10 #每300秒 10个修改记录触发
save 60 10000 #每60秒内 10000修改记录触发
2.使用自己创建的xxx.conf文件启动服务端
4.此时检查目录,/data/6379底下没有dbmp.rdb文件
5.通过save触发持久化,将数据写入RDB文件
redis持久化之AOF
# AOF(append-only log file)
#记录服务器执行的所有变更操作命令(
例如setdel等),并在服务器启动时,通过重新执行这些命令来还原数据集
AOF 文件中的命令全部以redis协议的格式保存,新命令追加到文件末尾。
# 优点:最大程序保证数据不丢
# 缺点:日志记录非常大
AOF主要参数体现
appendonly yes # 开启AOF持久化操作
# 三选一
appendfsync always 总是修改类的操作
everysec 每秒做一次持久化
no 依赖于系统自带的缓存大小机制
1.启动redis服务端,准备配置文件
# 自己创建xxx.conf文件
daemonize yes
port 6379
logfile /data/6379/redis.log
dir /data/6379
dbfilename dbmp.rdb
requirepass redhat
save 900 1
save 300 10
save 60 10000
appendonly yes
appendfsync everysec
2.使用自己创建的xxx.conf文件启动服务端
3.检查redis数据目录/data/6379/是否产生了aof文件
4.登录redis-cli,写入数据,实时检查aof文件信息
$ail -f appendonly.aof # 实时监控文件
5.设置新key,检查aof信息,然后关闭redis,检查数据是否持久化
### 补充redis不重启,切换RDB备份到AOF
# 切换仅临时切换 重启失效
1.确保redis版本在2.2以上
$redis-server -v # 查看版本
redis.conf服务端配置文件
daemonize yes
port 6379
logfile /data/6379/redis.log
dir /data/6379
dbfilename dbmp.rdb
save 900 1 #rdb机制 每900秒 有1个修改记录
save 300 10 #每300秒 10个修改记录
save 60 10000 #每60秒内 10000修改记录
2.备份这个rdb文件,保证数据安全
[root@pyyuc /data/6379 22:35:38]#cp dbmp.rdb /opt/
3.执行命令,开启AOF持久化
127.0.0.1:6379> CONFIG set appendonly yes #使用命令开启AOF功能
OK
127.0.0.1:6379> CONFIG SET save "" #使用命令关闭RDB功能
OK
4.修改conf文件以便下次重启服务自动转换
# rdb 以及aof的区别
rdb:基于快照的持久化,速度更快,一般用作备份,主从复制也是依赖于rdb持久化功能
aof:以追加的方式记录redis操作日志的文件。可以最大程度的保证redis数据安全,类似于mysql的binlog
=============Redis主从同步
原理:
1. 从服务器向主服务器发送 SYNC 命令。
2. 接到 SYNC 命令的主服务器会调用BGSAVE 命令,创建一个 RDB 文件,并使用缓冲区记录接下来执行的所有写命令。
3. 当主服务器执行完 BGSAVE 命令时,它会向从服务器发送 RDB 文件,而从服务器则会接收并载入这个文件。
4. 主服务器将缓冲区储存的所有写命令发送给从服务器执行。
-------------
1、在开启主从复制的时候,使用的是RDB方式的,同步主从数据的
2、同步开始之后,通过主库命令传播的方式,主动的复制方式实现
3、2.8以后实现PSYNC的机制,实现断线重连
测试环境
准备两个或两个以上redis实例
mkdir /data/638{0..2} #创建6380 6381 6382文件夹
配置文件示例:
vim /data/6380/redis.conf
port 6380 # 端口以文件存放位置变化
daemonize yes
pidfile /data/6380/redis.pid
loglevel notice
logfile "/data/6380/redis.log"
dbfilename dump.rdb
dir /data/6380
protected-mode no
6381.conf
vim /data/6381/redis.conf
port 6381
daemonize yes
pidfile /data/6381/redis.pid
loglevel notice
logfile "/data/6381/redis.log"
dbfilename dump.rdb
dir /data/6381
protected-mode no
6382.conf
port 6382
daemonize yes
pidfile /data/6382/redis.pid
loglevel notice
logfile "/data/6382/redis.log"
dbfilename dump.rdb
dir /data/6382
protected-mode no
启动三个redis实例
redis-server /data/6380/redis.conf
redis-server /data/6381/redis.conf
redis-server /data/6382/redis.conf
主从规划
主节点:6380
从节点:6381、6382
配置主从同步
6381/6382命令行
redis-cli -p 6381
SLAVEOF 127.0.0.1 6380 #指明主的地址
redis-cli -p 6382
SLAVEOF 127.0.0.1 6380 #指明主的地址
手动进行主从复制故障切换
#关闭主库6380
redis-cli -p 6380
shutdown # 关闭数据库
新主库创建
1.关闭从库身份
redis-cli -p 6381
info replication
slaveof no one # 关闭从库身份
2.将6382设为6381的从库
6382连接到6381:
[root@db03 ~]# redis-cli -p 6382
127.0.0.1:6382> SLAVEOF no one # 关闭原先旧的从库身份
127.0.0.1:6382> SLAVEOF 127.0.0.1 6381 # 创建新的从库身份
### Redis-Sentinel(哨兵)
# 保护redis主从集群正常运转,当主库挂掉后,自动的在从库中挑选新的主库,进行同步
# redis的一个进程,但是不存储数据,只是监控redis
安装与配置
创建redis-sentinel配置文件
# 创建多个配置文件只需要修改port端口号以及对应的数字 以及对应的节点别名(重要)
// Sentinel节点的端口
port 26379
dir /var/redis/data/
logfile "26379.log"
daemonize yes # 后台守护
// 当前Sentinel节点监控 192.168.119.10:6379 这个主节点
// 2代表判断主节点失败至少需要2个Sentinel节点节点同意
// mymaster是主节点的别名 可以自行设置
sentinel monitor 别名 192.168.119.10 6379 2
//每个Sentinel节点都要定期PING命令来判断Redis数据节点和其余Sentinel节点是否可达,如果超过30000毫秒30s且没有回复,则判定不可达
sentinel down-after-milliseconds 别名 30000
//当Sentinel节点集合对主节点故障判定达成一致时,Sentinel领导者节点会做故障转移操作,选出新的主节点,
原来的从节点会向新的主节点发起复制操作,限制每次向新的主节点发起复制操作的从节点个数为1
sentinel parallel-syncs 别名 1
//故障转移超时时间为180000毫秒
sentinel failover-timeout 别名 180000
然后启动三个sentinel哨兵
redis-sentinel /etc/redis-sentinel-26379.conf
redis-sentinel /etc/redis-sentinel-26380.conf
redis-sentinel /etc/redis-sentinel-26381.conf
查看哨兵是否成功通信
redis-cli -p 启动端口(主站) info replication 有如下信息代表哨兵正常启动
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.119.10:6379,slaves=2,sentinels=3
#看到最后一条信息正确即成功了哨兵,哨兵主节点名字叫做mymaster,状态ok,监控地址是192.168.119.10:6379,有两个从节点,3个哨兵
可以杀死主站进行查看是否成功切换(原理就是更改redis配置文件,切换主从身份)
重启主站看是否将其添加至从站
=========redis-cluster 集群
分布式数据库首要解决把整个数据集按照分区规则映射到多个节点的问题,即把数据集划分到多个节点上,每个节点负责整个数据的一个子集。
常见的分区规则有哈希分区和顺序分区。Redis Cluster采用哈希分区规则,因此接下来会讨论哈希分区规则。
节点取余分区
一致性哈希分区
虚拟槽分区(redis-cluster采用的方式)
安装:
官方提供通过ruby语言的脚本一键安装
1.环境准备
通过配置,开启redis-cluster
port 7000
daemonize yes
dir "/opt/redis/data"
logfile "7000.log"
dbfilename "dump-7000.rdb"
cluster-enabled yes #开启集群模式
cluster-config-file nodes-7000.conf #集群内部的配置文件
cluster-require-full-coverage no
#redis cluster需要16384个slot都正常的时候才能对外提供服务,换句话说,
只要任何一个slot异常那么整个cluster不对外提供服务。 因此生产环境一般为no
redis支持多实例的功能,我们在单机演示集群搭建,需要6个实例,三个是主节点,三个是从节点,数量为6个节点才能保证高可用的集群。
###每个节点仅仅是端口运行的不同!
[root@yugo /opt/redis/config 17:12:30]#ls
redis-7000.conf redis-7002.conf redis-7004.conf
redis-7001.conf redis-7003.conf redis-7005.conf
#确保每个配置文件中的端口修改!!
2.运行redis实例
# 创建6个节点的redis实例
redis-server redis-7000.conf.........
此时集群还不可用,可以通过登录redis查看
3.创建开启redis-cluster
准备ruby环境
下载、编译、安装Ruby
安装rubygem redis
安装redis-trib.rb命令
第一步,安装ruby(这些命令可以放入一个sh脚本文件里)
复制代码
#下载ruby # 建议到/etc/opt/下下载
wget https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.1.tar.gz
#安装ruby
tar -zxf ruby-2.3.1.tar.gz
./configure --prefix=/opt/ruby/
make && make install
# 安装ruby gem 包管理工具 # 建议cd到/opt/ruby/bin 目录下安装
wget http://rubygems.org/downloads/redis-3.3.0.gem
/opt/ruby/bin/gem install -l redis-3.3.0.gem # 使用绝对路径
增加环境变量 vim /etc/profile 添加
/opt/ruby/bin
通过find 找到 redis-trib.rb
find /etc/opt -name redis-trib.rb # 从安装redis的路径找 我的redis安装在/etc/opt下
一键开启redis-cluster集群 # 分配槽
#每个主节点,有一个从节点,代表--replicas 1
/通过找到的绝对路径/redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005
#集群自动分配主从关系 7000、7001、7002为 7003、7004、7005 主动关系
查看集群状态
redis-cli -p 7000 cluster info
redis-cli -p 7000 cluster nodes #等同于查看nodes-7000.conf文件节点信息
集群主节点状态
redis-cli -p 7000 cluster nodes | grep master
集群从节点状态
redis-cli -p 7000 cluster nodes | grep slave
####***登录集群必须使用redis-cli -c -p 7000必须加上-c参数
Songzhibin