redis

1. 性能测试工具(批量写入) ./bin/redis-benchmark -n 10000 一次性写入10000条数据

2.使用select 1可以切换到1号数据库(共有16个数据库)。


【Redis对于key的操作命令】


keys */?/[]:查询相应的key;完整匹配:keys site;模糊匹配:keys s*、keys sit[ey]

randomkey:随机返回key

type key:判断key的类型 string,set,zset,link,hash

exists key:判断key是否存在,返回1/0

del key1 key2 ... KeyN :删除1个或多个键

rename key newkey:给key赋一个新的key名,原值被覆盖

renamenx key newkey:同rename,不覆盖

move key 2:移动key到2号数据库

ttl/pttl key:查询key的剩余生命周期(秒/毫秒)

expire/pexpire key: 设置key的生命周期(秒/毫秒)

persist key: 设置key永久有效



【Redis字符串类型的操作】


set key value [ex]/[px]:多少[秒]/[毫秒]有效。

set key value [nx]/[xx]:设置一个[已存在]/[不存在]的key。

mset:一次性设置多个键值,mset key1 v1 key2 v2 ...。

get key: 获取key的值。

mget key1 key2 ... keyN: 获取多个key的值。

setrange key offset value:从offset个字符开始替换成value。

append key value:把value追加到key的原值上。

getrange key start stop :获取[start, stop]范围。

getset key newvalue :返回旧值并设置新值。

incr/decr key:值加/减1。

incrby/decrby key number:值加/减number。

incrbyfloat key floatnumber:值加浮点值(没有decrbyfloat)



【link 链表结构】


lpush/rpush key value: 把值插入到链接头部/尾部;

lpop/rpop key value: 从链接头部/尾部删除值;

lrange key start stop: 返回[start ,stop]的值,全部:0到-1.

lrem key count value: 从key中删除count个value值

ltrim key start stop: 剪切[start,stop]一段赋给key

lindex key index: 返回index索引上的值。

llen key:计算链接表的元素个数。

linsert key after/before s v: 寻找's'并在其值之前/之后插入v;

rpoplpush source bak: 把source的尾部剪切放在bak的头部;

brpop/blpop key timeout:等待弹出key的尾/头元素



【set无序集合(集合的性质: 唯一性,无序性,确定性)】


sadd key value1 value2: 往集合key中增加元素.

scard key : 返回元素个数。

smembers key: 返回所有元素。

srandmember key: 返回随机的1个元素。

sismember key v: 判断v是否存在key中,1/0。

srem key v1 v2:删除值为v1和v2的元素,返回真正删除掉的个数。

spop key:返回并删除集合key中1个随机元素。

smove key1 key2 value:把key1中的value值移动到key2中。

sinter k1 k2 k3: 求三者的交集然后返回。

sinterstore dest1 k1 k2 k3: 求交集然后赋给dest1。

sunion k1 k2 k3: 求并集然后返回。

sunionstore dest2 k1 k2 k3: 求并集然后赋给dest2。

sdiff k1 k2 k3: 求差集然后返回。

sdiffstore dest3 k1 k2 k3: 求差集然后赋给dest3。



【order set 有序集合】


zadd key score1 value1 score2 value2 .. :给有序集合添加元素。

zcard key: 返回元素个数。

zcount key min max:分数在[min,max]区间内元素的数量。

zrange/zrevrange key start stop [WITHSCORES] :集合升序/降序排列后返回名次[start,stop]的元素。全部:[0,-1]。

zrangebyscore/zrevrangebyscore key min max [withscores] [limit offset N]: 分数min和max按照分数升续/降序排序。

zrank/zrevrank key member:升序/降序后查询member的排名。

zrem key value1 value2... : 删除集合中的元素。

zremrangebyscore key min max :删除socre在[min,max]之间的。

zremrangebyrank key start end: 删除排名在[start,end]之间的。

zinterstore/zunionstore destination numkeys key1 [key2 ...]:取交集/并集。



【Hash 哈希数据类型】


hset key field value:把key中filed域的值设为value。

hget key field:返回key中field域的值。

hgetall key :返回key中所有域与其值。

hmset key field1 value1 [field2 value2...]:设置多个域和值。

hmget key field1 field2: 返回多个域的值。

hlen key:返回key中元素的数量。

hkeys key:返回key中所有的field。

hvals key:返回key中所有的value。

hexists key field:判断key中有没有field域。

hincrby/hincrbyfloat key field value:把key中的field域的值增长整型值/浮点值value。

hdel key field:删除key中field域。



【Redis的事务】


Mysql:开启[start transaction],语句[普通sql],失败[rollback回滚], 成功[commit].

Redis: 开启[muitl],语句[普通命令],失败[discard取消],成功[exec].

