Redis Installation、Configuration、Program Based On Redis Learning

目录

1. Redis 简介
2. Redis安装配置
3. 编程使用Redis
4. 使用Lua脚本

 

1. Redis 简介

0x1: Redis是什么

Redis是一款Nosql类型的基于key-value的高速缓存系统,

从架构上看,redis有3种特性

1. key value store
是一个以key-value形式存储的数据库,定位直指MySQL,用来作为唯一的存储系统

2. memory cache
是一个把数据存储在内存中的高速缓存,用来在应用和数据库间提供缓冲,替代memcachd

3. data structrue server
把它支持对复杂数据结构的高速操作作为卖点,提供某些特殊业务场景的计算和展现需求。比如排行榜应用,Top 10之类的

在redis的键值的"值"中,它所支持的数据结构有:

1. String
    1) 常用命令
      setget、decr、incr、mget等
      2) 应用场景
      String是最常用的一种数据类型,普通的key/value存储都可以归为此类 
      3) 实现方式
      String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr、decr等操作时会转成数值型进行计算,此时redisObject的encoding字段为int

2. Hash
    1) 常用命令
      hget、hset、hgetall等
      2) 应用场景
    3) 实现方式
      Redis Hash对应Value内部实际就是一个HashMap,实际这里会有2种不同实现
        3.1) 这个Hash的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,对应的value redisObject的encoding为zipmap
        3.2) 当成员数量增大时会自动转成真正的HashMap,此时encoding为ht

3. List
    1) 常用命令
      lpush、rpush、lpop、rpop、lrange等
      2) 应用场景
      Redis list的应用场景非常多,也是Redis最重要的数据结构之一,比如twitter的关注列表,粉丝列表等都可以用Redis的list结构来实现 
      3) 实现方式
      Redis list的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用的这个数据结构

4. Set
    1) 常用命令
      sadd、spop、smembers、sunion等 
      2) 应用场景
      Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集
合内的重要接口,这个也是list所不能提供的   
3) 实现方式:    set的内部实现是一个value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因 5. Sorted set 1) 常用命令    zadd、zrange、zrem、zcard等    2) 使用场景    Redis sorted set的使用场景与set类似,区别是set不是自动有序的,而sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。当你需要一个有序的并且不重复的集
合列表,那么可以选择sorted set数据结构,比如twitter 的public timeline可以以发表时间作为score来存储,这样获取时就是自动按时间排好序的   
3) 实现方式    Redis sorted set的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获
得比较高的查找效率,并且在实现上比较简单

Redis内部使用一个redisObject对象来表示所有的key和value

1. type
代表一个value对象具体是何种数据类型
2. encoding
不同数据类型在redis内部的存储方式,比如:type=string代表value存储的是一个普通字符串,那么对应的encoding可以是raw或者是int,如果是int则代表实际redis内部是按数值型类存储和表示这个字符串的,当然前提是这个
字符串本身可以用数值表示,比如:"123" "456"这样的字符串 3. ptr 数据指针 4. vm 只有打开了Redis的虚拟内存功能,此字段才会真正的分配内存,该功能默认是关闭状态的

我们可以发现Redis使用redisObject来表示所有的key/value数据是比较浪费内存的,当然这些内存管理成本的付出主要也是为了给Redis不同数据类型提供一个统一的管理接口

0x2: Redis支持的指令集

Redis提供了丰富的命令(command)对数据库和各种数据类型进行操作,这些command可以在Linux终端使用(redis-cli)。也可以在编程时通过API方式使用,比如使用Redis 的Java语言包

1. 连接操作相关的命令
    1) QUIT: 关闭连接(connection)
    2) AUTH: 简单密码认证

2. 适合全体类型的命令
    1) EXISTS(key) 确认一个 key 是否存在 
    2) DEL(key) 删除一个 key 
    3) TYPE(key) 返回值的类型 
    4) KEYS(pattern) 返回满足给定 pattern 的所有 key 
    5) RANDOMKEY:随机返回 key 空间的一个key 
    6) RENAME(oldname, newname) 将 key 由 oldname 重命名为 newname,若 newname 存在则删除 newname 表示的 key 
    7) DBSIZE:返回当前数据库中 key 的数目 
    8) EXPIRE(key,ttl) 设定一个 key 的生存时间 ttl(s) 
    9) TTL(key) 获得一个 key 的活动时间 
    10) SELECT(index) 按索引查询;
    11) MOVE(key, dbindex) 将当前数据库中的 key 转移到有 dbindex 索引的数据库 
    12) FLUSHDB:删除当前选择数据库中的所有 key 
    13) FLUSHALL:删除所有数据库中的所有 key

