reids使用场景:
1.一般的数据缓存
2.队列应用:非实时业务如发放积分或需要削峰降流的秒杀等场景都会用到队列
3.发布订阅
4.计数器
5.排行榜:微博的热榜就是很好的例子
6.资源锁:这个秒杀的时候往往也会用到,防止超卖等现象发生,当然还有很多其他防并发的用途。
redis的五种常用数据类型:string,hash,set,zset.list
redis淘汰策略:
noevication:不删除策略,达到maxmemory,直接返回错信息
allkeys-lru: 优先删除最近最少使用的key
volatile-lru: 优先删除设置过expire的最近最少使用的key.
allkeys-random: 随机删除部分key
volatile-random:随机删除设置过过期时间的key
volitile-ttl : 删除设置过期时间中的剩余时间最少的key
一般来说:
如果分为热数据与冷数据, 推荐使用 allkeys-lru 策略。 也就是, 其中一部分key经常被读写. 如果不确定具体的业务特征, 那么 allkeys-lru 是一个很好的选择。
如果需要循环读写所有的key, 或者各个key的访问频率差不多, 可以使用 allkeys-random 策略, 即读写所有元素的概率差不多。
假如要让 Redis 根据 TTL 来筛选需要删除的key, 请使用 volatile-ttl 策略。
Redis为了保证高可用性,采用Master-slave形式部署,采用AOF或RDB进行持久化,采用集群culster机制来分布式存储。
redis哨兵:
很显然,只使用单个sentinel进程来监控redis集群是不可靠的,当sentinel进程宕掉后(sentinel本身也有单点问题,single-point-of-failure)整个集群系统将无法按照预期的方式运行。所以有必要将sentinel集群,这样有几个好处:
即使有一些sentinel进程宕掉了,依然可以进行 集群的主备切换;
如果只有一个sentinel进程,如果这个进程运行出错,或者是网络堵塞,那么将无法实现redis集群的主备切换(单点问题);
如果有多个sentinel,redis的客户端可以随意地连接任意一个sentinel来获得关于redis集群中的信息
对象序列化:serialize 方便存储redis(strig)
1.hash:
是一个string类型的field 和 value的映射表,适合存储对象;
常用命令:hget,hset,hgetall 等;
hset myhash name cyh
hset user:001 name cyh
hget user:001 name --返回cyh 把user:001当作一个表 name为字段名 cyh为字段值
hsetnx user:002 name ss --如果存在则返回0不保存进去
hmset user:003 name ss age 20 sex women -- 批量设置
hmget user:003 name age sex -- 批量返回(ss ,0 ,women)
hget user:003 age --20
hincrby user:003 age 5 --自增5
hexists user:003 age --判断hash表里面的字段是否存在的方法
hlen user:003 --返回字段的数量
hdel user:003 age --删除age字段的值
hkeys user:003 --返回所以字段名(array_keys)
hvals user:003 --返回所有的值(array_vales)
hgetall user:003 --返回所有键值
expire age 10 --给age设置过期时间十秒钟
ttl age --返回-1查看是否过过期
2.list(有序非去重集合)
常用命令:lpush,rpush(存数到队列),lpop(取数),rpop,lrange等。
使用场景:做消息队列系统
下面这个程序模拟了20w人一瞬间涌入这个页面进行秒杀,能够秒杀成功的只有500人,我们把先进来的用户放入redis队列中,当队列中的用户达到500时,后来用户就转到秒杀结束页面。这里用随机数来表示不同的用户。
$redis = new redis();
$redis->connect('localhost','6379');
$redis->select(1);
$redis_name = 'miaosha';
$num = 500; 抢购名额
$i = 20000; 抢购人数
$n = 0; 验证执行数量是否正确
while($i--){
$n++;
$uid = mt_rand(100000,99999)
if( $redis->llen($redis_name) < $num){
$redis->rpush($reids_name,$uid);
echo $uid. '秒杀成功';
}else{
echo $uid . "秒杀失败"
}
$redis->close();
}
3.string:最简单的数据类型(k-v)
set name cyh
get name
setnx name cyh (防止覆盖,如果存在这个key则返回0存不进去)
setex color 10 red 设置键值并设置有效值
setrange name 6 qq.com 替换字符串,从下标为6(下表从0开始)的开始替换,后面的参数为替换的内容
mset key1 val1 key2 val2 批量设置
getrange name 0 5 获取字符串(0到5)
incr id3 自增操作
decr 自减
incrby id 5 自增五个
decrby id 5
append name.ss 给name的value追加字符串
strlen name 求键的长度
4.set(去重无序集合)
常用命令:sadd,spop,smembers,sunion 等。
5.zest
使用场景:排行榜应用,取TOP N操作
redis事务特性:不具有事务的原子性(加入队列中的某个事务没有执行成功,但是整个事务不会全部回滚),会导致数据不具有一致性
$redis->muilti --开始事务上下文
ok
$redis->set age 10 --先将事务放入队列中
queued
$redis->set age 20
queued
$redis->exec --执行事务
1ok
2ok
$redis->get age
20
$redis->discard --取消事务(事务回滚:清空队列并退出事务上下文)
redis 乐观锁:类似版本控制器(更新后版本号加1,如果提交版本号大于当前版本号则更新当前版本号,否则则认为是过期数据)
watch age --监控age
$redis->set age 30
$redis->muilti
$redis->set age 20
queued
$redis->exec --事务将执行失败 、
(当watch监控age时,发现从监视起这个key发生过变化,则整个事务将失败,(打开watch就相当于对当前key增加了一个乐观锁)
也可以调用watch多次监视多个key,这样就可以对指定的key加乐观锁,watch的key是对整个事务都有效的,事务也一样
unwatch ,discard,exec都会清除连接中的所有监视
当前事务的age不是最新的age,属于过期数据不允许执行)
redis 持久化机制:
redis是一个支持持久化的内存数据库,也就是说redis需要经常将内存中的数据同步到硬盘来保证持久化。
redis持久化的方式:
1.快照模式(snapshoting 文件dump.rdb 二进制) 也是默认方式: 相当于把数据做一个备份(将数据存到文件里)
ans:
这种方式是将内存中的数据以快照的方式写到二进制文件中,默认的文件名为dump.rdb。我们可以配置redis在n秒内如果超过m个key被修改就自动做快照
save 300 10 #300秒内如果有10个key被修改过,则发起快照保存
2.append_only_file(aof 文件appendonly.aof 某种协议) 增量型持久化: 将写和更改的操作存到一个文件里面(insert.update)extend:所有被写入 AOF 的命令都是以 redis 的协议('RESP')格式来保存的。
ans:
由于快照方式在一定时间间隔内做一次的,如果redis意外宕机,就会丢失掉最后一次快照后的所有修改。
aof比快照有更好的持久化,由于在使用aof时,redis会将每一个收到的写命令都通过write函数追加到文件中。
当redis重启时,会重新执行文件中保存的写命令来在内存中重建整个数据库的内容。
appendonly yes --启用aof持久化
appendfsync always --收到写命令就立即写入磁盘,最慢,但是数据最完整
appendfsync everysec --每秒写入磁盘一次 性能折中
appendfsync no --完全依赖os,性能最好,持久化没保障
发布与订阅:
是一种消息通信模式,主要是解决消息订阅者和发布者之间的耦合性,redis作为一个pub/sub的server,在订阅者和发布者之间起到路由的功能
订阅者可以通过subscribe和psubscribe向redis service订阅自己感兴趣的消息类型,redis将信息类型称为通道,当发布者通过publish命令向
redis service发送特定的消息类型的信息时,订阅改信息的全部client都会收到此消息
可以做一个web聊天系统
subscribe tv1 tv2
cyh
publish tv1 cyh
虚拟内存:
把不经常使用的数据交换到磁盘上
常规使用:
$redis = new redis();
redis->connect();
-- 添加
$redis->hset()
--批量添加
$uid = $redis->incr("userid");
$redis->hMset('user:' . uid,array('uid'=>$uid,name'=>'cyh','age'=>'20'))
$redis->hMset('header:rows1',array('name'=>'cyh','age'=>'20'))
-- 批量获取
require("redis.php");
foreach($i = 1 ; $i <= ($redis->get("userid")) ; $i++){
$data[] = $redis->hgetall("user:" . $i);
}
-- 删除
$redis->del("user" . $uid)
-- 分页
总数
$count = $redis->lsize("uid");
页大小
$page_size = 10;
当前页码
$page_num = $GET_['page'];
页总数
$page_count = ceil($count/$page_size)
$ids = $redis->lrange('uid',($page_num-1)*$page_size,(($page_num-1)*$page_size+($page_size-11))
foreach($ids as $v){
$data[] = $redis->hgetall('user:'.$v);
}