当先锋百科网

首页 1 2 3 4 5 6 7

NoSQL数据库之Redis数据库管理一(Redis的介绍与安装部署)

1 NoSQL
Not Only SQL,反SQL运动
非关系型数据库
处理超大规模,高并发的SNS类型Web2纯动态网站

NoSQL用Key-Value形式存储
不遵循SQL标准,ACID事务,表结构等
非关系型,分布式,开源,水平可扩展
处理超大量的数据
运行在便宜的pc服务器集群上
击碎了性能瓶颈
对数据高并发读写
对海量数据的高效率存储和访问
对数据的高可扩展性和高可用性


是一个数据结构服务器
key可以包含strings,hashed, lists, sets, sorted sets
这些数据类型都支持push/pop, add/remove
以及交集,并集,差集等操作
支持各种不同方式的排序
为保证效率,数据都是缓存的内存中
可以周期性的把更新的数据写入磁盘
或者把修改操作写入追加的纪录文件


全球最大的Redis用户是新浪微博
200多台物理机,400多个端口,+4G数据为Redis服务
部署场景
1 应用程序直接访问Redis数据库
一旦Redis宕机,内存数据可能永久丢失
2 应用程序直接访问Redis,只有当Redis访问失败时访问之后的MySQL
Redis数据和MySQL完全同步


适用场合
1 取最新N各数据的操作
2 排行榜应用,取TOPN操作
3 需要精确设定过期时间的应用
4 计数器应用
5 Uniq操作,获取某段时间所有数据排重值
6 实时系统,反垃圾系统
7 Pub/Sub 构建实时消息,发布与订阅
8 构建队列系统 list 栈,队列
9 缓存

    redis        mysql                mongodb
1    库            库                    库
2    /            表                    集和
3    /            字段,行,列        /


安装
1 下载 http://redis.io/download
2.4.17 stable
wget http://redis.googlecode.com/files/redis-2.4.17.tar.gz

***********************************************************
现在最新是3.0稳定版
$ wget http://download.redis.io/releases/redis-3.0.5.tar.gz
$ tar xzf redis-3.0.5.tar.gz
$ cd redis-3.0.5
$ make
Run Redis with:
$ src/redis-server
interact with Redis using the built-in client:
$ src/redis-cli
redis> set foo bar
OK
redis> get foo
"bar"
***********************************************************

2 编译源程序
(redis需要编译,mongo解压后直接用)
tar zxvf redis-2.4.17.tar.gz
cd redis-2.4.17
make (没有configuration,直接make )
cd src && make install (编译后跳入它src目录安装)

3 移动文件便于管理(所有软件redis,mysql,memcache等都在这个目录)
mkdir -p /usr/local/redis/bin
mkdir -p /usr/local/redis/etc
mv /lamp/redis-2.4.17/redis.conf /usr/local/redis/etc
cd /lamp/redis-2.4.17/src
mv mkreleasehdr.sh redis-benchmark redis-check-aof redis-check-dump redis-cli redis-server /usr/local/redis/bin

4 启动Redis服务
cd /usr/local/redis/bin/
./redis-server /usr/local/redis/etc/redis.conf
服务端默认连接端口6379
以上方式,将占用系统进程,没有在后台运行
记得以前mysql后台启动方式
mysqld_safe --user=mysql &
redis后台启动方式要修改配置文件
    daemonize 如果需要后台运行,改成yes
    pidfile 配置多个pid的地址,默认在/var/run/redis.pid
    bind 绑定ip,设置后只接受来自该ip的请求
    port 监听端口,默认6379
    timeout 客户端连接超时,单位秒
    loglevel 共4级,debug,verbose,notice,warning
    logfile 配置log文件地址
    databases 设置数据库各数,默认为0
    save 设置redis进行数据库镜像频率,同步和备份数据
    rdbcompression 镜像备份时,是否压缩
    Dbfilename 镜像备份文件的文件名
    Dir 数据库镜像备份文件放置路径
    Slaveof 设置数据库为其他数据库的从数据库
    Masterauth 主数据库连接需要的密码验证
    Requirepass 设置登陆时需要使用的密码
    Maxclients 限制同时连接的客户数量
    Maxmemory 设置redis能够使用的最大内存
    Appendonly 开启append only模式
    Appendfsync 设置对appendonly.aof文件同步的频率,数据备份的第二种方式
    vm-enabled 是否开启虚拟内存支持
    vm-swap-file 设置虚拟内存的交换文件路径
    vm-max-memeory 设置redis使用的最大无力内存大小
    vm-page-size 设置虚拟内存的页大小
    vm-pages 设置交换文件的总的page数量
    vm-max-threads 设置VMIO同时使用的线程数量
    Glueoutputbuf  把小的输出缓存存放在一起
    hash-max-zipmap-entries 设置hash的临界值
    Activerehashing 重新hash
