缓存
Redis--分库快速写 与 Memcached--搜索纯读取
--use
网络(单线程|多线程)
--curd
内存(预分配--内存占用 | 实时申请--慢一点)
有效性(LRU--惰性失效 | expire--惰性+随机+暴力)
数据一致性(memcache cas | redis 事务-Multi/Watch/Exec)
集群-分布式(consistent_hash | Twemproxy)
--save
数据备份(master-slave)
持久化(snapshot快照1/2和AOF-I/O)
性能(被动访问和内存不够用 |pipeline script )
小结:内存+主从+读写
1. 要进行Master-slave配置,出现服务故障时可以支持切换,只在slave上配置数据持久化。
2. 当Redis物理内存使用超过内存总容量的3/5时就会开始比较危险了,就开始做swap,内存碎片大,物理内存+虚拟内存不足,这个时候dump一直死着,时间久了机器挂掉.当达到最大内存时,会清空带有过期时间的key,即使key未到过期时间.
3. redis与DB同步写的问题,先写DB,后写redis,因为写内存基本上没有问题
cache(kv ex):
* key - user:userid:9:email(表名:主键名:主键值:列名) lisi@163.com
* val - 返回:个数|是否修改|存在性|类型|秒数 -1(已过期) -2(不存在);空字符串|旧值|头尾元素或中间元素|返回随机值
* ex - mem指定 | redis可指定可不指定 ttl
redis:(单线程 申请内存 | 主从 持久化 多数据类型 事务 | 过期策略(惰性 随机 暴力))
持久化剖析:全量bgsave 写 磁盘,增量aof 刷 磁盘;aof支持修复;持久化的fork导致负载大==>主从(从备份和从切换)
mem(分布式consistent 缓存故障(雪崩-ex 无底洞-分布式 数据踢除-lru 命中-slab))
cache 命中:
memcached中新的value过来存放的地址是该value的大小决定的,value总是会被选择存放到chunk与其最接近的一个slab中,如果我的value是80b,那么我这所有的value总是会被存放到1号slab中,那么memcached会把这个slab中最近最少被使用的chunk中的数据清掉,然后放上最新的数据
问题:持久化+分布式
持久化, 复制和备份带来的系统和网络问题,无分布式方案导致软件设计的复杂度增加.
Redis 不适合作为海量数据存储方案. Redis 适合在数据规模较小, 性能要求较高的条件下应用
1.方法:
redis有全量(save/bgsave)和增量(aof)的持久化命令。 全量的原理就是遍历里所有的DB并写入dump.rdb文件。增量备份就是aof,每次执行命令后如出现数据变化把数据写到server.aofbuffer里,然后依照设置刷到磁盘上
2.区别:
快照db文件,优点是二进制,大小比aof日志文件小。但会丢失最后一次成功备份时间到down机时间的数据。
aof相比而言文件大小就大了点,但相对快照来讲,不大容易丢失文件。
目前redis检查数据文件是否有错对于快照及aof都能够支持,但修复则只对aof文件有效。
3.问题:
redis如果每60s更改记录数过万条就会dump(磁盘没闲着),而且是通过fork子进程dump(内存占用翻倍,cpu打满)。解决方法,可考虑把dump时间改为30m 有一次写修改就dump一次,而且主不dump,启动时只是download数据,不然的话加载数据前无法提供服务,dump交给从
方式:
主从
备份:redis下同时存在aof和db备份文件,这时根据从redis是否支持aof来决定是否同步aof文件
slave->sync->master->bgsave->db
db->slave->ack
slave->(bgsave->ack期间新请求)->send->slave
切换:
slave slave of no one ->check or fix aof(下面2条) ->slave of master
./redis-check-aof /home/jbossas/Data/redis/redis-data/appendonly.aof
./redis-check-aof --fix /home/jbossas/Data/redis/redis-data/appendonly.aof
redis优化:当它们的元素数量及value大小小于某个限值时,会用(数组)来减少内存存储
redis zadd_push or multi
<?php
error_reporting(E_ALL);
ini_set('display_errors',1);
//1.zadd 有序集合实现队列
class RedisClient
{
const POSITION_FIRST = 0;
const POSITION_LAST = -1;
public $redis;
public function __construct(){
$this->redis = new redis();
$this->redis->connect('127.0.0.1', 6379);
}
public function testData()
{
$this->redis->zadd('msg', '0', 'lunch');
$this->redis->zadd('msg', '1', 'sleep');
$this->redis->zadd('msg', '2', 'run');
$this->redis->zadd('msg', '3', 'swim');
$this->redis->zadd('msg', '4', 'cinema');
}
public function zPop($zset)
{
return $this->zsetPop($zset, self::POSITION_FIRST);
}
public function zRevPop($zset)
{
return $this->zsetPop($zset, self::POSITION_LAST);
}
private function zsetPop($zset, $position)
{
$this->redis->watch($zset);
$element = $this->redis->zRange($zset, $position, $position);
if (!isset($element[0])) {
return false;
}
if ($this->redis->multi()->zRem($zset, $element[0])->exec()) {
return $element[0];
}
return $this->zsetPop($zset, $position);
}
//exmaple not use
public function mzadd($key, $scores, $vals){
$score_v = '';
foreach ($scores as $k => $v) {
$score_v .= ', '.$v.' , '.$vals[$k];
}
$score_v = substr($score_v, 1);
$lua = "return redis.call('zadd', '{$key}', {$score_v})";
if(!empty($score_v)){
$ret = $this->redis->eval($lua);
}
return $ret;
}
}
//list
$redis = new RedisClient;
echo '<pre>';
$redis->testData();
while ($msg = $redis->zRevPop('msg')) {
print_R($msg);echo PHP_EOL;
}
$redis->testData();
while ($msg = $redis->zPop('msg')) {
print_R($msg);echo PHP_EOL;
}
//2.multi enhance profile
for ($i = 1; $i <= 10; $i++) {
$redis->redis->multi(Redis::PIPELINE);
for ($j = 1; $j <= 10; $j++) {
$msgid = ($i - 1) * 10000 + $j;
$userid = rand(0,100);
$redis->redis->sAdd('usr:msg', $msgid);
}
$redis->redis->exec();
}
$re = $redis->redis->smembers('usr:msg');
print_r($re);
//2.1 multi zadd
$vals = array(100=>'lunch2',101=>'sleep3',201=>'run4',301=>'swim5',401=>'cinema6');
$redis->redis->multi(Redis::PIPELINE);
foreach($vals as $score=>$val){
$lua = "return redis.call('zadd', 'mynewmsg', {$score},'{$val}')";
$ret = $redis->redis->eval($lua);
}
$redis->redis->exec();
$msg = $redis->redis->zrange('mynewmsg',0,100);
echo '<pre>';
var_dump($msg);
?>
一、Redis
数据类型:
string 静态数据:存取
hash 属性数据:关系
set:去重数据:集合
list:列表数据:头尾
sort set 排序数据:先后
//存取 时效 加减1 键名操作 事务 持久化
//get set setex setex delete mset mget getMultiple=>multi flushdb flushall (ex nx m+)
//ttl persist setTimeout=>expire expireAt| (+4)
//incr incrby decr decrby (number)
//keys randomKey type dbsize rename renameNx move exists (key use)
//curd +eists contain len
//string:getSet get/setRange append strlen (get set)
//hash h m/set m/get del keys vals getall len exists incrby (field 0=>1 2=>3 4=>5 )
//list:l r push/x pop set get | l range/trim/rem/insert lsize |rpoplpush (in out range(save del get insert) lists)
+
//set s add rem move pop sort inter/store union/store diff-前面的差集/store randMember members contains=>sismember size=>scared (inter union diff | add rem pop | member(contain size rand))
//zset z add rem |rev/range rem/rangebyscore score rank |count incrby size |union/inter(add and multiple)
(key score meber | score member | score member … ... )
zrank zrevrank==>排序的位数不同
zrank rank go
:4
zrevrank rank go
:2
zrange zrevrange =>展现多顺序不同
zrange rank 0 10
*7
$5
lunch
$5
sleep
$6
runner
$4
swim
$2
go
$4
jump
$6
paipai
zrevrange rank 0 10
*7
$6
paipai
$4
jump
$2
go
$4
swim
$6
runner
$5
sleep
$5
lunch
zrangebyscore zrevrangebyscore=>排序的大小不同
zrangebyscore rank 0 10
*7
$5
lunch
$5
sleep
$6
runner
$4
swim
$2
go
$4
jump
$6
paipai
zrevrangebyscore rank 10 0
*7
$6
paipai
$4
jump
$2
go
$4
swim
$6
runner
$5
sleep
$5
lunch
//multi(顺序处理) exec(执行) discard(丢弃) watch(乐观锁 不许改动) unwatch pipeline(批处理)
//save bgsave lastsave bgrewriteaof slaveof info auth
//ttl -2 是过期了,-1是持久有效
//sort
sort key (desc) limit 0 5
sort by get store
$r->setex('keys',3,'vals');
$r->getMultiple(array('key1','key2','key3','key4','key5'));
$r->setTimeout('x',30);
$r->linsert('list',Redis::BEFORE,'key1','def');
$r->sort('int',array('sort'=>'desc'));
$r->zscore('set','123');
//事务失败后不能回滚,造成部分执行完毕,事务只能保证操作的原子性,但不能保证数据原子性,因此采用乐观锁,watch/unwatch
$r->watch('num');
$r->multi();
$r->incrby('num',100);
$r->exec(); //or discard();
$num = $r->get('num');
// echo $num;
$r = new Redis;
$r->connect('127.0.0.1',6379);
$r->auth('password');
$r->ping();
$r->set('key','val');
$r->get('key');
$r->setnx('key','val');
$r->ttl('keys');
$r->delete('key');
$r->persist('keys');
$r->mset(array('key1'=>'val1','key2'=>'val2','key3'=>'val3','key4'=>'val4','key5'=>'val5'));
$r->exists('key2');
$r->incr('num');
$r->incrby('num',10);
$r->flushDB();
$r->flushAll();
$r->randomKey();
$r->select(0);
$r->move('x',1);
$r->rename('name','myname');
$r->renameNx('myname','myname2');
$r->expireAt('x',time());
$r->keys('*');
$r->dbSize();
$r->bgrewriteaof();
$r->slaveof('127.0.0.1',6380);
$r->save();
$r->bgsave();
$r->lastSave();
$r->info();
$r->type('myname'));
//string
$r->set('string','str');
$r->strlen('string');
$r->append('string','insert');
$r->get('string');
$r->getSet('string','abc');
//hash
$r->hmset('student',array('wangming'=>80,'xiaohong'=>85,'ligang'=>90));
$r->hdel('student','ligang');
$r->hmget('student',array('xiaohong','wangming'));
$r->hget('student','xiaohong');
$r->hexists('student','wangming');
$r->hlen('student');
$r->hincrBy('student','xiaohong',10);
$r->hget('student','xiaohong');
$r->hkeys('student');
$r->hvals('student');
$r->hgetall('student');
//list
$r->lpush('list','key1');
$r->linsert('list',Redis::BEFORE,'key1','def');
$r->lrange('list',0,-1);
$r->ltrim('list',2,10);
$r->lsize('list');
$r->lget('list',0);
$r->rpoplpush('list','list2');
//set
$r->sadd('set',123);
$r->srem('set',789);
$r->smove('set2','set','789');
$r->smembers('set');
$r->spop('set2');
$r->ssize('set');
$r->scontains('set','456');
$r->srandMember('set');
$r->sort('int',array('sort'=>'desc'));
$r->sinter('int','int2');
$r->sunion('int','int2');
$r->sdiff('int2','int');
$r->sdiffstore('diffstore','int2','int');
$r->smembers('diffstore');
//zset
$r->zadd('set',0,'123');
$r->zcount('set',0,42);
$r->zsize('set');
$r->zincrby('set',2000,'123');
$r->zremrangeByScore('set',0,1);
$r->zscore('set','123');
$r->zrank('set','789');
$r->zRevrange('set',0,-1);
二、Memcached +pipe+bykey+cas | Memcache connect(8个)
// append prepend getDelayed fetch fetchall setMulti getMulti addServers getServerByKey setByKey cas--写锁的set setOptions
//set = add replace
$m = new Memcached();
$m->addServer('127.0.0.1',11211);
$m->set('foo','bar');
$m->setOption(Memcached::OPT_COMPRESSION,false);
$m->append('foo','def');
$m->prepend('foo','000');
$m->get('foo');
$m->getServerList();
$m->setByKey('127.0.0.1','orderKey',1);
$server = $m->getServerByKey($key);
$m->fetchAll();
$m->getDelayed(array('int','string'),true);
while ($re = $m->fetch()) {
}
$m->setMulti(array('key1'=>'val1','key2'=>'val2','key3'=>'val3'),true);
$m->getMulti(array('key1','key2','key3'),$cas);
$m->addServers(array(
array('127.0.0.1',11211),
array('127.0.0.1',11212)
));
do {
$ips = $m->get('ip_block',null,$cas);//加上$cas后面才能用
if ($m->getResultCode()==Memcached::RES_NOTFOUND) {
$ips = $_SERVER['REMOTE_ADDR'];
$m->add('ip_block',$ips);
}else{
$m->cas($cas,'ip_block',$ips);//加了写锁的set
}
} while ($m->getResultCode()!=Memcached::RES_SUCCESS);
var_dump($ips);
三、Memcache
set(key,val,compress,timeout) get delete flush flush_all close add replace
new connect
increment decrement
addServer(host,port,persist,weight,timeout,retry_intval,status,failecallback)
./memcached -m 256 -c 1000 -P -l 127.0.0.1 -p 11211 -u root -d /tmp/memcached.pid
getVersion getStats hit/cmd.. getExtendedStats getStatus