array(php)-->cache(mem)-->nosql(主从 事务 持久化)
使用:返回值 场景 字段
设计:字段 mysql冷数据 写入 测试
weibo:推拉 关注/被关注 用户注册登录
主从(含义、迁移和切换) + 运维(命令和参数) +
Redis-sentinel(集群管理工具) | 语法+配置+区别
主(-rdb +aof)--从(+rdb slaveof passwd slave-read-only)备份—>读写分离—>任务分离(分担备份、计算)
主从:
主1—>从1(可快速切换为主)切换时主需要全部dump出来,再aof,不要多台slave同时启动,以防io剧增
—>从2(从1 从2 互为主从,主1宕机可快速切换)
--redis 主从同步
redis和memcache比较像的,memcache可以实现服务器的集群,redis肯定也是可以的。下面在一台机,实现redis主从复制。
1,copy一下redis.conf,生成一个从机的配置
cp /usr/local/redis/redis.conf /usr/local/redis/redis_slave.conf
2,修改主服务器的配置redis.conf
bind 127.0.0.1
3,修改从服务器的配置redis_slave.conf
pidfile /usr/local/redis/var/redis_slave.pid
port 10002
bind 127.0.0.1
logfile /usr/local/redis/var/redis_slave.log
dbfilename dump_slave.rdb
slaveof 127.0.0.1 6379
4,启动主服务器,从服务器
redis-server /usr/local/redis/redis.conf
redis-server /usr/local/redis/redis_slave.conf
迁移:
1.
在新服务器上启动一个
redis
实例
,
slave
不仅读取,还要写入
slave-read-only no
2.
使新的
redis
服务成为
slave
redis
127.0.0.1:6379> SLAVEOF 192.168.1.100 6379
3.
完成迁移
,原有
master
关闭了
redis
127.0.0.1:6379> SLAVEOF no one
运维:
DBSIZE查看当前库中的key数量
BGREWRITEAOF后台进程重写AOF
BGSAVE 后台保存rdb快照
SAVE 保存rdb快照
LASTSAVE 上次保存时间
SLAVEOF 设为slave服务器
FLUSHALL 清空所有db
FLUSHDB 清空当前db
SHUTDOWN[""|save|nosave] 断开连接,关闭服务器
SLOWLOG 显示慢查询
INFO 显示服务器信息
CONFIG GET获取配置信息
CONFIG SET设置配置信息
MONITOR 打开控制台
SYNC 主从同步
CLIENT LIST客户端列表
CLIENT KILL关闭某个客户端
CLIENT SETNAME为客户端设置名字
CLIENT GETNAME
获取客户端名字
2.观察:内存 主从 持久化 fork耗时 慢日志
--
内存
# Memory
used_memory:859192数据结构的空间
used_memory_rss:7634944实占空间
mem_fragmentation_ratio:8.89前2者的比例,1.N为佳
--
主从复制
# Replication
role:slave
master_host:192.168.1.128
master_port:6379
master_link_status:up
--
持久化
# Persistence
rdb_changes_since_last_save:0
rdb_last_save_time:1375224063
--fork
耗时
#Status
latest_fork_usec:936 上次持久化花费微秒
config
get/set
slowlog
-log-slower-than
CONFIG get/SET
slowlog
-max-
len
slow log get
获取慢日志
补充:Redis-sentinel:
下面是我对Redis 2.9.11-非稳定版作的测试总结,更新的版本可能没有下面的问题。
1.使用Redis-sentinel,redis实例必须在非集群模式下运行
2.如果默认主从关系的主挂了,这时启动failover,在failover过程中,有n个slave还活着,那么新master就有n个slave,旧master重启还可以加入集群,但其他在failover之前或过程中挂掉的slave重启是不能加入集群的,除非修改它们的配置文件,将master指向新master.
3.如果默认主从关系变了,其中一个slave挂了重启,它的master还是指向配置文件指定的,但它也是新master的slave,这是由于旧master已变为新master的slave,所以旧master的slave也是新master的slave,新master的slave数不变,这可从命令:sentinel slaves mymaster看出。
ps:从这里可以看出,slave重启不会重定向master,主从关系是sentinel发info请求获取的
4.如果默认主从关系的主挂了且failover正常结束,旧master重启还可以加入集群,但旧master第二次重启就不能加入集群了。
5.只要有slave of master命令调用,即主从关系建立, 就会触发主和该从采用save方式持久化数据,不论你是否禁止save.
6.在集群中,如果默认主从关系的主挂了并立即重启,如果主没有做持久化,数据会完全丢失,从而从的数据也被清空。
7.在集群中,如果非默认主从关系的主挂了并立即重启,主从关系变乱,集群不可用。
区别:
Redis--分库快速写 与 Memcached--搜索纯读取
区别: redis memcache
网络IO 单线程 多线程
内存 申请 预先申请
客户端 操作少,快 操作多,慢
数据类型 多 少
持久化 支持swap 不支持
数据一致性 支持 不支持
分布式 Twemproxy consistent_hash
有效性 LRU--惰性失效 | expire--惰性+随机+暴力
内容 字符串 字符串 图片、视频
配置:
redis config
==配置文件全解===分布时效+效率安全
分布和时效:基本 + 主从 + 限制kv
效率和安全:日志 + 慢查询 +服务端 + 配置信息 +连接测试 + 安全
==基本配置
daemonize no 是否以后台进程启动
databases 16 创建database的数量(默认选中的是database 0)
save 900 1 #刷新快照到硬盘中,必须满足两者要求才会触发,即900秒之后至少1个关键字发生变化。
save 300 10 #必须是300秒之后至少10个关键字发生变化。
save 60 10000 #必须是60秒之后至少10000个关键字发生变化。
stop-writes-on-bgsave-error yes #后台存储错误停止写。
rdbcompression yes #使用LZF压缩rdb文件。
rdbchecksum yes #存储和加载rdb文件时校验。
dbfilename dump.rdb #设置rdb文件名。
dir ./ #设置工作目录,rdb文件会写入该目录。
==主从配置
slaveof <masterip> <masterport> 设为某台机器的从服务器
masterauth <master-password> 连接主服务器的密码
slave-serve-stale-data yes # 当主从断开或正在复制中,从服务器是否应答
slave-read-only yes #从服务器只读
repl-ping-slave-period 10 #从ping主的时间间隔,秒为单位
repl-timeout 60 #主从超时时间(超时认为断线了),要比period大
slave-priority 100 #如果master不能再正常工作,那么会在多个slave中,选择优先值最小的一个slave提升为master,优先值为0表示不能提升为master。
repl-disable-tcp-nodelay no #主端是否合并数据,大块发送给slave
slave-priority 100 从服务器的优先级,当主服挂了,会自动挑slave priority最小的为主服
===安全
requirepass foobared # 需要密码
rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52 #如果公共环境,可以重命名部分敏感命令 如config
===限制
maxclients 10000 #最大连接数
maxmemory <bytes> #最大使用内存
maxmemory-policy volatile-lru #内存到极限后的处理
volatile-lru -> LRU算法删除过期key
allkeys-lru -> LRU算法删除key(不区分过不过期)
volatile-random -> 随机删除过期key
allkeys-random -> 随机删除key(不区分过不过期)
volatile-ttl -> 删除快过期的key
noeviction -> 不删除,返回错误信息
#解释 LRU ttl都是近似算法,可以选N个,再比较最适宜T踢出的数据
maxmemory-samples 3
====日志模式
appendonly no #是否仅要日志
appendfsync no # 系统缓冲,统一写,速度快
appendfsync always # 系统不缓冲,直接写,慢,丢失数据少
appendfsync everysec #折衷,每秒写1次
no-appendfsync-on-rewrite no #为yes,则其他线程的数据放内存里,合并写入(速度快,容易丢失的多)
auto-AOF-rewrite-percentage 100 当前aof文件是上次重写是大N%时重写
auto-AOF-rewrite-min-size 64mb aof重写至少要达到的大小
====慢查询
slowlog-log-slower-than 10000 #记录响应时间大于10000微秒的慢查询
slowlog-max-len 128 # 最多记录128条
====服务端命令
time 返回时间戳+微秒
dbsize 返回key的数量
bgrewriteaof 重写aof
bgsave 后台开启子进程dump数据
save 阻塞进程dump数据
lastsave
slaveof host port 做host port的从服务器(数据清空,复制新主内容)
slaveof no one 变成主服务器(原数据不丢失,一般用于主服失败后)
flushdb 清空当前数据库的所有数据
flushall 清空所有数据库的所有数据(误用了怎么办?)
shutdown [save/nosave] 关闭服务器,保存数据,修改AOF(如果设置)
slowlog get 获取慢查询日志
slowlog len 获取慢查询日志条数
slowlog reset 清空慢查询
info []
config get 选项(支持*通配)
config set 选项 值
config rewrite 把值写到配置文件
config restart 更新info命令的信息
debug object key #调试选项,看一个key的情况
debug segfault #模拟段错误,让服务器崩溃
object key (refcount|encoding|idletime)
monitor #打开控制台,观察命令(调试用)
client list #列出所有连接
client kill #杀死某个连接 CLIENT KILL 127.0.0.1:43501
client getname #获取连接的名称 默认nil
client setname "名称" #设置连接名称,便于调试
====连接命令===
auth 密码 #密码登陆(如果有密码)
ping #测试服务器是否可用
echo "some content" #测试服务器是否正常交互
select 0/1/2... #选择数据库
quit #退出连接
语法:
<?php
/*
创建 连接=>curd+存在性+批量+数据增减+排序+生存时效+配置=>持久化+主从+事务
string 静态数据:存取
hash 属性数据:关系
set:去重数据:集合
list:列表数据:头尾
sort set 排序数据:先后
*/
//存取 时效 加减1 键名操作 事务 持久化
//get set setex setex delete mset getMultiple flushdb flushall| ttl persist setTimeout expireAt|
//incr incrby decr decrby | multi exec discard watch unwatch
//save bgsave lastsave bgrewriteaof slaveof info auth
//keys randomKey type dbsize rename renameNx move exists
//set s +add rem move pop sort + inter/store union/store diff-前面的差集/store +randMember members contains size
//zset z+ add rem |rev/range rem/rangebyscore score rank |count incrby size |union/inter
//list:l r + push/x pop+set get | l+range/trim/rem/insert lsize |rpoplpush
//string:getSet get/setRange append strlen
//hash h +m/set m/get del +keys vals getall +len exists incrby
//ttl -2 是过期了,-1是持久有效
//sort
/*
'by' => 'some_pattern_*',
'limit' => array(0, 1),
'get' => 'some_other_pattern_*' or an array of patterns,
'sort' => 'asc' or 'desc',
'alpha' => TRUE,
'store' => 'external-key'
*/
$r = new Redis;
$r->connect('127.0.0.1',6379);
$r->auth('password');
$option = $r->ping();
$r->set('key','val');
$val = $r->get('key');
// echo $val;
$r->setnx('key','val');
$nx = $r->get('key');
$r->setex('keys',3,'vals');
$time = $r->ttl('keys');
//echo $time;
$num = $r->delete('key');
//echo $num;
$r->persist('keys');
$val = $r->get('keys');
//echo $val;
$r->mset(array('key1'=>'val1','key2'=>'val2','key3'=>'val3','key4'=>'val4','key5'=>'val5'));
$val = $r->get('key3');
// echo $val;
$vals = $r->getMultiple(array('key1','key2','key3','key4','key5'));
// echo '<pre>';
// print_r($vals);
$have = $r->exists('key2');
// echo($have);
//$number = $r->set('num',1);
$numNow = $r->incr('num');
$numNow = $r->incrby('num',10);
// echo $numNow;
//事务失败后不能回滚,造成部分执行完毕,事务只能保证操作的原子性,但不能保证数据原子性,因此采用乐观锁,watch/unwatch
$r->watch('num');
$r->multi()->incrby('num',100)->exec(); //or $r->multi()->incrby('num',100)->discard();
$num = $r->get('num');
// echo $num;
// $r->flushDB();
// $r->flushAll();
$keyss = $r->randomKey();
echo $keyss;
$r->select(0);
$r->set('x',42);
$r->move('x',1);
$r->select(1);
$val = $r->get('x');
// echo $val;
$r->set('name1','zy');
$r->set('name2','zy');
$r->set('name3','zy');
$r->set('name4','zy');
$r->set('name','zy');
$r->rename('name','myname');
$r->get('myname');
$r->renameNx('myname','myname2');
$r->setTimeout('x',30);
$time = $r->ttl('x');
// echo $time;
$r->expireAt('x',time()+301);
$time = $r->ttl('x');
//echo $time;
$key = $r->keys('*');
// print_r($key);
$db = $r->dbSize();
// var_dump($db);
echo '<pre>';
$r->bgrewriteaof();
// $r->slaveof('127.0.0.1',6380);
$r->save();;
$r->bgsave();
$unix = $r->lastSave();
$date = date('Y-m-d H:i:s',$unix);
// echo $date;
// print_r($operate);
$info = $r->info());
var_dump($r->type('myname'));
//string
$r->set('string','str');
$len = $r->strlen('string');
$r->set('insert','insertVals');
$r->append('string','insert');
// $val = $r->get('string');
// echo $val;
$r->set('abc','def');
$vals = $r->getSet('string','abc');
echo $vals;
$change = $r->get('string');
echo $change;
//hash
$r->hmset('student',array('wangming'=>80,'xiaohong'=>85,'ligang'=>90));
$r->hdel('student','ligang');
$vals = $r->hmget('student',array('xiaohong','wangming'));
var_dump($vals);
$val = $r->hget('student','xiaohong');
echo $val;
$exists = $r->hexists('student','wangming');
var_dump($exists);
$len = $r->hlen('student');
echo $len;
$r->hincrBy('student','xiaohong',10);
$score=$r->hget('student','xiaohong');
var_dump($score);
$keys = $r->hkeys('student');
$vals = $r->hvals('student');
$all = $r->hgetall('student');
echo '<pre>';
var_dump($keys);
var_dump($vals);
var_dump($all);
//zset
$r->zadd('set',0,'123');
$r->zadd('set',1,'456');
$r->zadd('set',2,'789');
$r->zadd('set',3,'1231');
$r->zrem('set','789');
$r->zadd('set',42,'1232');
$count = $r->zcount('set',0,42);
$size = $r->zsize('set');
$r->zincrby('set',2000,'123');
$rangeScore = $r->zremrangeByScore('set',0,1);
$scores = $r->zscore('set','123');
var_dump($scores);
$rank = $r->zrank('set','789');
var_dump($rank);
echo $size;
var_dump($count);
echo '<pre>';
$range = $r->zRevrange('set',0,-1);
var_dump($range);