修改后重新运行命令,就后台启动了
ps -ef | grep redis 查看启动结果
netstat -tunpl |grep 6379 查看端口号是否被占用


5 客户端连接
/usr/local/redis/bin/redis-cli

6 停止Redis实例
可以使用/usr/local/redis/bin/redis-cli shutdown
或者 pkill redis-server (或者kill -9)
检查端口是否没有占用了
!net (调出最近的net相关命令,显示出下面命令)
netstat -tunpl |grep 6379


环境
视频中不使用桥接,桥接要插网线
使用VMware Network Apapter1, NAT方式连接
和主机在同一网段

试验环境,用了桥接,关闭VMware Network Apapter1




**********************************************************************
数据类型
String, hash, list, set, zset


String
二进制安全
可以包含任何数据
比如jpg图片,序列化对象,都可以打散成二进制来存放

查看是否启动redis
netstat -tunpl | grep 6379
启动redis
./redis-server /usr/local/etc/redis.conf
连接客户端
./redis-cli

set
添加key/value为 name/lijie
set name lijie

setnx
如果key存在返回0, 设值不成功,nx是not exist
不存在就设置value
setnx name lijie

get
获得key的value
get name

setex
添加key/value,指定对应的有效期,秒
setex haircolor 10 red
超过10秒后get haircolor,反回空(nil)

setrange
设定key的value的子字符串
setrange name 6 gmail.com
[email protected]替换成[email protected]
返回最终返回的字符数
如果替换的字符串没有元字符串长,会剩下一部分

mset
一次性设置多个,批量设置
mset key1 lijie1 key2 lijie2

mget
一次性获得多个值
mget key1 key2
没有返回(nil)

msetnx
不覆盖已经存在的key

getset
设置key的值,返回key的旧值

getrange
获得子字符串
getrange email 0 4  
获得下标为0到4的字符

incr
对某个值递增
incr key1
    2
incr key1
    3

incrby
递增指定值
incrby key1 5
    8
incrby key1 -1
    7


decr
decrby
自减

append
后面添加字符串,返回新字符串长度
append name .net
    9
get name
    lijie.net

strlen
字符串长度


******************************************************
hash
适用于存储对象
string类型也可以存储对象,把对象序列化成2进制存储
hash相比string,会占用更少的内存,并方便存取整个对象

hset
创建一个user:001的hash表, 有name属性,值是lijie
hset  user:001 name  lijie

hget
获得哈西表的属性值
hget user:001 name
    lijie

hsetnx
如果存在返回0,设置不成功

hmset
批量设置多个属性
hmset user:003 name hello age 20

hmget
hmget user:003 name age

hincrby
自增
hincrby user:003 age 5
    25

hexists
存在返回1,不存在返回0
hexists user:001 sex
    0

hlen
返回属性数量
hlen user:003
    2

hdel
删除一个属性
hdel user:003 age

hkeys
返回所有key
hkeys user:003
    name
    age

hvals
返回所有value
hvals user:003
    lijie
    20

hgetall
获得所有key/value
hgetall user:003
    name
    lijie
    age
    20

********************************************************************
lists类型
链表结构
push,pop,获取一个范围所有值
每个子元素都是string类型的双向链表
可以做栈也可以做队列

lpush
从头部压入一个元素,反回list中的元素个数,栈,先进后出
lpush mylist "world"
    1
lpush mylist "hello"
    2
lrange mylist 0 -1
    hello
    world

lrange
取一个范围的值,取出是总是从头出来
lrange mylist 0 -1
0代表头部第一个元素下标,-1代表最后一个元素(尾部第一个元素)
这里就是取得list里面所有元素
    

rpush
从尾部压入一个元素,队列,先进先出
rpush list2 "world"
    1
rpush list2 "hello"
    2
lrange list2 0 -1
    world
    hello

linsert
从特定位置前或者后添加字符串,头部为前
linsert list2 before world piece
在world前面插入piece
lrange list2 0 -1
    piece
    world
    hello

