【Redis基本】

1.redis安装完成后的几个文件:

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

redis-check-aof  日志文件检测工具(比如断电造成日志损坏,可以检测并修复)

redis-check-dump  快照文件检测工具,效果类上

redis-cli  客户端

redis-server 服务端

2.配置redis在后台运行:编辑conf配置文件,修改如下内容:daemonize yes

3.Redis中共有16个数据库(服务器),打开 redis.conf可以修改。使用select 1可以切换到1号数据库。

【Redis对于key的操作命令】

keys */?/[]:查询相应的key,支持*(任意字符)、?(单个字符)、[](括号内的一个字符)匹配

(查看当前有哪些key:keys *; 完整匹配:keys site;模糊匹配:keys s* 、 keys si?e 、 keys sit[ey])

randomkey:随机返回key

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

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

del key1 key2 ... KeyN :删除1个或多个键。不存在的key忽略掉,返回真正删除的key的数量

rename key newkey:给key赋一个新的key名。如果newkey已存在,则newkey的原值被覆盖

renamenx key newkey:同rename,只是newkey存在时,不进行操作

move key db:移动某个key到另一个数据库(例如:move site 1)

ttl key:查询key的剩余生命周期(返回秒)

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

expire key: 设置key的生命周期,以秒为单位

pexpire key: 设置生命周期,以毫秒数为单位

persist key: 把指定key置为永久有效

【Redis字符串类型的操作】

set key value [ex 秒数]/[px 毫秒数]

案例:set a 1 ex 10     #10秒有效

set key value [nx]/[xx] (nx表示key不存在时执行操作;xx表示key存在时执行操作。)

案例:set a 2 xx    #设置一个已经存在的key

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

案例:mset a hello b bold c cat d dog

get key: 获取key的值

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

案例:mget a b c d

setrange key offset value: 对字符串从第offset个字符开始替换成value

案例:setrange a 2 ??  #结果是he??o

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

案例:append a @@   #结果是 he??o@@

getrange key start stop :是获取字符串中 [start, stop]范围的(左数从0开始,右数从-1开始)

案例:set title ‘chinese’;getrange title 0 3 ====> ‘chin’

getset key newvalue :获取并返回旧值,设置新值,之后会替换原来的值。

案例:getset title ‘renxing’;  ====>返回‘chinese’,再次get得到‘renxing’

incr key:指定的key的值加1,并返回加1后的值。set age 20;incr age ==> 21

decr key:指定的key的值减1,并返回减1后的值。decr age; ====> 20

incrby key number:指定的key的值加number,并返回结果。incr age 4; ===>24

decrby key number:指定的key的值减number,并返回结果。decr age 8; ===>16

incrbyfloat key floatnumber:指定的key的值加浮点值,并返回结果(注意:没有decrbyfloat)

setbit  key offset value :设置offset对应二进制位上的值,返回该位上的旧值

getbit key offset:获取值的二进制表示,对应位上的值(从左,从0编号)

bitop operation destkey key1 [key2 ...]:对key1,key2..keyN作operation,并将结果保存到 destkey 上。operation 可以是 AND 、 OR 、 NOT 、 XOR。注意: 对于NOT操作, key不能多个。(基本没明白)

【link 链表结构】

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

案例:lpush wx a;rpush wx b;rpush wx c;lpush wx 8;  ===> 8 a b c

lrange key start stop: 返回链表中[start ,stop]中的元素,要取所有元素就是从0到-1.

案例:lrange wx 1 2; ===> a b

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

案例:lpop wx;(8)  rpop wx;(c)   ===> a b

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

案例①:rpush answer a d b a c b a; lrem answer 1 b; #从左向右删除一个b,结果是 a d a c b a

案例②:lrem answer -2 a; #从右向左删除2个a,结果是 a d c b

ltrim key start stop: 剪切key对应的链接,切[start,stop]一段,并把该段重新赋给key

案例:rpush wx a b c d e f; ltrim wx 2 5; #截取2和5之间的一段,结果是 c d e f

lindex key index: 返回index索引上的值。llen key:计算链接表的元素个数。

案例:lindex wx 0; ===>c    lindex wx 1; ===>d    llen wx; ===>4

linsert key after search value: 在key链表中寻找’search’,并在search值之前插入value。 linsert key before search value:同上,之后。注: 一旦找到一个search后命令就结束了,因此不会插入多个value。

案例:rpush num 1 3 6 8 9;  linsert num before 3 2  #在3前面插入一个2,结果 1 2 3 6 8 9

