手记

Redis学习笔记,并开发简易博客

这篇学习笔记来自学习 《Redis 入门指南》的过程,花几天时间在空闲时候钻研一门技术,亦或是拜读一些经典,给自己充一充电。为什么要学 Redis,因为它火啊......自己平时开发的项目都比较小,大部分都是外包项目,所以基本用不到缓存系统这一层面的技术,接触不到机会那就自己给自己创造机会。更何况现在开源运动的士气这么高涨,想要学习一门新技术,做一个开源项目(就算思路是别人的,那也没多大事,重点是实战了)。刚好书上也是按照一个故事链进行的,书中一个老师指导一个热爱学习的小伙子一步一步搭建用 Redis 作为数据库的博客系统。我也就紧跟着书中老师的步伐搭建了一个博客

安装
wget http://download.redis.io/redis-stable.tar.gz
tar xzf redis-stable.tar.gz
cd redis-stable
make
# 将可执行程序复制到/usr/local/bin目录中以便以后执行程序时可以不用输入完整的路径
make install
# 测试Redis是否编译正确
make test

使用 make test 命令时提示 You need tcl 8.5 or newer in order to run the Redis test,解决方案如下:

wget http://downloads.sourceforge.net/tcl/tcl8.6.1-src.tar.gz
tar xzvf tcl8.6.1-src.tar.gz  -C /usr/local/
cd  /usr/local/tcl8.6.1/unix/
./configure
make
make install
启动 Redis

直接启动

redis-server
# Redis服务器默认使用6379端口,通过--port参数可以自定义端口号
redis-server --port 6380

初始化脚本启动 Redis (推荐使用)

配置初始化脚本

将 Redis 源代码目录下的 utils 文件夹中的 redis_init_script 这个初始化脚本文件复制到 /etc/init.d 目录下,取名为 redis_6379,端口号可以改成自己想要的,只需修改脚本第6行 redisport 变量的值为同样的端口号即可。

建立文件夹

/etc/redis 和 /var/redis/6379,前者存放配置文件,后者存放持久化文件。

修改配置文件

将 Redis 源代码目录下的 redis.conf 复制到 /etc/redis 目录中,以端口号命名(如 6379.conf )。将配置项如下表修改:

配置项 说明
daemonize yes 使Redis以守护进程模式运行
pidfile /var/run/redis_6379.pid 设置Redis的PID文件位置
port 6379 设置Reids监听的端口号
dir /var/redis/6379 设置持久化文件存放位置

现在就可以使用 /etc/init.d/redis_6379 start 来启动 Redis 了。

停止 Redis
redis-cli SHUTDOWN
实战

Redis 不区分命令大小写,但和 Mysql 一样,建议使用大写字母表示命令。下面是对键的一些简单操作:

# 列出符合表达式的键名列表,pattern支持glob风格通配符格式
KEYS pattern

# 设置键为 "bar" 值为 "1" 的键值对
SET bar 1

# 取出键为 "bar" 的值
GET bar

# 判断一个键是否存在。存在返回1,否则返回0
EXISTS key

# 删除键。可以一次删除多个键,返回删除的键个数
DEL key [key ...]

# 删除所有符合规则的键
redis-cli KEYS "sc_*" | xargs redis-cli DEL

# 获得键值的数据类型
TYPE key

glob风格通配符格式如下表:

符号 含义
? 匹配一个字符
* 匹配任意个(包含0个)字符
[] 匹配括号间的任一符号,可以使用 "-" 符号表示一个范围,如[a-c]可以匹配"a","b","c"
\x 匹配字符x,用于转义符号。如要匹配 "?" ,就需要使用 \?

字符串类型

Redis 最基本的数据类型,可以存储二进制数据、JSON 化的对象(比如图片),一个字符串类型的键允许存储数据的最大容量为512M。

# 增加指定的整数,key为键,increment为增加的步进值
INCRBY key increment

# 减少指定的整数,参数同INCRBY
DECRBY key increment