lset
指定下标后设置值
lset list2 2 "hi"
lrange list2 0 -1
    piece
    world
    hi

lrem
从key对应的list中删除n个和value相同的元素
n<0从尾删除,n=0全部删除
lrange list3 0 -1
    hi
    hi
    piece
    world
    hi
lrem list3 2 hi
lrange list3 0 -1
    piece
    world
    hi

ltrim
保留指定key的value范围内的数据
其他元素删除
lrange list4 0 -1
    one
    two
    three
    four
ltrim list 1 2
lrange list4 0 -1
    two
    three

lpop
弹出
从头部删除元素,并返回该元素
lrange list5 0 -1
    one
    two
    three
    four
lpop list4
    one

rpop
从尾部删除元素,并返回该元素
rpop list5
    four
lrange list5 0 -1
    two
    three

rpoplpush
从第一个list尾部移出元素并添加到第二个list头部
rpoplpush list4 list5

lindex
返回list中index位置的元素
lrange list5 0 -1
    two
    three
lindex list5 1
    three

llen
返回list的元素个数
llen list5
    2

**************************************************************
set
集合
是string类型的无序集合
通过hash table实现
可以取并集,交集,差集
实现好友推荐,blog的tag等功能

sadd
添加元素
sadd set1 hello
    1
sadd set1 world
    1
sadd set1 hello
    0
不能添加重复元素

smembers
查看
smembers set1
    hello
    world
顺序是随机的

srem
删除元素
srem set2 one

spop
随机返回并删除一个元素
无法指定弹出哪个元素,随机弹出
smembers set3
    one
    two
    three
spop set3
    two

sdiff
返回差集,即set1中和set2不同的元素
smembers set4
    one
    two
smembers set5
    two
    three
sdiff set4 set5
    one
以前面的set4位标准,返回set4的元素

sdiffstore
返回差集并存放结果到另一个key中
sdiffstore set6 set4 set5
smembers set6
    one

sinter
交集
sinter set4 set5
    two

sinterstore
返回交集并存放结果到另一个key中

sunion
并集
sunion set4 set5
    two
    three
    one

sunionstore
返回并集并存放结果到另一个key

smove
从第一个集合中移出元素添加到另一个集合中
smove set4 set5 one
smembers set4
    two
smembers set5
    one
    two
    three

scard
返回集合个数
scard set5
    3

sismember
判断是否属于该集合
属于返回1,不属于返回0
sismember set5 one
    1

srandmember
随机返回一个元素,不删除
srandmember set5
    three
srandmember set5
    two

*******************************************************************
zset
sorted set 有序集合
增加了顺序属性

zadd
添加元素,声明其score顺序
zadd zset1 1 one
    1
zadd zset1 2 two
    1
zadd zset1 3 two
    0
不能增加value相同的元素
如果相同,则更新其序号

zrange
查看
zrange zset1 0 -1 withscores
    one
    1
    two
    3
withscores为输出顺序号

zrem
删除一个元素
zrem zset1 two
    1
zrange zset1 0 -1 withscores
    one
    1

zincrby
自增顺序号,如果元素不存在就插入新元素
返回新的序号
zincrby zset1 2 one
    3
zrange zset1 0 -1 withscores
    one
    3

zrank
返回集合中元素的排名(下标),按score来从小到大排序
zrange zset2 0 -1 withscores
    one
    1
    two
    2
    three
    3
    four
    4
zrank zset2 two
    1
返回排序后,two的下标

zrevrank
返回集合中元素的排名(下标),按score来从大到小排序
zrange zset2 0 -1 withscores
    one
    1
    two
    2
    three
    3
    four
    4
zrank zset2 two
    2

zrangebyscore
返回某个范围的元素
zrangebyscore zset2 1 2 withscores
    two
    2
    three
    3

zcount
返回某个score范围内元素数量
zcount  zset2 1 2
    2

zcard
集合中所有元素个数
zcount  zset2
    4

zremrangebyrank
删除某个下标范围的元素
zremrangebyrank zset2 1 1
    one
    1
    three
    3
    four
    4

zremrangebyscore
删除某个score范围的元素
zremrangebyrank zset2 1 1
    three
    3
    four
    4


***********************************************************
redis 常用命令和高级应用