watch key1 key2 keyN:监听key1,key2,keyN有没有变化,如果有变则事务取消。

unwatch:取消所有watch监听。



【消息订阅】


订阅端: Subscribe 频道名称。发布端: publish 频道名称 发布内容。psubscribe 通配符匹配接收。



【运维常用的server端命令(不区分大小写)】


time 查看时间戳与微秒数,

dbsize 查看当前库中的key数量,

BgRewriteAof 后台进程重写AOF,

BgSave 后台保存rdb快照,

save 保存rdb快照,

LastSave 上次保存时间,

SlaveOf 设为slave服务器,

shutdown[""|save|nosave] 断开连接,关闭服务器,

SlowLog 显示慢查询,

config get 获取配置信息,

config set 设置配置信息,

monitor 打开控制台,

sync 主从同步,

client list 客户端列表,

client kill 关闭某个客户端,

client setname 为客户端设置名字,

client getname 获取客户端名字,

FlushAll 清空所有db,

FlushDB 清空当前db。

备注:不小心flushall,立即shutdown nosave,编辑aof文件,去掉flushall,然后导入。



【info:显示服务器全部信息】


Memory 内存

Persistence 持久化的信息

Status中的fork耗时

Replication 主从复制的信息






【开发中Redis的key的设计技巧】


MySQL的user表:id:9|name:lisi|pwd:123;

Redis存储:set user:id:9:name lisi|set user:id:9:pwd 123;

查询有哪些字段:keys user:id:9*;

查询某个字段的值:get user:id:9:name;

要按照name查id:set user:name:lisi:id 9;



【Redis实战】

MySQL的user表:


|userid:9|username:lisi|password:222|email:​​lisi@163.com​​|


转换为Redis存储:


set user:userid:9:username lisi

set user:userid:9:password 111111

set user:userid:9:email ​​lisi@163.com​


查询方法

(1)查询有哪些字段:keys user:userid:9* ,结果如下:

(2)查询某个字段的值:get user:userid:9:username ===> lisi

注意:冗余字段

按照username查询,需要生成一条按照username列为主的key-value:set user:username:lisi:uid 9

这样就可以根据username:lisi:uid ,查出userid=9。 再查user:9:password/email ...


【增删改查分页完整操作】

使用hash类型存储数据。hmset:批量添加数据,例如:


$redis->hmset(‘user:1’,array(‘name’=>“renxing”,‘age’=>“28”));

hgetall:获取所有数据;

del:删除数据。



$redis->incr("userid"); //设定一个自增的数值,类似于主键

$redis->hmset("user:".$uid,array $add_data); //批量添加

$incr_id = $redis->get("uid"); //获取当前自增的数值

for($i=1;$i<=$incr_id;$i++){

$data[] = $redis->hgetall("user:".$i); //使用hgetall获取数据

}

$res = $redis->del("user:".$uid); //删除数据


[分页操作思路分析] 将所有的uid存在链表结构list中,用rpush uid 1 存储,用lrange uid 0 -1 获取全部数据。在删除数据的时候,用lrem删除对应的id号。PHP中用lsize可以获取list的总数。假设每页显示3条,那么,第1页:lrange uid 0 2,第2页:lrange uid 3 5,第3页:lrange uid 6 8。


【PHP操作Redis】


$redis = new Redis();

$flag = $redis->connect('localhost',6379);

$redis->auth("renxing");//授权

$redis->set('user:id:2:name','lisi');

$value = $redis->get('user:id:2:name');

print_r($value);



【使用hash类型存储数据】


hmset:批量添加数据:$redis->hmset('user:1',array('name'=>'renxing','age'=>'28'));

hgetall:获取所有数据;

del:删除数据。



【执行添加/修改操作 hmset】


$uid = $redis->incr("id");//设定自增

$add_data = array(

"uid"=>$uid,

"username"=>'lisi',

"password"=>'123',

);

$res = $redis->hmset("user:".$uid,$add_data);

此时终端hgetall user:1,得到:uid 1 username lisi password 123。



【获取数据 hgetall】


$incr_id = $redis->get("uid");

for($i=1;$i<=$incr_id;$i++){

$data[] = $redis->hgetall("user:".$i);

}

print_r($data);



【删除操作】


$res = $redis->del("user:".$uid);



【分页】

用lsize获取list的总数,假设每页显示3条,

那么第1页:lrange uid 0 2,第2页:lrange uid 3 5,第3页:lrange uid 6 8。


$count = $redis->lsize("uid"); //用户总数

$page_size = 3;

$page_num = $_GET['page'];

$page_count = ceil($count/$page_size);

$page_up = ($page_num-1)<=1?1:($page_num-1);