rpoplpush source bak: 把source链表的尾部拿出,放在bak链表的头部,并返回该单元值(应用双链表完成安全队列)

案例:rpush source a b c;rpush bak 1 2 3;rpoplpush source bak(c);===>此时source:a b,此时bak:c 1 2 3

brpop/blpop  key timeout:等待弹出key的尾(brpop)/头(blpop)元素。timeout为等待超时时间,如果为0则一直等待。(应用场景: 长轮询Ajax,在线聊天时,能够用到)

案例:两个终端,其中一个使用 brpop job 20,表示等待20秒;另一个rpush job e 表示给job写入数据,此时再回到第一个终端,会看到:job e (15.72s).

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

sadd key value1 value2: 往集合key中增加元素(由于具有唯一性,所以增加一个已经存在的元素时会返回0)

案例:sadd city beijing shanghai jilin

scard key : 返回集合中元素的个数。

案例:scard city  ===> 3

smambers key: 返回集合中所有元素。

案例:smembers city ===> beijingjilin shanghai

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

案例:srandmember city ===> beijing(随机的)

sismember key value: 判断value是否存在key集合中,存在返回1,不存在返回0。

案例:sismember city wlmq; ===> 0

srem key value1 value2:删除集合中值为 value1 value2的元素;返回真正删除掉的元素的个数。

案例:srem city jilin; ===>1,此时city的结果是 beijing shanghai

spop key:返回并删除集合key中1个随机元素。随机--体现了无序性。

案例:spop city; ===>shanghai(随机的),此时city的结果是 beijing

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

案例:sadd city beijing shanghai jilin;

sadd name jerry helen rx polly;

smove name city polly; ===>此时 city是 beijing polly jilin shanghai

sinter  key1 key2 key3: 求出key1 key2 key3 三个集合中的交集并返回

sinterstore dest1 key1 key2 key3: 求出key1 key2 key3 三个集合中的交集,并赋给dest1

sunion key1 key2.. Keyn  : 求出key1 key2 keyn的并集,并返回

sunionstore dest2 key1 key2.. Keyn : 求出key1 key2 keyn的并集, 并赋给dest2

sdiff key1 key2 key3: 求出key1与key2 key3的差集,即key1-key2-key3

sdiffstore dest3 key1 key2 key3: 求出key1与key2 key3的差集, 并赋给dest3

【order set 有序集合】

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

案例:zadd class 12 lily 13 lucy 18 lilei 6 poly ===>4

zrange key start stop [WITHSCORES] :把集合升序排列后,返回名次[start,stop]的元素。Withscores 是把score也打印出来。取出所有数据start=0,stop=-1即可。

zrevrange key start stop [WITHSCORES] 相反的降序排列。

案例:zrange class 1 2  #取出第1名到第2名的数据(从小到大),结果是 lily(12) lucy(13)

zrangebyscore  key min max [withscores] [limit offset N]: 取分数介于min和max之间的,按照分数升续排序;limit offset N:从第 offset个开始取出N个。

案例:zrangebyscore class 1 20 limit 1 2  #取分数介于1到20之间的,从1位开始取,取2个。

zrevrangebyscore  key max min [withscores] [limit offset N]: 取分数介于max和min之间的,按照分数降续排序(注意这里是max到min)

zrank key member:查询member的排名(升续 从0名开始)

zrevrank key memeber:查询 member的排名(降续 从0名开始)

案例:zrank class poly; zrevrank class poly;  #升序情况下poly是第0名,降序情况下是第3名。

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

案例:zrem class lily poly;   #删除name是lily和poly的

zremrangebyscore key min max :按照socre来删除元素,删除score在[min,max]之间的

案例:zremrangebyscore class 10 15; #删除score介于10到15的

zremrangebyrank key start end: 按排名删除元素,删除名次在[start,end]之间的

案例:zremrangebyrank class 0 2     #删除名次介于第0名到第2名的。

zcard key: 返回元素个数。

案例:zcard class; ===>4

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

案例:zcount class 10 15;  ===>2

 

zinterstore destination numkeys key1 [key2 ...]

[WEIGHTS weight [weight ...]]

[AGGREGATE SUM|MIN|MAX]

求key1,key2的交集,key1,key2的权重分别是 weight1,weight2。聚合方法用: sum |min|max。聚合的结果保存在dest集合内。

zunionstore destination numkeys key1 [key2 ...]

[WEIGHTS weight [weight ...]]

[AGGREGATE SUM|MIN|MAX]

取并集,和上面类似。