1)服务器命令
quit:关闭连接(connection)
auth:简单密码认证
help cmd: 查看cmd帮助,例如:help quit
save:将数据同步保存到磁盘
bgsave:将数据异步保存到磁盘
lastsave:返回上次成功将数据保存到磁盘的Unix时戳
shundown:将数据同步保存到磁盘,然后关闭服务

info:提供服务器的信息和统计
    redis_version
    vm_enabled 0
    role:master
    ......

monitor:实时转储收到的请求
slaveof:改变复制策略设置
config:在运行时配置Redis服务器
config get: 实时获取参数配置的值
    config get dir
        dir
        /usr/local/redis/
    config get *
        返回所有配置文件的配置项和值
ping: 测试连接是否存活
    ping
        PONG 代表redis连接正常
echo: 打印内容
    echo lijie
        lijie


2)对value操作的命令
exists(key):确认一个key是否存在
    exists name
        1    表示name存在

del(key):删除一个key
    del age

type(key):返回值的类型
    type list1
        list
    type zset1
        zset

keys(pattern):返回满足给定pattern的所有key
    keys *    返回当前库中所有键
    keys my* 模糊匹配

randomkey:随机返回key空间的一个key
    randomkey
        age

rename(oldname, newname):重命名key
    rename age age_new

dbsize:返回当前数据库中key的数目
    dbsize
        12 当前数据库总共12个键

expire:设定一个key的活动时间(s)
    expire set1 10    设置10秒后过期
    
ttl:获得一个key的活动时间
    ttl set1
        9
    ttl set1
        8
    ttl set1
        7
    。。。
    ttl set1
        -1    表示过期

select(index):按索引查询, 选择数据库
    数据库名为0~15,总共16个数据库
    select 0
    select 1
    select 2
    相当于mysql 的use database;
move(key, dbindex):移动当前数据库中的key到dbindex数据库
    move age 1 移动age这个key到1数据库

persist: 移除给定key的过期时间
    expire age 300
    persist age
    ttl age
        -1

flushdb:删除当前选择数据库中的所有key
    flushdb
        OK
    
flushall:删除所有数据库中的所有key
    flushall
        OK



**************************************************************
高级应用
1 安全性
让登陆客户端时使用密码
配置文件中
vi /usr/local/redis/etc/redis.conf
requirepass openview
启动服务端
/usr/local/redis/bin/redis-server /usr/local/redis/etc/redis.conf
客户端
/usr/local/redis/bin/redis-cli
授权
auth openview
    OK
然后可以使用命令
或者登陆时,
/usr/local/redis/bin/redis-cli -a openview
就可以不用auth



2 主从复制
配置和使用非常简单
slave可以和master数据同步
master可以有多个slave
slave可以连接master,也可以连接其他slave
主从复制不会阻塞master,同步数据时,master可以继续处理client请求
提高系统伸缩性
主从复制过程
1 slave与master建立连接,发送sync同步命令
2 master启动一个后台进程,将数据库快照保存到文件,同时master收集新的写命令并缓存
3 后台完成保存后,将文件发送给slave
4 slave将文件保存到硬盘
配置slave
在slave配置文件redis.conf,加入
slaveof 192.168.10.1 6379
masterauth openview
这样就配置成功了
master里面set testkey hello 后
slave 里面get testkey,可以得到hello
使用info命令可以查看主从关系




3 事务处理(不成熟)
redis只能保证一个client发起的事务中的命令可以连续执行
而中间不会插入其他client命令
当一个client发送multi命令时,进入事务上下文
该连接后续的命令不会立即执行,放到一个队列
执行exec后,才按照顺序执行队列中的命令
multi
set age 10
    QUEUED
set age 20
    QUEUED
exec
    OK
    OK
get age
    20

discard
取消一个事务
multi
set age 30
    QUEUED
set age 40
    QUEUED
discard
    OK
get age
    20


redis乐观锁
添加version字段
开两个session对age进行操作
session1
    get age
        10
    watch age    监控age,如果age被修改过,不允许事务执行,就是乐观锁
        OK
    multi
        OK
    
session2
    set age 30
        OK
    get age
        30
session1
    set age 20
        QUEUED
    exec
        nil
    get age
        30

unwatch,exec,discard可以清楚监视
redis的事务回滚有问题,成功的操作不会回滚



4 持久化机制
1 snapshotting,快照,默认方式
    默认报存文件是dump.rdb, 二进制文件
    /usr/local/redis/bin/dump.rdb
    配置redis在n秒内如果超过m个key被修改就自动做快照
    save 900 1    900秒内超过1个key修改,发起快照保存
    save 300 10    
    save 60 10000
    