$page_down = ($page_num+1)>=$page_count?$page_count:($page_num+1);

$a = ($page_num-1)*$page_size;

$b = $a+$page_size-1;

$ids = $redis->lrange("uid",$a,$b);

foreach($ids as $v){

$data[] = $redis->hgetall("user:".$v);

}



【登录操作】

存储username和uid的对应关系 $redis->set("username:".$username,$uid);

登录的时候,使用get 判断username是否存在 $id = $redis->get("username:".$username);

通过$id得到存储在数据库中的password,和用户输入的密码进行判断 $pwd = $redis->hget("user:".$id,"password”);

如果正确了,设置SESSION或者cookie。

Redis与Memcached的区别与比较

1 、Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。memcache只支持简单的数据类型,String。

2 、Redis支持数据的备份,即master-slave模式的数据备份。

3 、Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而Memecache把数据全部存在内存之中。

4、Memcached是多线程,非阻塞IO复用的网络模型;Redis使用单线程的IO复用模型。


使用Redis缓存查询数据

与Memcache的区别:性能相差不大

Redis在2.0版本后增加了自己的VM特性,突破物理内存的限制,Memcache可以修改最大可用内存,采用LRU算法。

Redis 依赖客户端来实现分布式读写,Memcache本身没有数据冗余机制。

Redis支持(快照、AOF),依赖快照进行持久化,aof增强了可靠性的同时,也对性能有所影响。

Memcache 不支持持久化,通常做缓存,提升性能。

Memcache在并发场景下,用cas保证一致性,redis事务支持比较弱,只能保证事务中的每个操作连续执行。

Redis支持多种类的数据类型,Redis 用于数据量较小的高性能操作和运算上;

Memcache用于在动态系统中减少数据库负载,提升性能;适合做缓存,提高性能。


1. 使用redis有哪些好处?

(1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)

(2) 支持丰富数据类型,支持string,list,set,sorted set,hash

(3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行

(4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除


3. redis常见性能问题和解决方案:

(1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件

(2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次

(3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内

(4) 尽量避免在压力很大的主库上增加从库

(5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3...

这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。


【redis的rdb和aof】


redis.conf关于rdb的配置:

save 900 1 (900秒之后至少1个关键字发生变化),

save 300 10 (300秒之后至少10个关键字发生变化),

save 60 10000 (60秒之后至少10000个关键字发生变化)。


rdb的缺陷:在2个保存点之间断电,将会丢失1-N分钟的数据。

出于对持久化的更精细要求,redis增添了aof方式 append only file。

把最终的结果逆化为新的变量,只存储最终的结果,忽略中间变化的过程,叫做:aof重写

bgrewriteaof 可以直接马上重写;使用aof后性能稍微会下降。


如果不小心运行了flushall, 立即 shutdown nosave ,关闭服务器。

然后手工编辑aof文件, 去掉文件中的 “flushall ”相关行, 然后开启服务器,就可以导入回原来数据。

如果flushall之后,系统恰好bgrewriteaof了,那么aof就清空了,数据丢失.


aof (append only file)的配置:


appendonly yes (打开aof日志功能),

appendfsync always (每1个命令都立即同步到aof.安全,速度慢,丢失数据少),

appendfsync everysec (折中方案,每秒写1次),

appendfsync no (由操作系统判断缓冲区大小,统一写入到aof.同步频率低,速度快。)


aof重写:只存储最终的结果,忽略中间变化的过程。


auto-aof-rewrite-percentage 100 (.aof文件增长到多少MB时重写),

auto-aof-rewrite-min-size 64mb (当至少大于64MB时再重写),

bgrewriteaof 马上重写。


rdb和aof可以同时使用,优先使用aof恢复。rdb恢复的快,aof是逐条执行的命令。


rdb和aof的一些注意事项:

(1)在dump rdb过程中,aof如果停止同步,会不会丢失? (不会,所有的操作缓存在内存的队列里, rdb执行dump完成后,统一操作.)

(2)aof重写是指什么? (aof重写是指把内存中的数据,逆化成命令,写入到.aof日志里.解决 aof日志过大的问题.)

(3)rdb和aof,两种是否可以同时用? (可以,而且推荐这么做)

(4)如果rdb文件和aof文件都存在,优先用谁来恢复数据? (优先aof)

(5)恢复时rdb和aof哪个恢复的快 (rdb快,因为其是数据的内存映射,直接载入到内存,而aof是命令,需要逐条执行)


【Redis集群】

集群的作用:主从备份,防止主机宕机;读写分离,分担master的任务;任务分离,主从服务器分别负责备份工作与计算工作。

redis集群的实现:


1.官方cluster方案:

从redis 3.0版本开始支持redis-cluster集群,redis-cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他节点连接。官方推荐的方案是将node配置成主从结构,即一个master主节点,挂n个slave从节点。如果主节点失效,redis cluster会根据选举算法从slave节点中选择一个上升为master节点,整个集群继续对外提供服务。

2.twemproxy代理方案:

Redis代理中间件twemproxy是一种利用中间件做分片的技术。客户端不直接访问redis服务器,而是通过twemproxy代理中间件间接访问。降低了客户端直连后端服务器的连接数量。twemproxy是一个单点,很容易对其造成很大的压力,所以通常会结合keepalived来实现twemproy的高可用。

3.哨兵模式Sentinel:

是Redis的高可用性解决方案:由一个或多个Sentinel实例组成的Sentinel系统可以监视任意多个主服务器以及这些主服务器下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器。

4.codis:

codis是一个分布式的Redis解决方案,对于上层的应用来说,连接codis proxy和连接原生的redis server没什么明显的区别,上层应用可以像使用单机的redis一样使用,codis底层会处理请求的转发,不停机的数据迁移等工作,所有后边的事情,对于前面的客户端来说是透明的,可以简单的认为后边连接的是一个内存无限大的redis服务。

5.客户端分片:

分区的逻辑在客户端实现,由客户端自己选择请求到哪个节点。方案可参考一致性哈希,这种方案通常适用于用户对客户端的行为有完全控制能力的场景。


【总结】

Redis Sentinal着眼于高可用,在master宕机时会自动将slave提升为master,继续提供服务。

Redis Cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。


缓存雪崩和穿透问题:


缓存穿透:一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就应该去后端系统查找(比如DB)。一些恶意的请求会故意查询不存在的key,请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。

如何避免?

1:对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert了之后清理缓存。

2:对一定不存在的key进行过滤。可以把所有的可能存在的key放到一个大的Bitmap中,查询时通过该bitmap过滤。


缓存雪崩:当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,会给后端系统带来很大压力。导致系统崩溃。

如何避免?

1:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。

2:做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期

3:不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。



Redis里面的 sub 和 pub?


Pub/Sub功能(means Publish, Subscribe)即发布及订阅功能。

基于事件的系统中,Pub/Sub是目前广泛使用的通信模型,它采用事件作为基本的通信机制,提供大规模系统所要求的松散耦合的交互模式:

订阅者(如客户端)以事件订阅的方式表达出它有兴趣接收的一个事件或一类事件;

发布者(如服务器)可将订阅者感兴趣的事件随时通知相关订阅者。

Redis的pub/sub是一种消息通信模式,主要的目的是解除消息发布者和消息订阅者之间的耦合,

Redis作为一个pub/sub的server,在订阅者和发布者之间起到了消息路由的功能。




使用Memcache 缓存查询数据

memcache的工作原理:

Memcache是一个高性能的发布式的内存对象缓存系统,通过在内存里维护一个统一的巨大的hash表,它能够用来存储各种格式的数据,包括图片、视频、文件以及数据库检索的结果等。简单的说就是将数据调用到内存,然后从内存中读取,从而大大提高读取速度。


memcache的工作流程:

先检查客户端的请求数据是否在memcached中,如有,直接把请求数据返回,不再对数据库进行任何操作;如果请求的数据不在memcached中,就去查数据库,把从数据库中获取的数据返回给客户端,同时把数据缓存一份到memcached中。


memcache的一些方法:



获取:get(key)
设置:set(key, val, expire)
删除:delete(key)





【telnet localhost 11211】



stats    查看状态(stats sizes  表示:64M中使用了 3M)
version 查看版本
添加操作:add myvar1 1 3000 10 //myvar1 键值,1 标志位,3000 存储时间(秒),10 存储的字符数(输入刚才设置的字符数10,举例:helloworld,显示STORED操作成功)。
获取操作:get myvar1 (此时再设置一个myvar1 就会出错)
覆盖操作:set myvar1 1 3000 10
删除操作:delete myvar1
清除所有操作:flush_all
说明:如果重启服务数据就会丢失。



【启动memcached】



memcached -d -m 128 -u root -l localhost -p 11211
/usr/local/memcached/bin/memcached -d -m 1024 -u root -l 127.0.0.1 -p 11211 -c 256 -P /tmp/memcached.pid
# 查看内存分配情况:
/usr/local/memcached/bin/memcached -d -m 1024 -u root -l 127.0.0.1 -p 11211 -c 256 -P /tmp/memcached.pid -vv



【PHP中测试 Memcache功能】



$mem=new memcache;
$mem->connect("localhost", 11211) or die ("连接失败");
$key = ‘goods_1’;
$data=$mem->get($key);
if(!$data){
$mem->set($key, ‘abcdefg’, MEMCACHE_COMPRESSED, 3600);
}