【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>';

}