2 append-only file aof 方式
snapshot有间隙,可能导致数据丢失
aof方式有更好的持久化性
将每一个写命令追加到文件保存
重起时,执行文件中的写命令
通过配置文件使用同步函数fsync函数强制os写入到磁盘的时机
#开启aof方式
appendonly yes
#每秒写入磁盘一次,在性能和持久化做了折中
appendfsync everysec
#收到命令就写磁盘,慢
#appendfsync always
#完全依赖os,性能最好,持久没保障
#appendfsyno no

然后在/usr/local/redis/bin/下就生成
appendonly.aof文件
里面存了所有操作





5 发布订阅消息
pub/sub
一种消息通信模式
接触发布者和订阅者的耦合
redis作为pub/sub的服务器,在两者之间起到消息路由功能
订阅者通过subscribe和psubscribe命令向redis订阅消息类型
redis将信息类型称为通道channel
发布者通过publish向redis server发送特定信息,
订阅了该类型的client都收到此消息

session1
subscribe tv1     订阅了tv1

session2
subscribe tv1 tv2    订阅了tv1,tv2

session3
publish tv1 lijie

然后session1,session2都显示
message
tv1
lijie




6 虚拟内存使用
把不经常访问的数据从内存交换到磁盘
腾出宝贵的内存空间
vm-enabled yes        开启vm功能
vm-swap-file /tmp/redis.swap    交换的value保存的路径
vm-max-memory 1000000    redis使用的最大内存上限
vm-page-size 32        每个页面的大小32字节
vm-pages 134217728        最多使用多少页面
vm-max-threads 4        用于执行value对象换入的工作线程数量

配置好重起
pkill redis-server




***********************************************************************
实例1
php+redis
用户管理系统

首先查看php环境
php中是否加入了redis扩展
vi /usr/local/php/etc/php.ini
查找extension
可以看到
extension=pdo_mysql.so
extension=memcache.so
表示集成了mysql和memcache
但是没有redis
也可以打开php的info页面
192.168.10.1/test.php

php官方没有redis
redis官方有方法集成php
并且有介绍php中如何使用redis命令
redis-2.4.17.tar.gz
owlient-phpredis-2.1.1.1-g90ecd17.tar.gz

安装源代码包步骤
1 解压
tar zxvf owlient-phpredis-2.1.1.1-g90ecd17.tar.gz
cd owlient-phpredis-2.1.1.1-g90ecd17
2 准备php编译环境
/usr/local/php/bin/phpize
看到目录下有了个configure文件
就可以用来执行./configure了
./conigure --with-php-config=/usr/local/php/bin/php-config
3 编译和安装
make && make install
4 配置php
找到编译后生成的redis.so文件
/usr/local/php//lib/php/extensions/no-debug-non-zts-20060613/
把路径配置到php
vi /usr/local/php/etc/php.ini
extension="redis.so"
重起php
/usr/local/apache2/bin/apachectl restart
访问php的info页面
192.168.10.1/test.php
可以看到redis页面


配置linux和windows的虚拟磁盘映射
安装samba服务器
挂载光盘
mount /dev/cdrom / /mnt/cdrom
yum -y install samba* --skip-broken 跳过有损的依赖包
vi /etc/samba/smb.conf
[web]
    path=/usr/local/apache2/htdocs
    browseable=yes
    writable=yes
service smb restart
smbpasswd -a apache
    密码123

windows下添加映射网络驱动
z:\盘映射到\\192.168.10.1\web
用户是    apache/123
进入z:\
看到里面一些php主页的文件和图片
test.php,index.html等等

要给apache用户添加对htdocs/目录的权限,使用acl命令
这样才可以在里面添加文件和目录
setfacl -m u:apache:rwx -R /usr/local/apache2/htdocs/

进入z:\
新建redis文件夹
新建redis.php


redis.php
<?php
    //实例化
    $redis = new Redis();
    //连接服务器
    $redis->connect("localhost");
    //授权
    $redis->auth("lamplijie");


reg.php
<?php
    require("redis.php");
    $username = $_POST['username'];
    $password = md5($_POST['password']);
    $age = $_POST['age'];
    $uid = $redis->incr("userid");
    $redis->hmset("user:".$uid,array("uid"=>$uid,"username"=>$username,"password"=>$password,"age"=>$age));
    header("location:list.php");