【Hash 哈希数据类型】

hset key field value : 把key中 filed域的值设为value(没有则添加,有则覆盖)

案例:hset user1 name lisi; hset user1 age 28; hset user1 height 175;

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

案例:hget user1 name ===> lisi

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

案例:hgetall user1 ===> name lisi age 28 height 175

hmset key field1 value1 [field2 value2 field3 value3 ......fieldN valueN] : 设置field1->N 个域, 对应的值是value1->N 【对应PHP理解为  $key = array(file1=>value1, field2=>value2 ....fieldN=>valueN) 】

案例:hmset user2 name wang age 10 height 100;

hgetall user2 ===> name wang age 10 height 100

hmget key field1 field2 fieldN : 返回key中field1 field2 fieldN域的值

案例:hmget user1 name height ===> lisi  175

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

案例:hlen user1 ===> 2

hkeys key : 返回key中所有的field

案例:hkeys user1 ===> name height

hvals key : 返回key中所有的value

案例:hvals user1 ===> lisi 175

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

案例:hexists user1 name ===>1

hincrby key field value : 是把key中的field域的值增长整型值value

hincrbyfloat  key field value : 是把key中的field域的值增长浮点值value

案例:对user2的age值+1:  hincrby user2 age 1 ===> 11

hdel key field : 删除key中 field域

案例:hdel user1 age; hgetall user1 ===> name lisi height 175

【Redis的事务】---- Redis支持简单的事务

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

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

Redis的事务中,只负责监测key没有被改动.具体的命令---- watch命令(监视)

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

unwatch    作用: 取消所有watch监听

【消息订阅】

订阅端: Subscribe 频道名称

发布端: publish 频道名称 发布内容

psubscribe 通配符匹配接收:

【Redis持久化配置】----- 2种方式:(1)rdb快照持久化 (2)日志

1. rdb的工作原理:

每隔N分钟或N次写操作后,从内存dump数据形成rdb文件,压缩,放在备份目录。优势在于:恢复速度很快!

 

2. redis.conf 配置文件中 rdb快照相关参数(如果这3个选项都屏蔽,则rdb禁用)

save 900 1          #刷新快照到硬盘中,必须满足两者要求才会触发,即900秒之后至少1个关键字发生变化。

save 300 10         #必须是300秒之后至少10个关键字发生变化。

save 60 10000       #必须是60秒之后至少10000个关键字发生变化。

3. rdb的缺陷

在2个保存点之间断电,将会丢失1-N分钟的数据。出于对持久化的更精细要求,redis增添了aof方式 append only file

4. aof配置

[开启] redis.conf ,搜索 aof:将的appendonly no  改为 appendonly yes,并设置aof的保存目录。

[具体配置]

appendonly no           #是否打开aof日志功能

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

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

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

no-appendfsync-on-rewrite  yes:     #正在导出rdb快照的过程中,要不要停止同步aof(重写aof时同步最新数据)

auto-aof-rewrite-percentage 100     #aof文件大小比起上次重写时的大小,增长率100%时,重写(当前aof文件是上次重写是大N%时重写)

auto-aof-rewrite-min-size 64mb      #aof文件,至少超过64M时,重写(aof重写至少要达到的大小)

5. 把最终的结果逆化为新的变量,只存储最终的结果,忽略中间变化的过程,叫做:aof重写。需要用到上面配置文件里的下面两个选项:

① auto-aof-rewrite-percentage 100 (假如上次aof的文件大小是2MB,那么下次达到4MB则重写,再下次8MB重写,再下次16MB...)

② auto-aof-rewrite-min-size 64mb(因为前期文件较小,重写的意义不大,可以设置当大于64MB的时候再重写。当然此值可以配置。)

◆ 还有一个命令:bgrewriteaof 可以直接马上重写。

◆ 没有用aof时,1秒钟可以写入1800条。打开aof并设置为1秒钟备份一次,测试结果1秒钟可以写入1300条,性能稍微会下降。

6. rdb和aof的一些注意事项:

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

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

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

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

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

【Redis主从复制】

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

2. 集群的形式:① slave1→master←slave2:所有的slave都围着master。

② slave2→slave1→master:后面的slave作为前面slave的slave。好处是:master宕机后,可以直接切换到slave1。

3. 主要配置:[Master配置]:关闭rdb快照(备份工作交给slave),master可以开启aof。[slave配置]:声明slave-of;某1个slave打开 rdb快照功能;配置是否只读(slave-read-only)。

