使用过Redis,那就先说说使用过那些场景吧
字符串缓存
//举例
$redis->set();
$redis->get();
$redis->hset();
$redis->hget();
队列
//举例
$redis->rpush();
$redis->lpop();
$redis->lrange();
发布订阅
//举例
$redis->publish();
$redis->subscribe();
计数器
//举例
$redis->set();
$redis->incr();
排行榜
//举例
$redis->zadd();
$redis->zrevrange();
$redis->zrange();
集合间操作
//举例
$redis->sadd();
$redis->spop();
$redis->sinter();
$redis->sunion();
$redis->sdiff();
悲观锁
解释:悲观锁(Pessimistic Lock), 顾名思义,就是很悲观。
每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁。
场景:如果项目中使用了缓存且对缓存设置了超时时间。
当并发量比较大的时候,如果没有锁机制,那么缓存过期的瞬间,
大量并发请求会穿透缓存直接查询数据库,造成雪崩效应。
/**
* 获取锁
* @param String $key 锁标识
* @param Int $expire 锁过期时间
* @return Boolean
*/
public function lock($key = '', $expire = 5) {
$is_lock = $this->_redis->setnx($key, time()+$expire);
//不能获取锁
if(!$is_lock){
//判断锁是否过期
$lock_time = $this->_redis->get($key);
//锁已过期,删除锁,重新获取
if (time() > $lock_time) {
unlock($key);
$is_lock = $this->_redis->setnx($key, time() + $expire);
}
}
return $is_lock? true : false;
}
/**
* 释放锁
* @param String $key 锁标识
* @return Boolean
*/
public function unlock($key = ''){
return $this->_redis->del($key);
}
// 定义锁标识
$key = 'test_lock';
// 获取锁
$is_lock = lock($key, 10);
if ($is_lock) {
echo 'get lock success<br>';
echo 'do sth..<br>';
sleep(5);
echo 'success<br>';
unlock($key);
} else { //获取锁失败
echo 'request too frequently<br>';
}
乐观锁
解释:乐观锁(Optimistic Lock), 顾名思义,就是很乐观。
每次去拿数据的时候都认为别人不会修改,所以不会上锁。
watch命令会监视给定的key,当exec时候如果监视的key从调用watch后发生过变化,则整个事务会失败。
也可以调用watch多次监视多个key。这样就可以对指定的key加乐观锁了。
注意watch的key是对整个连接有效的,事务也一样。
如果连接断开,监视和事务都会被自动清除。
当然了exec,discard,unwatch命令都会清除连接中的所有监视。
$strKey = 'test_age';
$redis->set($strKey,10);
$age = $redis->get($strKey);
echo "---- Current Age:{$age} ---- <br/><br/>";
$redis->watch($strKey);
// 开启事务
$redis->multi();
//在这个时候新开了一个新会话执行
$redis->set($strKey,30); //新会话
echo "---- Current Age:{$age} ---- <br/><br/>"; //30
$redis->set($strKey,20);
$redis->exec();
$age = $redis->get($strKey);
echo "---- Current Age:{$age} ---- <br/><br/>"; //30
//当exec时候如果监视的key从调用watch后发生过变化,则整个事务会失败
上面的一些场景,咱们大部分都使用过,却还没有提及到Rdb文件。
“对吧,使用过Redis,却不知道Rdb文件,你中枪了吗?”
Rdb文件是什么,它是干什么的
Redis 作为互联网产品开发中不可缺少的常备武器,它性能高、数据结构丰富、简单易用,但同时也是因为太容易用了,不管什么数据、不管这数据有多大、不管数据有多少,通通塞进去,最后导致的问题就是 Redis 内存使用持续上升,但是又不知道里面的数据是不是有用,是否可以拆分和清理,最可怕的是服务器发生宕机后,Redis 数据库里的所有数据将会全部丢失。
比如当内存上升,性能慢时,我们进行性能调优的时候,我们想知道:
- 哪些Key占用了大量的内存?
- 想知道每个Key的占用空间?
- 想知道占用空间大的Key都存了啥?
- 想知道占用空间大的Key的重要性,当性能慢的时候是否可以马上删除?
- 更想知道这些Key是哪个业务方,哪个开发创建的?这样就可以找他沟通了。
尝试解决问题的思路
一、先通过 keys * 命令,拿到所有的 key,然后根据 key 再获取所有的内容。
- 优点:可以不使用 Redis 机器的硬盘,直接网络传输。
- 缺点:如果 key 数据特别多,keys 命令可能会直接导致 Redis 卡住,从而影响业务使用 或 对 Redis 请求太多次,资源消耗多,遍历数据太慢。
二、开启 aof,通过 aof 文件获取所有的数据。
- 优点:无需影响 Redis 服务,完全离线操作,足够安全。
- 缺点:有一些 Redis 实例写入频繁,不适合开启 aof,普适性不强;aof 文件有可能特别大,传输、解析起来太慢,效率低。
三、使用 bgsave,获取 rdb 文件,解析后获取数据。
- 优点:机制成熟,可靠性好;文件相对小,传输、解析效率高。
- 缺点:bgsave 虽然会 fork 子进程,但还是有可能导致主进程卡住一段时间,对业务有产生影响的风险。
综合评估后,决定采用低峰期在从节点做 bgsave 获取 rdb