add.php
<form action="reg.php" method="post">
    用户名:<input type="text" name="username" /><br />
    密码:<input type="password" name="password" /><br />
    年龄:<input type="text" name="age" /><br />
    <input type="submit" value="注册" />
    <input type="reset" value="重新填写" />
</form>


list.php
<a href="add.php" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" >注册</a>
<?php
    require("redis.php");
    for($i=1;$i<=($redis->get("userid"));$i++){
        $data[] = $redis->hgetall("user:".$i);
    }
    //dump可以在页面打印内容
    //var_dump($data);
    $data = array_filter($data);
?>
<table border=1>
    <tr>
        <th>uid</th>
        <th>username</th>
        <th>age</th>
        <th>操作</th>
    <tr>
<?php foreach($data as $v){?>
    <tr>
        <td><?php echo  $v['uid']?></td>
        <td><?php echo  $v['username']?></td>
        <td><?php echo  $v['age']?></td>
        <td><a href="del.php?id=<?php echo $v['uid']?>" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" >删除</a> <a href="mod.php?id=<?php echo $v['uid']?>" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" >编辑</a></td>
    </tr>
<?php }?>
</table>

mod.php
<?php
    require("redis.php");
    $uid = $_GET['id'];
    $data = $redis->hgetall("user:".$uid);
?>
<form action="doedit.php" method="post">
    <input type="hidden" value="<?php echo $data['uid']?>" name="uid" />
    用户名:<input type="text" name="username" value="<?php echo $data['username']?>" /><br />
    年龄:<input type="text" name="age" value="<?php echo $data['age']?>" /><br />
    <input type="submit" value="修改" />
    <input type="reset" value="重新填写" />
</form>


doedit.php
<?php
    require("redis.php");
    $uid = $_POST['uid'];
    $username = $_POST['username'];
    $age = $_POST['age'];
    $a = $redis->hmset("user:".$uid,array("username"=>$username,"age"=>$age));
    if($a){
        header("location:list.php");
    }else{
        header("location:mod.php?id=".$uid);
    }


del.php
<?php
    require("redis.php");
    $uid = $_GET['id'];
    $redis->del("user:".$uid);
    header("location:list.php");

***********************************************************************
实例2
好友关注

redis.php
<?php
    //实例化
    $redis = new Redis();
    //连接服务器
    $redis->connect("localhost");
    //授权
    $redis->auth("lamplijie");

reg.php
使用md5加密password
<?php
    require("redis.php");
    $username = $_POST['username'];
    $password = md5($_POST['password']);
    $age = $_POST['age'];
    $uid = $redis->incr("userid");
    $redis->hmset("user:".$uid,array("uid"=>$uid,"username"=>$username,"password"=>$password,"age"=>$age));
    $redis->rpush("uid",$uid);
    $redis->set("username:".$username,$uid);
    header("location:list.php");

login.php
使用md5加密生成一个登陆密钥,放入cookie中
<?php
    require("redis.php");
    $username = $_POST['username'];
    $pass = $_POST['password'];
    $id = $redis->get("username:".$username);
    if(!empty($id)){
        $password = $redis->hget("user:".$id,"password");
        if(md5($pass) == $password){
            $auth = md5(time().$username.rand());
            $redis->set("auth:".$auth,$id);
            setcookie("auth",$auth,time()+86400);
            header("location:list.php");
        }
    }

    
?>
<form action="" method="post">
    用户名:<input type="text" name="username" /><br />
    密码:<input type="password" name="password" /><br />
    <input type="submit" value="登陆" />
</form>


logout.php
<?php
    setcookie("auth","",time()-1);
    header("location:list.php");

add.php
<form action="reg.php" method="post">
    用户名:<input type="text" name="username" /><br />
    密码:<input type="password" name="password" /><br />
    年龄:<input type="text" name="age" /><br />
    <input type="submit" value="注册" />
    <input type="reset" value="重新填写" />
</form>