4.如果主服务器设置了密码,那么从服务器就获取不到主服务器的key了,此时就需要在所有的从服务器上也加上密码。其实内网之间其实是不需要密码的。

5.redis主从复制的缺陷:每次salave断开后,(无论是主动断开,还是网络故障),如果再连接master,都要把master全部dump出来rdb,再aof,即同步的过程都要重新执行一遍.所以:多台slave不要一下都启动起来,否则master可能I/O剧增.

【运维常用的server端命令】(不区分大小写,下面的写法是为了方便查阅)

time            查看时间戳与微秒数

dbsize          查看当前库中的key数量

BgRewriteAof    后台进程重写AOF

BgSave          后台保存rdb快照

save            保存rdb快照

LastSave        上次保存时间

SlaveOf         设为slave服务器

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

SlowLog         显示慢查询

config get      获取配置信息(例如:config get requirepass #是否需要密码 )

config set      设置配置信息(例如:cinfig set slowlog-max-len #存储多少条慢查询的记录)

monitor         打开控制台

sync            主从同步

client list     客户端列表

client kill     关闭某个客户端

client setname 为客户端设置名字

client getname 获取客户端名字

FlushAll        清空所有db(慎用)

FlushDB         清空当前db(慎用)

◆ 如果不小心运行了flushall, 立即 shutdown nosave ,关闭服务器。然后手工编辑aof文件,
去掉文件中的 “flushall ”相关行, 然后开启服务器,就可以导入回原来数据。如果flushall之后,系统恰好bgrewriteaof了,那么aof就清空了,数据丢失.

info:显示服务器全部信息(info后面可以直接跟上想要获取的子阶段的内容,例如:INFO Server/ INFO Memory /)

* 通过 INFO 得到的数据,主要观察如下几个:

① Memory(内存)

used_memory:859192              #实际占用的数据结构的空间

used_memory_rss:7634944         #理论上分配的空间

mem_fragmentation_ratio:8.89    #前2者的比例,1.N为佳。如果此值过大,说明redis的内存的碎片化严重,可以导出再导入一次.


Persistence (持久化的信息)

rdb_changes_since_last_save:0   #自从上次rdb改动之后,又有多少改动

rdb_last_save_time:1375224063       #上次改动保存的时间

 


Status中的fork耗时:

latest_fork_usec:936       #上次导出rdb快照,持久化花费微秒

 


Replication (主从复制的信息)

role:master

connected_slaves:1

slave0:127.0.0.1,6381,online

【sentinel运维监控】

◆ 复制redis安装包下的 sentinel.conf 到 redis运行目录下,进行如下编辑:

sentinel
monitor mymaster 127.0.0.1 6379 1  # “1”表示有几个sentinel失效,生产环境下建议改为>2

sentinel
parallel-syncs mymaster 1  #当master宕机后,同时把几个slave指向新的master。

接下来,启动三台服务器。使用    ./bin/redis-server ./sentinel.conf --sentinel    启动sentinel

接下来在6379客户端执行 shutdown ,故意让master宕机,再回到刚才的sentinel界面,稍等片刻,可以看到:master 由6379转移到了(switch) 6381。

◆ 总结:

① Sentinel不断与master通信,获取master的slave信息,监听master与slave的状态。如果某slave失效,直接通知master去除该slave。如果master失效,是按照slave优先级(可配置), 选取1个slave做 new master,把其他slave转换为new slave。

② sentinel与master通信,如果某次因为master
IO操作频繁,导致超时,此时,认为master失效,很武断:sentnel允许多个实例看守1个master,
当N台(N可设置)sentinel都认为master失效,才正式失效.

③ Sentinel选项配置

port 26379     # 端口

sentinel monitor mymaster
127.0.0.1 6379 2  #给主机起的名字(不重即可), 当2个sentinel实例都认为master失效时,正式失效

sentinel down-after-milliseconds
mymaster 30000  #多少毫秒后连接不到master认为断开

sentinel
can-failover mymaster yes      #是否允许sentinel修改slave->master. 如为no,则只能监控,无权修改.

sentinel parallel-syncs mymaster
1      #一次性修改几个slave指向新的new master.

sentinel client-reconfig-script
mymaster /var/redis/reconfig.sh   # 在重新配置new master,new slave过程,可以触发的脚本

【Redis的key的设计原则】---- 实现书签系统

1. 使用普通的key方式存储书籍

set book:5:title 'PHP圣经'

set book:6:title 'ruby实战'

set book:7:title 'mysql运难'

set book:8:title ‘ruby server’

2. 使用“集合”的方式存储标签和书籍的关系

sadd tag:PHP 5      //tag为PHP的标签对应 5号书籍

sadd tag:WEB 5 6    //tag为WEB的标签对应 5号书籍和6号书籍,如下类似

sadd tag:database 7

sadd tag:ruby 6 8

sadd tag:SERVER 8

3.查询方法

① 查询既有PHP又有WEB的书——查集合的交集(Sinter)

sinter tag:PHP
tag:WEB ===> 5

② 查询有PHP或有WEB标签的书——查集合的并集(Sunin)

sunin tag:PHP
tag:WEB ===> 5    6

③ 查询含有ruby,不含WEB标签的书——求差集(Sdiff)

sdiff tag:ruby
tag:WEB ===> 8

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

第1段:把表名转换为key前缀。如 tag:

第2段:放置用于区分区key的字段,对应mysql中的主键的列名。如userid

第3段:放置主键值。如2,3,4...., a , b ,c

第4段:放置要存储的列名

例如:MySQL的如下数据。user表:

userid:9

username:lisi

password:111111

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


查询方法

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

user:userid:9:password

user:userid:9:username

user:userid:9:email

② 查询某个字段的值:

get
user:userid:9:username ===> lisi

get
user:userid:9:email ===> lisi@163.com

◆ 注意:冗余字段。

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

这样就可以根据username:lisi:uid ,查出userid=9。 再查user:9:password/email ... 完成了根据用户名来查询用户信息

【PHP操作Redis】

<?php

//实例化Redis对象

$redis = new Redis();

//连接到redis服务器

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

//print_r($flag);

//set方法存入数据

$redis->set('user:userid:2:username','renxing');

$value =
$redis->get('user:userid:2:username');

print_r($value);  //输入“renxing”

?>

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

使用hash类型存储数据。

hmset:批量添加数据,例如:$redis->hmset(‘user:1’,array(‘name’=>“renxing”,‘age’=>“28”));

hgetall:获取所有数据

del:删除数据

【连接到服务器】

$redis = new Redis();   //实例化

$redis->connect("localhost");   //连接到服务器

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

【执行添加操作】【修改操作(类似
添加操作)】

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

$add_data = array(

"uid"=>$uid,

"username"=>$_POST[username],

"password"=>$_POST[password],

"age"=>$_POST[age]

);  //使用hmset批量添加元素

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

(此时用终端运行hgetall user:1,得到如下数据:uid 1 username renxing password 123 age 18 )

【获取数据】

require_once
"conn.php";

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

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

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

}

print_r($data);

【删除操作】

$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。

...

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

$page_size = 3; //每页显示多少条数据

$page_num =
$_GET['page']?$_GET['page']:1;  //当前第几页

$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);  //下一页