3. 自定义扩展指令相关的指令
    1) EVAL: EVAL script numkeys key [key ...] arg [arg ...]
    EVAL 和 EVALSHA 命令是从 Redis 2.6.0 版本开始的,使用内置的 Lua 解释器,可以对 Lua 脚本进行求值

3、对 STRING 操作的命令
    1) SET(key, value) 给数据库中名称为 key 的 string 赋予值 value 
    2) GET(key) 返回数据库中名称为 key 的 string 的 value 
    3) GETSET(key, value) 给名称为 key 的 string 赋予上一次的value 
    4) MGET(key1, key2,…, key{$n}) 返回库中多个 string(它们的名称为key1,key2...)的value 
    5) SETNX(key, value) 如果不存在名称为 key 的 string,则向库中添加 string,名称为 key,值为 value;
    6) SETEX(key, time, value) 向库中添加 string(名称为key,值为value)同时,设定过期时间time;
    7) MSET(key1, value1, key2, value2,…key{$n}, value{$n}) 同时给多个 string 赋值,名称为 key{$i} 的 string 赋值 value{$i};
    8) MSETNX(key1, value1, key2, value2,…key{$n}, value{$n}) 如果所有名称为 key{$i} 的 string 都不存在,则向库中添加 string,名称 key{$i} 赋值为 value{$i};
    9) INCR(key) 名称为 key 的 string 增1操作;
    10) INCRBY(key, integer) 名称为 key 的 string 增加 integer;
    11) DECR(key) 名称为 key 的 string 减1操作;
    12) DECRBY(key, integer) 名称为 key 的 string 减少 integer;
    13) APPEND(key, value) 名称为 key的 string 的值附加 value;
    14) SUBSTR(key, start, end) 返回名称为 key 的 string 的 value 的子串

4、对无索引序列 LIST 操作的命令
    1) RPUSH(key, value) 在名称为 key 的 list 尾添加一个值为 value 的元素;
    2) LPUSH(key, value) 在名称为 key 的 list 头添加一个值为 value 的 元素;
    3) LLEN(key) 返回名称为 key 的 list 的长度;
    4) LRANGE(key, start, end) 返回名称为 key 的 list 中 start 至 end 之间的元素(下标从0开始,下同)
    5) LTRIM(key, start, end) 截取名称为 key 的 list,保留 start 至 end 之间的元素;
    6) LINDEX(key, index) 返回名称为 key 的 list 中 index 位置的元素;
    7) LSET(key, index, value) 给名称为 key 的 list 中 index 位置的元素赋值为 value;
    8) LREM(key, count, value) 删除 count 个名称为 key 的 list 中值为value的元素。count 为0,删除所有值为 value 的元素,count>0从 头至尾删除 count 个值为 value 的元素,count<0从尾到头删除|count|个值为value的元素;
    9) LPOP(key) 返回并删除名称为key的list中的首元素;
    10) RPOP(key) 返回并删除名称为key的list中的尾元素;
    11) BLPOP(key1, key2,… key{$n}, timeout) LPOP 命令的 block 版本。即当 timeout 为0时,若遇到名称为 key{$i} 的 list 不存在或该 list 为空,则命令结束。如果 timeout>0,则遇到上述情况时,等待 timeout 秒,如果问题没有解决,则对 key{$i}+1 开始的 list 执行 pop 操作;
    12) BRPOP(key1, key2,… key{$n}, timeout) RPOP 的 block 版本。参考上一命令;
    13) RPOPLPUSH(srckey, dstkey) 返回并删除名称为 srckey 的 list 的尾元素,并将该元素添加到名称为 dstkey 的 list 的头部。