# 增加指定浮点数,参数同INCRBY
INCRBYFLOAT key incremnet

# 向尾部追加值,key为键,value为追加的值。如果键不存在则将该键的值设置为value。返回值是追加后字符串的总长度
APPEND key value

# 获取字符串长度,对于中文UTF-8编码,每一个中文长度是3
STRLEN key

# 同事获得/设置多个键值
MGET key [key ...]
MSET key value [key value ...]

散列类型

散列类型适合存储对象:使用对象类别和 ID 构成键名,使用字段表示对象的属性,而字段值则存储属性值。

# 散列类型的操作前缀带H

# 单个赋值
HSET key field value

# 单个取值
HGET key

# 多个赋值
HMSET key field value [field value ...]

# 多个取值
HMGET key field [field ...]

# 取全部值
HGETALL key

# 判断field字段是否存在
HEXISTS key field

# 当field字段不存在时赋值
HSETNX key field value

# 增加数字,如果key不存在则会自动建立并默认field字段为0,散列类型没有HINCR命令
HINCRBY key field incremnet

# 删除field字段
HDEL key field [field ...]

# 获取字段名/值
HKEYS key
HVALS key

# 获取字段数量
HLEN key

HSET 命令不区分插入和更新操作。当执行的是插入操作返回 1,执行的是更新操作返回 0。

列表类型

# 向列表两端增加元素
# 补充说明:当添加多个值时
# eg. LPUSH news n1 n2 n3
# 先将n1增加到news列表中,然后是n2,n3,所以最后news:[n3 n2 n1 ...],因此当使用LRANGE输出全部值时就会变成n3在最开始
LPUSH key value [value ...]
RPUSH key value [value ...]

# 向列表两端弹出元素
LPOP key
RPOP key

# 获取列表中元素的个数
LLEN key

# 获得列表片段,返回索引从start到stop之间的所有元素(包括两端的元素),也支持负索引
# 补充说明:
# 1. 如果start的索引位置比stop的索引位置靠后,则会返回空列表 
# 2. 如果stop大于实际的索引范围,则会返回到列表最右边的元素
LRANGE key start stop
# eg.返回列表mylist中的所有元素
LRANGE mylist 0 -1

# 删除列表中指定的值,删除列表中前count个值为value的元素,返回值是实际删除的元素个数
# 补充说明:
# 1. 当 count > 0 时,LREM会从列表左边开始删除
# 2. 当 count < 0 时,LREM会从列表右边开始删除
# 3. 当 count = 0 时,LREM会删除所有值为value的元素
LREM key count value

# 获得/设置指定索引的元素值
LINDEX key index
LSET key index value

# 只保留列表指定片段
LTRIM key start end
# 补充说明:
# LTRIM常和LPUSH一起使用来限制列表中元素的数量,比如记录日志时只保留最近的100条日志
LPUSH logs newlog
LTRIM logs 0 99

# 向列表中插入元素,首先从左到右查找值为value1的元素,然后根据BEFORE或AFTER来决定将value2插入到该元素的前或后
LINSERT key BEFORE|AFTER value1 value2

# 将一个列表右边的元素转到另一个列表左边
RPOPLPUSH source destination

集合类型

# 增加/删除元素
# 补充说明:添加时若不存在该键则会自动创建。因为在集合中不能有相同的元素,所以如果元素已存在则会忽略。返回值时成功加入的元素数量
SADD key member [member ...]
SREM key member [member ...]

# 获得所有元素
SMEMBERS key

# 判断元素是否在集合中
SISMEMBER key member

# 集合间运算:差集、交集、并集
SDIFF key [key ...]
SINTER key [key ...]
SUNION key [key ...]

# 获得元素个数
SCARD key

# 进行集合运算并将结果存储,将运算的结果存储在destination键中,常用于多步集合运算的场景中
SDIFFSTORE destination key [key ...]
SINTERSTORE destination key [key ...]
SUNIONSTORE destination key [key ...]