//分析:假设每页显示3条,那么 —— 第1页: lrange uid
0 2 ; 第2页: lrange uid 3 5

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

$b = $a+$page_size-1;

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

//print_r($ids);

/*分页取数据*/

foreach($ids as $v){

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

}

【登录操作】

在注册操作的时候要存储一个 username和uid的对应关系 $redis->set("username:".$username,$uid); 那么登录的时候,使用get 就可以知道这个username是否存在 $id =
$redis->get("username:".$username); 然后通过这个$id 用hget得到存储在数据库中的password,和用户输入的密码进行判断
$pwd
= $redis->hget("user:".$id,"password"); 如果正确了,设置SESSION或者cookie。

【加关注】

① 用集合存储比较合适,sadd存储数据,smembers获取数据,sdiff获取两个用户关注的差集。

$id = $_GET['id']; //关注谁的uid

$uid = $_SESSION['uid']; //我的uid

$redis->sadd("user:".$uid.":guanzhu",$id);  //$uid 关注了哪些人

$redis->sadd("user:".$id.":fensi",$uid);    //$id 的粉丝

得到差集,就可以推荐用户了。

② 在PHP中显示我关注了谁。

$my_guanzhu =
$redis->smembers("user:".$_SESSION['uid'].":guanzhu");

foreach($my_guanzhu as $gz){

$row
= $redis->hgetall("user:".$gz);

echo
$row['uid'].'----'.$row['username'].'----'.$row['age'].'<br>';

}

③ 在PHP中显示我的粉丝

$my_fensi =
$redis->smembers("user:".$_SESSION['uid'].":fensi");

foreach($my_fensi as $fs){

$row2
= $redis->hgetall("user:".$fs);

echo
$row2['uid'].'----'.$row2['username'].'----'.$row2['age'].'<br>';

}

