十七、搭建Redis主-备(mster-slave)+哨兵(sentinel)
redis中文文档:http://www.redis.cn/
redis可视化工具:https://raw.githubusercontent.com/caoxinyu/RedisClient/windows/release/redisclient-win32.x86.2.0.exe
1,Windows安装
①下载redis并安装msi文件
https://github.com/MicrosoftArchive/redis
②设置环境变量
③运行server
redis-server D:Redis edis.windows-service.conf
④运行client
redis-cli
2,Linux安装
①进入usr-》local目录下
②$ wget http://download.redis.io/releases/redis-4.0.6.tar.gz 下载redis
③$ tar xzf redis-4.0.6.tar.gz 解压
④$ cd redis-4.0.6 进入目录
⑤$ make 编译
⑥编译好之后进入src文件下,发现编译好了几个可执行文件。将这几个文件考到sbin下面
⑦配置环境变量
/etc/profile 打开文件,设置
export PATH=$PATH:/usr/local/redis-4.0.6/sbin
⑧重启centOS
set username jack 设置缓存
get username 读取缓存
keys * 查看所有的key
flushall 清空所有数据(没有一点安全保证)
bgsave 立马持久化缓存信息
bind 127.0.0.1 如果没有注销掉bind,则Redis监听所有链接
protected-mode yes【yes只能回路地址127.0.0.1 本地访问】
生产环境一般配置
#bind
protected-mode no
port 6379 配置redis端口
dir ./ 配置rdb和logfile的存放位置(例如:dir /usr/redis/sbin/db)
databases 16设置数据库的数量
logfile "server_log.txt" 指定日志文件名称
loglevel notice 日志记录级别
rename-command 改变命令的名称
redis-cli -h 192.168.1.102 -p 6379 远程链接redis
redis-cli -h 192.168.1.102 -p 6379 -a 123456 --stat 监控redis状态
设置密码: 在配置文件中设置requirepass(例如 requirepass 123456) 连接redis服务器时需要带上密码(例如:redis-cli -h 192.168.1.102 -p 6379 -a 123456)
redis远程链接失败总结
①看看redis-server指向的配置文件是否指定对了
②conf配置
#bind
protected-mode no
③关闭centOS的防火墙
systemctl stop firewalld.service #停止firewall
systemctl disable firewalld.service #禁止firewall开机启动
1,设置key的过期时间
persist【O(1)】:PERSIST key【移除给定key的生存时间】
expire【O(1)】:EXPIRE key seconds【设置key的过期时间,单位秒】
pexpire【O(1)】:PEXPIRE key milliseconds【设置key的过期时间,单位毫秒】
expireat【O(1)】:EXPIREAT key timestamp【设置key的过期时间,单位时间戳】
2,Redis配置
①maxmemory设置最大内存空间(默认注释)
②maxmemory-policy最大内存空间策略(默认注释)
策略:
# volatile-lru -> 删除使用LRU算法设置过期的key
# allkeys-lru ->根据LRU算法删除任何key
# volatile-random -> 删除过期集合的key
# allkeys-random ->删除一个随机key,任何key
# volatile-ttl ->删除最接近过期时间的key
# noeviction -> 如果内存写满,则抛异常
lru :最久未使用的
wireshark 网络封包分析软件
nuget StackExchange.Redis
public static class RedisCache { private static ConnectionMultiplexer connection; private static IDatabase database; static RedisCache() { var options=new ConfigurationOptions() { Password = "123456", EndPoints = { { "192.168.1.101", 6379 } } }; connection = ConnectionMultiplexer.Connect(options); database = connection.GetDatabase(); } public static void Set(string key,string str) { database.StringSet(key, str); } public static string Get(string key) { return database.StringGet(key); } }
https://stackexchange.github.io/StackExchange.Redis/Configuration
1,RedisServer struct
2,RedisDb struct 标识一个db,config配置默认为16个RedisDb
3,RedisObject struct 也就是对象化的数据结构表示
4,SDS struct char[] 一个包装类(存放字符串)
1,get/set 命令(时间复杂度O(1))
SET key value [EX seconds] [PX milliseconds] [NX|XX]
EX
seconds – 设置键key的过期时间,单位时秒PX
milliseconds – 设置键key的过期时间,单位时毫秒NX
– 只有键key不存在的时候才会设置key的值XX
– 只有键key存在的时候才会设置key的值
案例:使用分布式锁 set a a NX 业务操作 del a
2,incr/decr/incrby/decrby
incr 自增加1(i++)
decr自增减1(i--)
incrby自增加n(i=i+n)
decrby自增减n(i=i-n)
如果存放string中的value是int,则内存使用int存储值
3,ttl key 查询key还有多长时间过期(-2 表示过期)
4,PSETEX/SETEX
PSETEX key milliseconds value
SETEX key seconds value
PSETEX和SETEX一样,唯一的区别是到期时间以毫秒为单位,而不是秒。
双向链表
可以用作队列(lpush/rpop、rpush/lpop)或者栈(lpush/lpop、rpush/rpop)
一般用作队列
lpush:LPUSH key value [value ...]【将所有指定的值插入到存于 key 的列表的头部。如果 key 不存在,那么在进行 push 操作前会创建一个空列表】
lpop:LPOP key【移除并且返回 key 对应的 list 的第一个元素】
rpush:RPUSH key value [value ...]【向存于 key 的列表的尾部插入所有指定的值。如果 key 不存在,那么会创建一个空的列表然后再进行 push 操作】
rpop:RPOP key【移除并返回存于 key 的 list 的最后一个元素】
llen:LLEN key【返回存储在 key 里的list的长度。 如果 key 不存在,那么就被看作是空list,并且返回长度为 0】
blpop:BLPOP key [key ...] timeout【弹出最前面的值,如果列表中没有值,会阻塞】
brpop:BRPOP key [key ...] timeout【弹出最后面的值,如果列表中没有值,会阻塞】
注:timeout:等待时间,单位秒。0:标识永久等待
lrange:LRANGE key start stop【返回存储在 key 的列表里指定范围内的元素】
注:start stop位数组的下标
linsert:LINSERT key BEFORE|AFTER pivot value【把 value 插入存于 key 的列表中在基准值 pivot 的前面或后面】
注:BEFORE为之前/AFTER为之后
pivot:list中的值
hget:HGET key field【返回 key 指定的哈希集中该字段所关联的值】
hset:HSET key field value
设置 key 指定的哈希集中指定字段的值。
如果 key 指定的哈希集不存在,会创建一个新的哈希集并与 key 关联。
如果字段在哈希集中存在,它将被重写。
field:字典中的key
value:字典中的value
hdel:HDEL key field [field ...]【从 key 指定的哈希集中移除指定的域】
hexists:HEXISTS key field【返回hash里面field是否存在】
hgetall:HGETALL key【返回 key 指定的哈希集中所有的字段和值】
hkeys:HKEYS key【返回 key 指定的哈希集中所有字段的名字】
hlen:HLEN key【返回 key 指定的哈希集包含的字段的数量】
hmget:HMGET key field [field ...]【返回 key 指定的哈希集中指定字段的值】
hmset:HMSET key field value [field value ...]【设置 key 指定的哈希集中指定字段的值】
hvals:HVALS key【返回 key 指定的哈希集中所有字段的值】
无value的字典
sadd【O(N)】:SADD key member [member ...]【添加一个或多个指定的member元素到集合的 key中】
scard【O(1)】:SCARD key【返回集合存储的key的基数 (集合元素的数量)】
sismember【O(1)】:SISMEMBER key member【检查成员member是否存在集合中】
smembers【O(N)】:SMEMBERS key【返回key集合所有的元素】
spop【O(1)】:SPOP key [count]【随机从集合弹出count(默认1)个值】
srem【O(N)】:SREM key member [member ...]【在key集合中移除指定的元素】
差集、交集、并集
sdiff【O(N)】:SDIFF key [key ...]【差集】
sdiffstore【O(N)】:SDIFFSTORE destination key [key ...]【将差集保存到destination中】
sinter【O(N*M)】:SINTER key [key ...]【交集】
sinterstore【O(N*M)】:SINTERSTORE destination key [key ...]【将交集保存到destination中】
sunion【O(N)】:SUNION key [key ...]【并集】
sunionstore【O(N)】:SUNIONSTORE destination key [key ...]【将并集保存到destination中】
一般用做优先级队列
zadd【O(log(N))】:ZADD key [NX|XX] [CH] [INCR] score member [score member ...]【将所有指定成员添加到键为key有序集合(sorted set)里面】
XX: 仅仅更新存在的成员,不添加新成员。
NX: 不更新存在的成员。只添加新成员。
CH: 修改返回值为发生变化的成员总数,原始是返回新添加成员的总数 (CH 是 changed 的意思)。更改的元素是新添加的成员,已经存在的成员更新分数。 所以在命令中指定的成员有相同的分数将不被计算在内。注:在通常情况下,ZADD返回值只计算新添加成员的数量。
INCR: 当ZADD指定这个选项时,成员的操作就等同ZINCRBY命令,对成员的分数进行递增操作。
zcard【O(1)】:ZCARD key【返回key的有序集元素个数】
zrange【O(log(N)+M)】:ZRANGE key start stop [WITHSCORES]【从小到大排序。显示字典信息(WITHSCORES 显示出key)】【例如:zrange sdic 0 -1 withscores】
zrevrange【O(log(N)+M)】:ZREVRANGE key start stop [WITHSCORES]【从大到小排序。显示字典信息(WITHSCORES 显示出key)】【例如:zrevrange sdic 0 -1 withscores】
zremrangebyrank【O(log(N)+M)】:ZREMRANGEBYRANK key start stop【移除有序集key中】
0:最小
-1:最大
-2:第二大。。以此类推
等于distinct+count
计算不是100%准确
使用场景:计算某一天独立ip访问数量
pfadd 20180125 192.168.1.1 192.168.1.1 192.168.1.2【O(1)】
pfcount 20180125【O(1)】
multi:开启一个事务
exec:提交事务
discard:回滚事务
watch【乐观锁】:WATCH key [key ...]
例如客户端1watch username并开启了一个事务。当在另一个客户端2修改了username值的时候。客户端1事务exec会执行失败(回滚)
subscribe:SUBSCRIBE channel [channel ...]【订阅频道】
publish:PUBLISH channel message【发布消息】
psubscribe:PSUBSCRIBE pattern [pattern ...]【订阅给定的模式的频道】
static void Main(string[] args) { var con= ConnectionMultiplexer.Connect("192.168.1.102:6379"); var db = con.GetDatabase(); var batch= db.CreateBatch(); batch.SetAddAsync("username", "hunter"); batch.ListInsertBeforeAsync("list", "1", "2"); batch.HashSetAsync("dic", "username", "jack"); batch.Execute(); Console.ReadKey(); }
Lua工具下载地址:LuaForWindows_v5.1.4-46.exe
1,命令
EVAL script numkeys key [key ...] arg [arg ...]
script:指定lua脚本的路径。或者直接写lua脚本
numkeys:制定参数的个数
key:在脚本中使用到的key(KEYS 全局key集合。单项:KEYS[1])
arg:在脚本中使用到的arg(ARGV 全局arg集合。单项:ARGV[1])
注:KEYS[0]或者ARGV[0]是错误的写法
2,Lua脚本调用Redis
redis.call('set','foo','bar')
redis.pcall('hset','dic','key','value')
redis.call() 与 redis.pcall()很类似, 他们唯一的区别是当redis命令执行结果返回错误时,
redis.call()将返回给调用者一个错误,而redis.pcall()会将捕获的错误以Lua表的形式返回
3,案例
①直接嵌入lua脚本
> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second 1) "key1" 2) "key2" 3) "first" 4) "second"
②调用独立的lua脚本
return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}
redis-cli --eval C:UsersHunter2DesktopLua ext.lua a b , a b 1) "key1" 2) "key2" 3) "first" 4) "second"
③更高级的调用
需求:输入的参数在集合中存在,则打印出来
local key=KEYS[1] local args=ARGV local result={} local list=redis.call("smembers",key) for m,n in ipairs(args) do local exists= redis.call("sismember",key,n) if(exists==1)then table.insert(result,n) end end return result
C:WINDOWSsystem32>redis-cli --eval C:UsersHunter2DesktopLua ext.lua list , 1 2 3 10 1) "1" 2) "2" 3) "3"
注:逗号分割key和agrv
1,rds(默认)
快照模式,定时保存。如果重启有可能会丢失部分数据
配置信息:
强制持久化命令:save(客户端阻塞)或者bgsave(会开启一个进程执行)
2,aof
来一条命令则保存一次,aof文件保存的是文本协议
开启方式
①注销save
②appendonly设置为yes
三种写入模式:
#appendfsync always:来一条保存一条,强制保存到硬盘【安全性高,性能差】
appendfsync everysec:1秒强制保存到硬盘一次【折中方案】
#appendfsync no:来一条保存一条,只保存到系统的缓冲期,至于保存到硬盘是有系统决定【安全性差,性能高】
1,配置mster-slave
配置文件# slaveof <masterip> <masterport>配置主机地址
例如:slaveof 192.168.1.102 6379
如果主redis有密码需要配置# masterauth <master-password>
info 查看redis信息
注:一旦redis服务器被设置为slave,则该redis不能写入了。只能从master中写入
2,c#中使用
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using StackExchange.Redis; namespace ConsoleApp1 { class Program { static void Main(string[] args) { var con= ConnectionMultiplexer.Connect("192.168.1.102:6379,192.168.1.103:6379"); var db = con.GetDatabase(); db.StringAppendAsync("password", "123456");//会使用主redis var p = db.StringGetAsync("password").Result;//会使用备redis Console.WriteLine(p); Console.ReadKey(); } } }
注:如果主redis挂了,则写入会失败。可以查询
十七、搭建Redis主-备(mster-slave)+哨兵(sentinel)
sentinel监视redis主备,当主redis挂了,sentinel自动让一个slave变为master。
当原来master恢复之后,原来master恢复已经成为slave了
1,架构sentinel
例如在一台centos上建立三个哨兵
这三个哨兵会监视一个master,当有2个哨兵主观认为master挂了,则sentinel自动让一个slave变为master
主redis:192.168.1.102:6379
备redis:192.168.1.103:6379
主redis未挂时,备redis的状态:
主redis挂了,备redis的状态:
sentinel案例下载:https://pan.baidu.com/s/1ggvCu47
1,配置具体的sentinel
①拷贝redis-sentinel程序和sentinel.conf配置文件到s1、s2、s3文件中
②配置sentinel.conf
port 26379 #设置sentinel的端口
protected-mode no #将protected-mode设置为no
sentinel monitor <master-name> <ip> <redis-port> <quorum> #设置监控的master
master-name:设置主redis 的名称,随便写
ip:主redis 的ip地址
redis-port:主redis的端口号
quorum:当主观认为master挂掉的哨兵达到2个时,则客观认为master挂了。需要切换master了
例:sentinel monitor mymaster 192.168.1.102 6379 2
sentinel auth-pass <master-name> <password> #如果master设置了密码,则需要配置master的密码
例:sentinel auth-pass mymaster 123456
sentinel down-after-milliseconds mymaster 30000 #master30秒没响应,则主观认为master挂了
③启动sentinel
进入放置sentinel的文件夹
执行命令./redis-sentinel ./sentinel.conf 启动sentinel
注意:在同一系统下运行多个sentinel,sentinel的端口号不要重复
python写的一个web监控
1,安装redislive
①首先需要安装pip(python的安装工具包pip-9.0.1.tar.gz)【相当于nuget】
下载地址:https://pypi.python.org/pypi/pip
网速慢可以下载:https://pan.baidu.com/s/1qZezffm
拷贝到centos的usr文件下,解压,进入解压文件中
python setup.py install 安装
②安装tornado【相当于iis】
pip install tornado --upgrade
③安装redis.py 【python redis驱动,相当于.net StackExchange】
pip install redis
④安装python-dateutil 【类库,相当于dll】
pip install python-dateutil --upgrade 强制更新到最新
⑤获取redislive的源代码
https://github.com/nkrode/RedisLive
解压放到centos的usr文件下。进入文件夹
将redis-live.conf.example改为redis-live.conf
redis-live.conf配置文件解析:
RedisServers:监控的RedisServers的服务器地址和端口
DataStoreType:存储类型(redis或者Sqlite存储,默认redis)
RedisStatsServer:配置存储redis的服务器地址和端口
{ "RedisServers": [ { "server": "192.168.1.103", "port" : 6379 } ], "DataStoreType" : "redis", "RedisStatsServer": { "server" : "192.168.1.103", "port" : 6379 }, "SqliteStatsStore" : { "path": "to your sql lite file" } }
⑥开启监控脚本:./redis-monitor.py --duration=120【duration=120 默认采集二分钟】
⑦开启web站点:./redis-live.py
⑧访问
http://localhost:8888/index.html
http://192.168.1.103:8888/index.html
参考文档:http://www.redis.cn/topics/cluster-tutorial.html
可能出现各种依赖问题请参考:
https://www.cnblogs.com/carryping/p/7447823.html
http://blog.csdn.net/Hello_World_QWP/article/details/78260684
http://blog.csdn.net/hello_world_qwp/article/details/78261618
1,开启cluster(集群)模式
配置文件:
cluster-enabled yes 开启集群
cluster-config-file nodes-6379.conf 集群节点文件
2,redis src下找到redis-trib.rb
①安装ruby环境:yum install ruby
②ruby的redis驱动:gem install redis
③rubygems安装:yum install -y rubygems
④通过第三方工具进行安装
./redis-trib.rb create --replicas 1 192.168.1.103:6379 192.168.1.103:6380 192.168.1.103:6381 192.168.1.103:6382
cluster nodes 查看集群
redis-cli -c 使用这种redis-cli的开启方式
3,c#驱动访问方式:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using StackExchange.Redis; namespace ConsoleApp1 { class Program { static void Main(string[] args) { var con= ConnectionMultiplexer.Connect("192.168.1.103:6379,192.168.1.103:6380,192.168.1.103:6381,192.168.1.103:6382"); var db = con.GetDatabase(); db.StringSet("cc", "123456");//会使用主redis var p = db.StringGetAsync("cc").Result;//会使用备redis Console.WriteLine(p); Console.ReadKey(); } } }
升级完ruby需要重启
注意:
>>>创建群集
***错误:集群创建的配置无效。
*** Redis群集至少需要3个主节点。
***对于每个节点3个节点和1个副本,这是不可能的。
***至少需要6个节点。