5、对有索引无序集合 SET 操作的命令
    1) SADD(key, member) 向名称为 key 的 set 中添加元素 member;
    2) SREM(key, member) 删除名称为 key 的 set 中的元素 member;
    3) SPOP(key) 随机返回并删除名称为 key 的 set 中一个元素;
    4) SMOVE(srckey, dstkey, member) 将 member 元素从名称为 srckey 的集合移到名称为 dstkey 的集合;
    5) SCARD(key) 返回名称为 key 的 set 的基数;
    6) SISMEMBER(key, member) 测试 member 是否是名称为 key 的 set 的元素;
    7) SINTER(key1, key2,…key{$n}) 求交集;
    8) SINTERSTORE(dstkey, key1, key2,…key{$n}) 求交集并将交集保存到 dstkey 的集合;
    9) SUNION(key1, key2,…key{$n}) 求并集;
    10) SUNIONSTORE(dstkey, key1, key2,…key{$n}) 求并集并将并集保存到 dstkey 的集合;
    11) SDIFF(key1, key2,…key{$n}) 求差集;
    12) SDIFFSTORE(dstkey, key1, key2,…key{$n}) 求差集并将差集保存到 dstkey 的集合;
    13) SMEMBERS(key) 返回名称为 key 的 set 的所有元素;
    14) SRANDMEMBER(key) 随机返回名称为 key 的 set 的一个元素。
 
6、持久化
    1) SAVE:将数据同步保存到磁盘;
    2) BGSAVE:将数据异步保存到磁盘;
    3) LASTSAVE:返回上次成功将数据保存到磁盘的 UNIX 时戳;
    4) SHUNDOWN:将数据同步保存到磁盘,然后关闭服务

7、远程服务控制
    1) INFO:提供服务器的信息和统计;
    2) MONITOR:实时转储收到的请求;
    3) SLAVEOF:改变复制策略设置;
    4) CONFIG:在运行时配置 Redis 服务器 

0x3: 配置密码登录

关于密码验证

1. 如果redis监听回环IP之外的地址 任何人都可以读取其信息,所以安全问题需要考虑,即redis是默认未授权可任意访问的
2. 即使显式设置了密码验证,但是redis服务器的login响应速度极快,因此官方文件中提醒设置比较复杂的密码,防止机器破解

redis配置密码

1. vim /etc/redis.conf
2. requirepass littlehann 
//littlehann是设置的密码
3. 重启redis
sudo service redis restart  
#或者  
sudo service redis stop  
sudo redis-server /etc/redis.conf  

4. 尝试登录redis,在没有进行身份认证的情况下
(error) ERR operation not permitted

5. 进行身份认证
redis-cli -h 127.0.0.1 -p 6379 -a littlehann  
#或者
redis-cli 
auth littlehann

除了使用这种修改配置文件并重启的方式进行redis密码配置之外,还可以通过指令,动态地对redis进行密码配置(重启后失效)

1. config set requirepass my_redis  
2. config get requirepass  
//无需重启redis,之前配置的老密码会失效(只在本次启动声明周期内有效) 

Relevant Link:

http://try.redis.io/
http://www.redis.cn/
http://jandyu.diandian.com/post/2012-03-15/16145594
http://tech.it168.com/a2011/0818/1234/000001234478_all.shtml
http://hedatou.com/archives/introduction_to_redis.html
http://blog.csdn.net/tianmohust/article/details/7739739
http://www.redis.cn/commands.html
http://www.lvtao.net/content/book/redis.htm 
http://blog.csdn.net/zyz511919766/article/details/4226821

 

2. Redis安装配置

0x1: 主程序安装

cd /usr/local
wget http://download.redis.io/releases/redis-2.8.13.tar.gz
tar xzf redis-2.8.13.tar.gz
cd redis-2.8.13
make
/*make命令执行完成后,会在当前src目录(/usr/local/redis-2.8.13/src)下生成本个可执行文件如下:
1. redis-server:Redis服务器的daemon启动程序
2. redis-cli:Redis命令行操作工具。当然,你也可以用telnet根据其纯文本协议来操作
3. redis-benchmark:Redis性能测试工具,测试Redis在你的系统及你的配置下的读写性能
4. redis-stat:Redis状态检测工具,可以检测Redis当前状态参数及延迟状况 
*/

0x2: 命令测试

//启动server
./redis-server
//测试benchmark
./redis-benchmark
//使用内置的客户端连接Redis
./redis-cli
127.0.0.1:6379> set foo bar
OK
127.0.0.1:6379> get foo
"bar"
127.0.0.1:6379>

0x3: 运行Redis所需要的内核参数优化