# 随机获得集合中的元素
# 补充说明:
# 1. 当 count > 0 时,随机从集合中获得count个不重复的元素,如果count的值大于集合中的元素个数,则返回全部元素
# 2. 当 count  < 0 时,随机从集合里获得|count|个有可能重复的元素
SRANDMEMBER key [count]

有序集合类型

# 增加元素,如果元素已经存在则会用新的分数替换原有的分数,返回值时新价格的元素个数,分数不仅可以是整数,还支持双精度浮点数
# eg.
# -inf 表示负无穷 +inf 表示正无穷
# ZADD test -inf a
# ZADD test +inf b
ZADD key score member [score member ...]

# 获得元素的分数
ZSCORE key member

# 获得排名在某个范围的元素列表,可选参数 WITHSCORES 表示连带分数也一起输出
ZRANGE key start stop [WITHSCORES]
ZREVRANGE key start stop [WITHSCORES]

# 获得指定分数范围的元素
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
# 补充说明:
# 1. 如果希望分数范围不包含端点值,可以再分数前加上 “(” 符号。eg. ZRANGEBYSCORE test 60 (100
# 2. min 和 max 支持无穷大(-inf +inf)。eg. ZRANGEBYSCORE test (60 +inf
# 3. LIMIT offset count 与SQL中的用法基本相同

# 增加/减少某个元素的分数,返回值是更改后的分数
# eg. 给Jerry加4分:ZINCRBY test 4 Jerry
# eg. 给Jerry减4分:ZINCRBY test -4 Jerry
ZINCRBY key incremnet member

# 获得集合中元素的数量
ZCARD key

# 获得指定分数范围内的元素个数,min max同样支持无穷大
ZCOUNT key min max

# 按照排名范围删除元素,返回删除的元素数量
ZREMRANGEBYRANK key start stop

# 按照分数范围删除元素,返回删除的元素数量
ZREMRANGEBYSCORE key min max

# 获得元素的排名
ZRANK key member
ZREVRANK key member

# 计算有序集合的交集,destinade键中元素的分数是由AGGREGATE参数决定的
# 1. 当AGGREGATE时SUM(也就是默认值),destination键中元素的分数是每个参与计算的集合中该元素分数的和
# 2. 当AGGREGATE时MIN,destination键中元素的分数是每个参与计算的集合中最小的元素分数
# 3. 当AGGREGATE时MAX,destination键中元素的分数是每个参与计算的集合中最大的元素分数
# eg.
# ZADD ss1 1 a 2 b
# ZADD ss2 9 a 8 b
# ZINTERSTORE res 2 ss1 ss2 AGGREGATE SUM
# ZRANGE res 0 -1 WITHSCORES
# 结果:a 10 b 10
# 4. WEIGHTS参数可以设置每个集合的权重,在参与计算时元素的分数会被乘上该集合的权重
# eg,
# ZINTERSTORE res1 2 ss1 ss2 WEIGHTS 0.8 0.5
# 结果:a 8 b 5
ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
事务

一组命令的集合,同命令一样都是最小执行单位,一个事务中的命令要么都执行,要么多不执行。

redis> MULTI
OK
redis> SADD "user:1:following" 2
QUEUED
redis> SADD "user:2:followers" 1
QUEUED
redis> EXEC
1) (integer) 1
2) (integer) 1

首先使用 MULTI 命令告诉 Redis 下列的命令是属于同一个事务的;接下来执行的所有命令返回 QUEUED 表示已经进入等待执行的事务队列中;使用 EXEC 告诉 Redis 将等待执行的事务队列中的所有命令按照发送顺序依次执行。如果在发送 EXEC 命令前客户端断线,则 Redis 会清空事务队列。

事务的错误处理

语法错误

只要有一个命令有语法错误,执行 EXEC 后 Redis 就会直接返回错误,其他正确的命令也不会执行。

运行错误