Redis的使用完整版的更多相关文章

  1. redis安装以及主从复制完整版

    redis安装以及主从复制完整版redis版本:redis-3.2.11主从复制模式:master--> slave1--> slave2 master:10.10.11.32 slave ...

  2. Swoole入门到实战 打造高性能 赛事直播平台(完整版)

    Thinkphp+Swoole入门到实战打造高性能赛事直播平台 第1章 课程介绍 欢迎大家来到swoole的课程!本章主要是介绍了swoole的一些特性,以及使用场景,并且分享了swoole在其他公司 ...

  3. 如何安全的将VMware vCenter Server使用的SQL Server Express数据库平滑升级到完整版

    背景: 由于建设初期使用的vSphere vCenter for Windows版,其中安装自动化过程中会使用SQL Server Express的免费版数据库进行基础环境构建.而此时随着业务量的增加 ...

  4. Android版的菜谱客户端应用源码完整版

    Android版的菜谱客户端应用源码完整版,这个文章是从安卓教程网转载过来的,不是本人的原创,希望能够帮到大家的学习吧. <ignore_js_op> 152936qc7jdnv6vo0c ...

  5. sed实例精解--例说sed完整版

    原文地址:sed实例精解--例说sed完整版 作者:xiaozhenggang 最近在学习shell,怕学了后面忘了前面的就把学习和实验的过程记录下来了.这里是关于sed的,前面有三四篇分开的,现在都 ...

  6. flexbox-CSS3弹性盒模型flexbox完整版教程

    原文链接:http://caibaojian.com/flexbox-guide.html flexbox-CSS3弹性盒模型flexbox完整版教程 A-A+ 前端博客•2014-05-08•前端开 ...

  7. 转贴 IT外企那点儿事完整版

    转贴 IT外企那点儿事完整版 第一章:外企也就那么回儿事(http://www.cnblogs.com/forfuture1978/archive/2010/04/30/1725341.html) 1 ...

  8. C#.Net 上传图片,限制图片大小,检查类型完整版

    C#.Net 上传图片,限制图片大小,检查类型完整版 源代码: 处理图片类,如检查图片大小,按宽度比例缩小图片 public class CImageLibrary{   public enum Va ...

  9. office2016 软件全集 官方下载免费完整版(含破解文件)不含垃圾软件 win10完美激活

    office2016官方下载免费完整版是新一代办公软件,office2016官方下载免费完整版已经分享到下面,office2016官方下载免费完整版包括了Word.Excel.PowerPoint.O ...

随机推荐

  1. Android --Android Stuido混淆签名打包

    参考博客:Android studio 使用心得(五)—代码混淆和破解apk 参考博客:Android studio 使用心得(四)---android studio 多渠道打包 参考博客:Andro ...

  2. [ROS] slam_gmapping

    slam_gmapping节点 1)slam_gmapping 节点在sensor_msgs/LaserScan消息内获取数据并建立地图 map(nav_msgs/OccupancyGrid).该地图 ...

  3. 字符集WideCharToMultiByte

    GDAL C#封装对中文字符转换过程中存在问题. C++封装一个Win32 DLL,采用Unicode字符集.使用标准头文件. https://msdn.microsoft.com/en-us/lib ...

  4. PHP PSR-1 基本代码规范(中文版)

    基本代码规范 本篇规范制定了代码基本元素的相关标准,以确保共享的PHP代码间具有较高程度的技术互通性. 关键词 “必须”("MUST").“一定不可/一定不能”("MUS ...

  5. Linux 动态链接库学习笔记

    参考资料: http://www.linuxidc.com/Linux/2012-01/50739.htm http://www.yolinux.com/TUTORIALS/LibraryArchiv ...

  6. D3D9 GPU Hacks (转载)

    D3D9 GPU Hacks I’ve been trying to catch up what hacks GPU vendors have exposed in Direct3D9, and tu ...

  7. Nand Flash与Nor Flash的区别

    区别:http://zhidao.baidu.com/question/1068445.html?qbl=relate_question_0&word=Serial%20Flash%20%D3 ...

  8. Custom IFormatProvider

    The following example shows how to write a custom IFormatProvider which you can use in methodString. ...

  9. Python学习总结14:时间模块datetime & time & calendar (一)

    Python中的常用于处理时间主要有3个模块datetime模块.time模块和calendar模块. 一.time模块 1. 在Python中表示时间的方式 1)时间戳(timestamp):通常来 ...

  10. DO.NET操作数据库

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...