/*
1. overcommit_memory
指定了内核针对内存分配的策略,其值可以是0、1、2。
0: 表示内核将检查是否有足够的可用内存供应用进程使用
    1) 如果有足够的可用内存,内存申请允许
    2) 否则,内存申请失败,并把错误返回给应用进程
1: 表示内核允许分配所有的物理内存,而不管当前的内存状态如何
2: 表示内核允许分配超过所有物理内存和交换空间总和的内存
*/
vim /etc/sysctl.conf
//添加
vm.overcommit_memory=1
//刷新配置使之生效
sysctl vm.overcommit_memory=1 

0x4: Redis配置文件

vim /usr/local/redis-2.8.13/redis.conf
//1. 开启守护进程
daemonize yes 
//2. 每隔5秒输出一行监控信息(默认)
daemonize no

//3. 减小改变次数,这个参数可以根据情况进行指定
save 60 1000 

//4. 分配256M内存
maxmemory 256000000  
 
//5. pid文件位置
pidfile /var/run/redis.pid 

//6. 监听的端口号
port 6379

//7. 请求超时时间
timeout 0

//8. log信息级别
loglevel notice

//9. 开启数据库的数量
databases 16

/*
10. 保存快照的频率
    1) 第一个*表示多长时间
    2) 第二个*表示执行多少次写操作
在一定时间内执行一定数量的写操作时,自动保存快照。可设置多个条件
*/
save * *

//11. 是否使用压缩
rdbcompression yese

//12. 数据快照文件名(只是文件名,不包括目录)
dbfilename dump.rdb

//13. 数据快照的保存目录(这个是目录)
dir ./

//14. 是否开启appendonlylog,开启的话每次写操作会记一条log,这会提高数据抗风险能力,但影响效率
appendonly no

/*
15. appendonlylog如何同步到磁盘
    1) always: 每次写都强制调用fsync
    2) everysec: 每秒启用一次fsync
    3) no: 不调用fsync等待系统自己同步
*/ 
appendfsync everysec

配置好保存,重启redis,就可以正常启动了,和mysql一样,redis是基于socket监听端口的方式提供服务的,我们可以使用telnet、或者socket方式进行连接

Relevant Link:

http://www.redis.cn/documentation.html
http://www.redis.cn/download.html
http://www.php100.com/html/webkaifa/PHP/PHPyingyong/2011/0406/7873.html

 

3. 编程使用Redis

0x1: PHP连接Redis

使用php连接redis需要安装php的扩展

下载redis扩展源代码
http://pecl.php.net/package/redis
解压缩后进行编译
phpize
./configure --enable-hello
make
关于php扩展的原理以及编译过程请参阅另一篇文章
http://www.cnblogs.com/LittleHann/p/3562259.html
将编译好的.so文件复制到php的扩展目录中
cp redis.so /usr/lib/php/modules/
修改php.ini中的extension,增加redis扩展的自动启动
重启apache即可

Code

<?php  
    $redis = new redis();  
    $result = $redis->connect('192.168.207.128', 6379);  
    var_dump($result); //结果:bool(true)  

    $result = $redis->set('name',"LittleHann");  
    var_dump($result); //结果:bool(true)  

    $result = $redis->get('name'); 
    var_dump($result); //结果:LittleHann 

    $redis->delete('name');  
    var_dump($result); //结果:bool(true)  
?>  

Relevant Link:

https://github.com/nrk/predis
https://github.com/Shumkov/Rediska
https://github.com/jdp/redisent
http://www.cnblogs.com/ikodota/archive/2012/03/05/php_redis_cn.html
http://blog.51yip.com/cache/1439.html
http://www.cnblogs.com/jackluo/p/3412670.html

0x2: Java连接Redis

Relevant Link:

http://outofmemory.cn/code-snippet/128/java-usage-redis-jiandan-usage
http://www.cnblogs.com/edisonfeng/p/3571870.html

 

4. 使用Lua脚本(Redis将lua引擎静态编译包含使redis具备解析lua脚本的能力)

0x1: 初始化 Lua 环境

在初始化 Redis 服务器时, 对 Lua 环境的初始化也会一并进行,为了让 Lua 环境符合 Redis 脚本功能的需求, Redis 对 Lua 环境进行了一系列的修改, 包括添加函数库、更换随机函数、保护全局变量, 等等
整个初始化 Lua 环境的步骤如下

1. 调用 lua_open 函数,创建一个新的 Lua 环境 
2. 载入指定的 Lua 函数库,包括:
    1) 基础库(base lib)
    2) 表格库(table lib)
    3) 字符串库(string lib)
    4) 数学库(math lib)
    5) 调试库(debug lib)
    6) 用于处理 JSON 对象的 cjson 库
    7) 在 Lua 值和 C 结构(struct)之间进行转换的 struct8) 处理 MessagePack 数据的 cmsgpack 库 