比如使用散列类型的命令操作集合类型的键,这种情况下,其他的命令依然会继续执行。由于 Redis 不支持回滚功能,也使得 Redis 在事务上可以保持简洁和快速。上述两种错误,语法错误完全可以在开发时找出并解决,运行错误可以通过良好的规划数据库来规避。

# 监控一个或多个键,一旦其中有一个键被修改,之后的第一个事务就不会执行。监控一直持续到 EXEC 命令(因为事务中的命令是在 EXEC 之后才执行的,所以在 MULTI 命令之后 EXEC 命令之前是有可以修改监控的键的值),执行 EXEC 命令后会取消对所有键的监控
WATCH key [key ...]
# 取消监控,保证下一个事务的执行不会受到影响
UNWATCH key [key ...]

# 举例一:开始监听name后,在执行第一个事务前对name赋值,导致该事务不会执行,而第二个事务正常执行。也因此由于监控键的值在事务执行前被修改而导致失败后仍需再次执行,保证该事务的业务逻辑操作得到执行
redis> SET name koro
OK
redis> WATCH name
OK
redis> SET name sara
OK
redis> MULTI
OK
redis> SET name mike
QUEUED
redis> EXEC
(nil)
redis> GET name
"sara"
redis> MULTI
OK
redis> SET name lara
QUEUED
redis> EXEC
1) OK
redis> GET name
"lara"
生存时间

在 Redis 中可以使用 EXPIRE 命令设置一个键的生存时间,到时间后 Redis 会自动删除它。

# 前者时间单位为秒,后者时间单位为毫秒。返回1表示设置成功,返回0表示键不存在或设置失败
EXPIRE key seconds
PEXPIRE key millisecond

# 查看指定键剩余生存时间(单位秒),返回-1表示键不存在或永久存在(设置一个键时默认为永久存在)
TTL key

# 取消键的生存时间设置,即将键恢复成永久的。返回1表示成功清除,返回0表示键不存在或已经是永久的
# 特别说明:使用 SET 或 GETSET 命令为键赋值也会同时清除键的生存时间;如果使用 WATCH 命令检测了一个拥有生存时间的键,该键时间到期自动删除并不会被 WATCH 命令认为该键被改变
PERSIST key
淘汰键策略

当服务器内存有限时,如果大量地使用缓存键且生存时间设置得过长就会导致 Redis 占满内存;另一方面如果为了防止 Redis 占用内存过大而将缓存键的生存时间设得太短,就可能导致缓存命中率过低并且大量内存白白地闲置。实际开发中会发现很难为缓存键设置合理的生存时间,为此可以闲置 Redis 能够使用的最大内存,并让 Redis 按照一定的规则淘汰不需要的缓存键,这种方式在只将 Redis 用作缓存系统时非常实用。
设置方法:修改配置文件的 maxmemory 参数,限制 Redis 最大可用内存大小(单位是字节),当超出了这个限制时 Redis 会依据 maxmemory-policy 参数指定的策略来删除不需要的键,直到 Redis 占用的内存小于指定内存。

规则 说明
volatile-lru 使用LRU(最近最少)算法删除一个键(只对设置了生存时间的键)
allkeys-lru 使用LRU(最近最少)算法删除一个键
volatile-random 随机删除一个键(只对设置了生存时间的键)
allkeys-random 随机删除一个键
volatile-ttl 删除生存时间最近的一个键
noeviction 不删除键,只返回错误
排序

认识 SORT 命令