list.php
<a href="add.php" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" >注册</a>
<?php
    require("redis.php");
    if(!empty($_COOKIE['auth'])){
    $id = $redis->get("auth:".$_COOKIE['auth']);
    $name = $redis->hget("user:".$id,"username");
?>
欢迎您,<?php echo $name?>,<a href="logout.php" target="_blank" rel="external nofollow" >退出</a>

<?php
}else{
?>

<a href="login.php" target="_blank" rel="external nofollow" >登陆</a>
<?php
}
    //用户总数
    $count = $redis->lsize("uid");
    
    //页大小
    $page_size = 3;
    
    //当前页码
    $page_num = (!empty($_GET['page']))?$_GET['page']:1;
    
    //页总数
    $page_count = ceil($count/$page_size);
    
    $ids = $redis->lrange("uid",($page_num-1)*$page_size,(($page_num-1)*$page_size+$page_size-1));
    
    //var_dump($ids);
    
    
    foreach($ids as $v){
        $data[] = $redis->hgetall("user:".$v);
    }
    //var_dump($data);
    //$data = array_filter($data);
?>
<table border=1>
    <tr>
        <th>uid</th>
        <th>username</th>
        <th>age</th>
        <th>操作</th>
    <tr>
<?php foreach($data as $v){?>
    <tr>
        <td><?php echo  $v['uid']?></td>
        <td><?php echo  $v['username']?></td>
        <td><?php echo  $v['age']?></td>
        <td><a href="del.php?id=<?php echo $v['uid']?>" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" >删除</a> <a href="mod.php?id=<?php echo $v['uid']?>" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" >编辑</a>
        
        <?php if(!empty($_COOKIE['auth'])&&$id!=$v['uid']){?>
        <a href="addfans.php?id=<?php echo $v['uid']?>&uid=<?php echo $id?>" target="_blank" rel="external nofollow" >加关注</a></td>
        <?php }?>
    </tr>
<?php }?>
<tr>
    <td colspan="4">
        <a href="?page=<?php echo (($page_num-1)<=1)?1:($page_num-1)  ?>" target="_blank" rel="external nofollow" >上一页</a>
        <a href="?page=<?php echo (($page_num+1)>=$page_count)?$page_count:($page_num+1)  ?>" target="_blank" rel="external nofollow" >下一页</a>
        <a href="?page=1" target="_blank" rel="external nofollow" >首页</a>
        <a href="?page=<?php echo $page_count?>" target="_blank" rel="external nofollow" >尾页</a>
        当前<?php echo $page_num?>页
        总共<?php echo $page_count?>页
        总共<?php echo $count?>个用户
    </td>
</tr>
</table>

<table border=1>
    <caption>我关注了谁</caption>
    <?php
        $data = $redis->smembers("user:".$id.":following");
        foreach($data as $v){
            $row = $redis->hgetall("user:".$v);
    ?>
    <tr>
        <td><?php echo $row['uid']?></td>
        <td><?php echo $row['username']?></td>
        <td><?php echo $row['age']?></td>
    </tr>
    <?php
        }
    ?>
</table>

<table border=1>
    <caption>我的粉丝</caption>
    <?php
        $data = $redis->smembers("user:".$id.":followers");
        foreach($data as $v){
            $row = $redis->hgetall("user:".$v);
    ?>
    <tr>
        <td><?php echo $row['uid']?></td>
        <td><?php echo $row['username']?></td>
        <td><?php echo $row['age']?></td>
    </tr>
    <?php
        }
    ?>
</table>


mod.php
<?php
    require("redis.php");
    $uid = $_GET['id'];
    $data = $redis->hgetall("user:".$uid);
?>
<form action="doedit.php" method="post">
    <input type="hidden" value="<?php echo $data['uid']?>" name="uid" />
    用户名:<input type="text" name="username" value="<?php echo $data['username']?>" /><br />
    年龄:<input type="text" name="age" value="<?php echo $data['age']?>" /><br />
    <input type="submit" value="修改" />
    <input type="reset" value="重新填写" />
</form>

edit.php
<?php
    require("redis.php");
    $uid = $_POST['uid'];
    $username = $_POST['username'];
    $age = $_POST['age'];
    $a = $redis->hmset("user:".$uid,array("username"=>$username,"age"=>$age));
    if($a){
        header("location:list.php");
    }else{
        header("location:mod.php?id=".$uid);
    }

del.php
<?php
    require("redis.php");
    $uid = $_GET['id'];
    $redis->del("user:".$uid);
    $redis->lrem("uid",$uid);
    header("location:list.php");


addfans.php
<?php
    $id = $_GET['id'];
    $uid = $_GET['uid'];
    require("redis.php");
    $redis->sadd("user:".$uid.":following",$id);
    $redis->sadd("user:".$id.":followers",$uid);
    header("location:list.php");






========================================================