redis总结整理
一直在使用redis,使用redis的一些简单功能,
例如key-value的存储一些数据,使用list记录用户的浏览记录的前100条数据,例如记录书籍的浏览次数
可以一直对redis没有全面的了解,希望通过这篇博客,我能够对redis做一个全面的总结,以后遇到redis相关的问题,能够找到问题的解决方向。
redis的介绍
Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、 Key-Value 数据库,并提供多种语言的 API。
redis的类型
作为 Key-value 型数据库,Redis 也提供了键(Key)和键值(Value)的映射关系。但是,除 了常规的数值或字符串,Redis 的键值还可以是以下形式之一:
string
Lists (列表)
Sets (集合)
Sorted sets (有序集合)
Hashes (哈希表)
redis的 连接
$redis = new Redis();
$host = "127.0.0.1";
$port = 6379;
$timeout = 5;
$redis->connect($host,$port,$timeout);
redis string的使用
原子操作,是每次操作都会使用一个锁(信号量)之类的工具,其他的带操作都加入待处理列表
//简单的字符串操作
$key = "abc";
$val = "abcdefeag";
$key1 = "efg";
$val1 = "dddsdfasdfasdf";
//设置字符串
$redis->set($key,$val);
//设置字符串,如果key不存在
$redis->setnx($key,$val."abc123");
//$redis->set($key,$val."abc123");
//设置有效期3600秒
$redis->setex($key,3600,"axxxxooxoxoxxo");
//字符串的替换
$redis->setRange($key,12,"dddddd");
//一次给多个key赋值
$redis->mset(array($key=>$val,$key1=>$val1));
//设置新的值获取旧的值
var_dump($redis->getSet($key,"axxxxooxoxoxxo"));
//片段获取
var_dump($redis->getRange($key,0,5));
//追加
$redis->append($key,$val1);
var_dump($redis->get($key),$redis->get($key1));
//字符串的
$redis->set("view_book_1",100);
//原子增加
$redis->incr("view_book_1");var_dump($redis->get("view_book_1"));
$redis->incrBy("view_book_1",100);var_dump($redis->get("view_book_1"));
//原子减少
$redis->decr("view_book_1");var_dump($redis->get("view_book_1"));
$redis->decrBy("view_book_1",100);var_dump($redis->get("view_book_1"));
//字符串长度
var_dump($redis->strlen($key));
//删除
$redis->del($key);
redis hash的使用
hash可以用于存储一系列的数据,比如一本书的所有信息,一条微博的所有信息,
//hash串的基本操作
$key = "myhash";
//hash的设置
$redis->hSet($key,"id",12);
//如果不存在才设置
$redis->hSetNx($key,"id",13);
//批量设置
$redis->hMset($key,array("title"=>"入梦师","author"=>"八重樱妖"));
$redis->hSet($key,"views",12);
//自增
$redis->hIncrBy($key,"views",1);
//判断key是否存在
var_dump($redis->hExists($key,"title"));
//keys 的长度
var_dump($redis->hLen($key));
//删除对应的字段
var_dump($redis->hDel($key,"views"));
//获取键列表,获取值列表
var_dump($redis->hKeys($key),$redis->hVals($key));
//批量获取数据
var_dump($redis->hMget($key,array("title","author")));
//获取单个数据
var_dump($redis->hGet($key,"id"));
//获取所有数据
var_dump($redis->hGetAll($key));
redis list的使用
咱们可以理解成数据结构里面的链表
$kl ="mylist";
//左(右)边入队列,左(右)边入队列
$redis->rPush($kl,"hello");
//$redis->rPop($kl);
$redis->lPush($kl,"php");
//$redis->lPop($kl);
$redis->rPushx($kl,"hello");
$redis->rPop($kl);
//设置0上的数据
$redis->lSet($kl,0,"c++");
//获取0上的数据
var_dump($redis->lGet($kl,0));
//删除数据
$redis->lrem($kl,"hello",1);
//截取数据
$redis->ltrim($kl,1,2);
var_dump($redis->lSize($kl),$redis->lrange($kl,0,-1));
$redis->del($kl);
redis set的使用
//set 不重复的集合
$sk = "myset";
//添加
$redis->sAdd($sk,12);
$redis->sAdd($sk,12);
$redis->sAdd($sk,13);
$redis->sAdd($sk,14);
//删除
$redis->sRemove($sk,13);
//
$sk2 = "myset2";
$redis->sAdd($sk2,13);
$redis->sAdd($sk2,14);
$redis->sAdd($sk2,15);
//随机返回一个元素
var_dump($redis->sRandMember($sk2));
//是否是里面的数据
var_dump("contain#",$redis->sContains($sk,12));
//排序获取数据
var_dump($redis->sSize($sk),$redis->sort($sk,array(
'sort'=>'desc',
)));
//交集
var_dump($redis->sInter($sk,$sk2));
//并集
var_dump($redis->sUnion($sk,$sk2));
//差集
var_dump($redis->sDiff($sk,$sk2));
var_dump($redis->sDiff($sk2,$sk));
$redis->del($sk);
$redis->del($sk2);
redis sorted set的使用
//zset
$key = "zs";
//添加元素,分数不能相同
$redis->zAdd($key,75,"php");
$redis->zAdd($key,85,"java");
$redis->zAdd($key,75,"c++");
$redis->zAdd($key,80,"c#");
$redis->zDelete($key,"c#");
//显示元素
var_dump($redis->zRange($key,0,-1));
var_dump($redis->zRange($key,0,-1,true));
//zrangebyscore 可以用来分页
var_dump($redis->zrangebyscore($key,1,100,array('limit' => array(1, 1),'withscores' => TRUE)));
//数据的数量
var_dump($redis->zCount($key,1,100));
//返回分数
var_dump($redis->zScore($key,"php"));
//加分
$redis->zIncrBy($key, 3, 'php');
var_dump($redis->zRange($key,0,-1,true));
//返回排名值
var_dump($redis->zRank($key,"php"),$redis->zRank($key,"c++"));
$key1 = "zs2";
$redis->zAdd($key1,75,"php");
$redis->zAdd($key1,85,"java1");
$redis->zAdd($key1,75,"c++1");
$redis->zAdd($key1,80,"c#1");
$redis->zDelete($key1,"c#1");
//交集,分数相加
var_dump("##",$redis->zInter("keynew",array($key,$key1)));
var_dump($redis->zRange("keynew",0,-1,true));
//并集
var_dump("##",$redis->zUnion("keynew2",array($key,$key1)));
var_dump($redis->zRange("keynew2",0,-1,true));
$redis->del($key);
$redis->del($key1);
$redis->del("keynew2");
$redis->close();
redis 使用场景
一切可以使用string,list,set ,sorted sets hash的地方都可以用到redis,关键是你得取舍,你的取舍就是了解redis的优缺点,你业务的特性啦
redis的数据持久化
所实话我没有看过redis源码,以下内容来自网络。如果大家向我一样,c语言不好,可以看看《redis的设计和实现》这本书
通常,Redis 将数据存储于内存中,或被配置为使用虚拟内存。通过两种方式可以实现数据 持久化:
使用截图的方式,将内存中的数据不断写入磁盘;
或使用类似 MySQL 的日志方式, 记录每次更新的日志。
前者性能较高,但是可能会引起一定程度的数据丢失;后者相反。
redis的数据持久化有两种方式:
一种是 Snapshotting(快照)也是默认方式
一种是 Append-only file(缩写 aof)的方式。下面分别介绍:
redis快照的处理过程
快照是默认的持久化方式。这种方式是就是将内存中数据以快照的方式写入到二进制文件中, 默认的文件名为 dump.rdb。可以通过配置设置自动做快照持久化的方式。我们可以配置 redis 在 n 秒内如果超过 m 个 key 被修改就自动做快照,下面是默认的快照保存配置
save 900 1 #900 秒内如果超过 1 个 key 被修改,则发起快照保存
快照保存过程
1.redis 调用 fork,现在有了子进程和父进程。
2. 父进程继续处理 client 请求,子进程负责将内存内容写入到临时文件。由于 os 的实时复 制机制(copy on write)父子进程会共享相同的物理页面,当父进程处理写请求时 os 会为父 进程要修改的页面创建副本,而不是写共享的页面。所以子进程地址空间内的数据是 fork时刻整个数据库的一个快照。
3.当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出
client 也可以使用 save 或者 bgsave 命令通知 redis 做一次快照持久化。save 操作是在主线程 中保存快照的,由于 redis 是用一个主线程来处理所有 client 的请求,这种方式会阻塞所有 client 请求。所以不推荐使用。另一点需要注意的是,每次快照持久化都是将内存数据完整 写入到磁盘一次,并不是增量的只同步变更数据。如果数据量大的话,而且写操作比较多, 必然会引起大量的磁盘 io 操作,可能会严重影响性能。
aof 方式
另外由于快照方式是在一定间隔时间做一次的,所以如果 redis 意外 down 掉的话,就会丢 失最后一次快照后的所有修改。如果应用要求不能丢失任何修改的话,可以采用 aof 持久化 方式。
aof 比快照方式有更好的持久化性,是由于在使用 aof 持久化方式时,redis 会将每一个收到 的写命令都通过 write 函数追加到文件中(默认是 appendonly.aof)。当 redis 重启时会通过重 新执行文件中保存的写命令来在内存中重建整个数据库的内容。当然由于 os 会在内核中缓 存 write 做的修改,所以可能不是立即写到磁盘上。这样 aof 方式的持久化也还是有可能会 丢失部分修改。不过我们可以通过配置文件告诉 redis 我们想要通过 fsync 函数强制 os 写入 到磁盘的时机。有三种方式如下(默认是:每秒 fsync 一次)
appendonly yes //启用 aof 持久化方式
# appendfsync always //收到写命令就立即写入磁盘,最慢,但是保证完全的持久化
#appendfsync everysec //每秒钟写入磁盘一次,在性能和持久化方面做了很好的折中
# appendfsync no //完全依赖 os,性能最好,持久化没保证
aof可能带来的问题
aof 的方式也同时带来了另一个问题。持久化文件会变的越来越大
bgrewriteaof 处理大文件问题
为了压缩 aof 的持久化文件。redis 提 供了 bgrewriteaof 命令。收到此命令 redis 将使用与快照类似的方式将内存 中的数据以命令 的方式保存到临时文件中,最后替换原来的文件。具体过程如下
1、redis 调用 fork ,现在有父子两个进程
2、子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令
3、父进程继续处理 client 请求,除了把写命令写入到原来的 aof 文件中。同时把收到的写命 令缓存起来。这样就能保证如果子进程重写失败的话并不会出问题。
4、当子进程把快照内容写入已命令方式写到临时文件中后,子进程发信号通知父进程。然 后父进程把缓存的写命令也写入到临时文件。
5、现在父进程可以使用临时文件替换老的 aof 文件,并重命名,后面收到的写命令也开始 往新的 aof 文件中追加。
redis的安全
redis可以设置成需要auth才能进行操作
redis的主从同步
配置好 slave 后,slave 与 master 建立连接,然后发送 sync 命令。无论是第一次连接还是重新连接,master 都会启动一个后台进程,将数据库快照保存到文件中,同时 master 主进 程会开始收集新的写命令并缓存。后台进程完成写文件后,master 就发送文件给 slave,slave 将文件保存到硬盘上,再加载到内存中,接着 master 就会把缓存的命令转发给 slave,后续 master 将收到的写命令发送给 slave。如果 master 同时收到多个 slave 发来的同步连接命令, master 只会启动一个进程来写数据库镜像,然后发送给所有的 slave。
redis支持简单的事务
如题
消息的订阅和发布
发布订阅(pub/sub)是一种消息通信模式,主要的目的是解耦消息发布者和消息订阅者之间的 耦合,这点和设计模式中的观察者模式比较相似。pub/sub 不仅仅解决发布者和订阅者直接 代码级别耦合也解决两者在物理部署上的耦合。redis 作为一个 pub/sub 的 server,在订阅者 和发布者之间起到了消息路由的功能。订阅者可以通过 subscribe 和 psubscribe 命令向 redis server 订阅自己感兴趣的消息类型,redis 将消息类型称为通道(channel)。当发布者通过 publish 命令向 redis server 发送特定类型的消息时。订阅该消息类型的全部 client 都会收到 此消息。这里消息的传递是多对多的。一个 client 可以订阅多个 channel,也可以向多个 channel 发送消息。
redis相当于路由器,起到了消息中转的作用
参考了红丸的redis pdf文件