可以对列表类型、集合类型和有序集合类型键进行排序,并且可以完成与关系数据库中的连接查询相类似的任务。

  • 对数字进行排序
  • 对非数字进行排序,需要加上 ALPHA 参数
  • 默认为从小到大排序,需要从大到小排序使用 DESC 参数
  • 需要分页显示可用 LIMIT 参数

    认识 LIMIT 参数

    LIMIT offset count(跳过前 offset 个元素并获取之后的 count 个元素)
    举例:SORT tag_php:articles DESC LIMIT 1 2

    认识 BY 参数

  • 语法为 BY 参考键 ,其中参考键可以使字符串类型键或者是散列类型键的某个字段。如果提供了 BY 参数,SORT 命令将依据参考键的值来排序
  • 举例(散列类型):SORT tag_php:articles BY article:*->time DESC
  • 举例(字符串类型):SORT articles BY articles:* DESC

    认识 GET 参数

  • 该参数不影响排序,是使 SORT 命令的返回结果不再是元素自身的值,而是 GET 参数中指定的键值。支持字符串类型和散列类型的键,并使用 * 作为占位符。可以有多个 GET 参数
  • 举例(实现在排序后直接返回 ID 对应的文章标题):SORT tag_php:articles BY article:*->time DESC GET article:*->title
  • GET # 可以获取元素本身的值

    认识 STORE 参数

    将结果保存到指定键中

    性能优化

  • 尽可能减少待排序键中元素的数量
  • 使用 LIMIT 参数值获取需要的数据
  • 如果要排序的数据量较大,尽可能使用 STORE 参数将结果缓存
任务队列

当页面需要进行如发送邮件、复杂数据运算等耗时较长的操作时会阻塞页面的渲染。为了避免用户等待太久,应该使用独立的线程来完成这类操作。不过一些编程语言或框架不易实现多线程,这时就可以通过其他进程来实现,那么只需要通知这个进程向指定的地址发送邮件就可以了。
通知的过程可以借助任务队列来实现。与任务队列进行交互的实体有两类:生产者(producer)和消费者(consumer)。生产者会将需要处理的任务放入任务队列中,而消费者则不断从任务队列中读入任务信息并执行
好处:

松耦合

生产者和消费者无需知道彼此的实现细节,只需约定好任务的描述格式。这使得生产者和消费者可以由不同的团队使用不同的编程语言编写。

可扩展性

消费者可以有多个,而且可以分布在不同的服务器中,可以轻易地降低单台服务器的负载。
简单示例(伪代码):

loop
    $task = BRPOP queue 0
    execute $task[1]

命令:BRPOP BLPOP

说明:当列表中没有元素时会一直阻塞住连接,直到有新元素加入,才会从队列中弹出一个元素。
参数:key timeout,第一个是键名,第二个是超时时间,单位是秒。当超过了此时间仍然没有获得新元素的话就会返回 nil。若为 0,表示不限制等待的时间,即一直会等待新元素加入。
详解:

  • 完整命令:BLPOP key [key ...] timeout
  • 说明:同时检测多个键,如果所有键都没有元素则阻塞,如果其中有一个键有元素则从该键中弹出元素。如果有多个键都有元素则按照 从左到右 的顺序取第一个键中的一个元素。

优先级队列

当需要对通知给同一个进程的不同任务进行优先级对待的时候,可以使用完整格式的 BLPOP 来实现。

“发布/订阅”模式:用到时再做分析

管道

通过管道可以一次性发送多条命令并在执行完后一次性将结果返回。管道通过减少客户端与 Redis 的通信次数来实现降低往返时延累计值的目的

节省空间

精简键名和键值:最直观的减少内存占用的方式,比如将 very.important.person:20 改成 VIP:20,将存储用户性别的字符串类型键的值改成 0 和 1。

践行

PHP 与 Redis:Redis官方推荐的PHP客户端是 Predisphpredis。前者是完全使用PHP代码实现的原生客户端,后者是使用 C 语言编写的 PHP 扩展。在功能上区别并不大,就性能而言后者更胜一筹。我选择的是后者 phpredis。

安装 Redis

phpize
./configure [--enable-redis-igbinary]
make && make install

添加 php-redis 扩展

修改你的 php.ini 文件或者在 /etc/php5/conf.d 中添加一个 redis.ini 文件,内容为 extension=redis.so

6人推荐
随时随地看视频
慕课网APP

热门评论

从redis的认识到redis集群搭建:http://www.roncoo.com/course/view/4f746bc8df3448b09caf30b267985292

不错哦,很全面,很实用

查看全部评论