3. 屏蔽一些可能对 Lua 环境产生安全问题的函数,比如 loadfile
4. 创建一个 Redis 字典,保存 Lua 脚本,并在复制(replication)脚本时使用。字典的键为 SHA1 校验和,字典的值为 Lua 脚本 
5. 创建一个 redis 全局表格到 Lua 环境,表格中包含了各种对 Redis 进行操作的函数,包括 
    1) 用于执行 Redis 命令的 redis.call 和 redis.pcall 函数 
    2) 用于发送日志(log)的 redis.log 函数,以及相应的日志级别(level)
        redis.LOG_DEBUG
        redis.LOG_VERBOSE
        redis.LOG_NOTICE
        redis.LOG_WARNING
    3) 用于计算 SHA1 校验和的 redis.sha1hex 函数 
    4) 用于返回错误信息的 redis.error_reply 函数和 redis.status_reply 函数 
6. 用 Redis 自己定义的随机生成函数,替换 math 表原有的 math.random 函数和 math.randomseed 函数,新的函数具有这样的性质:每次执行 Lua 脚本时,除非显式地调用 math.randomseed ,否则 math.random 生成的伪随机数序列总是相同的 
7. 创建一个对 Redis 多批量回复(multi bulk reply)进行排序的辅助函数 
8. 对 Lua 环境中的全局变量进行保护,以免被传入的脚本修改 
9. 因为 Redis 命令必须通过客户端来执行,所以需要在服务器状态中创建一个无网络连接的伪客户端(fake client),专门用于执行 Lua 脚本中包含的 Redis 命令:当 Lua 脚本需要执行 Redis 命令时,它通过伪客户端来向服务器发送命令请求,服务器在执行完命令之后,将结果返回给伪客户端,而伪客户端又转而将命令结果返回给 Lua 脚本 
10. 将 Lua 环境的指针记录到 Redis 服务器的全局状态中,等候 Redis 的调用 

这些步骤都执行完之后, Redis 就可以使用 Lua 环境来处理脚本了

0x2: 脚本的安全性

Redis 对 Lua 环境所能执行的脚本做了一个严格的限制 —— 所有脚本都必须是无副作用的纯函数(pure function)
为此,Redis 对 Lua 环境做了一些列相应的措施

1. 不提供访问系统状态状态的库(比如系统时间库)
2. 禁止使用 loadfile 函数
3. 如果脚本在执行带有随机性质的命令(比如 RANDOMKEY ),或者带有副作用的命令(比如 TIME )之后,试图执行一个写入命令(比如 SET ),那么 Redis 将阻止这个脚本继续运行,并返回一个错误 
4. 如果脚本执行了带有随机性质的读命令(比如 SMEMBERS ),那么在脚本的输出返回给 Redis 之前,会先被执行一个自动的字典序排序,从而确保输出结果是有序的 
5. 用 Redis 自己定义的随机生成函数,替换 Lua 环境中 math 表原有的 math.random 函数和 math.randomseed 函数,新的函数具有这样的性质:每次执行 Lua 脚本时,除非显式地调用 math.randomseed ,否则 math.random 生成的伪随机数序列总是相同的 

经过这一系列的调整之后, Redis 可以保证被执行的脚本

1. 无副作用
2. 没有有害的随机性
3. 对于同样的输入参数和数据集,总是产生相同的写入命令

0x3: 脚本的执行

在脚本环境的初始化工作完成以后, Redis 就可以通过 EVAL 命令或 EVALSHA 命令执行 Lua 脚本了

1. EVAL(对输入的脚本代码体进行求值)

EVAL "return 'hello world'" 0

由于redis替换了lua中的基本库全局变量,产生了sandbox的效果,所以很多提权操作是不允许的

eval "return io.popen('ifconfig')" 0

2. EVALSHA 则要求输入某个脚本的 SHA1 校验和, 这个校验和所对应的脚本必须至少被 EVAL 执行过一次

Relevant Link:

http://www.redis.cn/commands/eval.html
http://origin.redisbook.com/feature/scripting.html

 

Copyright (c) 2014 LittleHann All rights reserved

 

posted @ 2014-08-09 21:04  郑瀚Andrew  阅读(1111)  评论(0编辑  收藏  举报