本文翻译自 《An Introduction to Redis in PHP using Predis》,已得到作者 @Daniel Gafitescu 的允许。原文版权归作者和 sitepoint.com 所有,如果你有原文使用需求请自行联系作者。
Redis 是一个开源的内存数据库服务器,得益于内建的数据类型,Redis 不仅仅只能做简单的 key/value 存储。
Redis 由 @Salvatore Sanfilippo 在 2009 年发布,因为其受欢迎和增长迅速,被很多大公司比如 VMware(后来聘请作者去全职工作)、GitHub、Craigslist、Disqus、Digg、Blizzard、Instagram 等(详见:https://redis.io/)使用。
你可以使用 Redis 做会话(session)处理程序,当你使用负载均衡的分布式服务时特别有用。Redis 也可以用作发布/订阅系统,很优雅的创建一个在线聊天或者实时订购程序。关于 Redis 的文档,所有的命令以及其它信息都能在项目网站 redis.io 上找到。
一直以来都有 Redis 和 Memcache 哪个更好的争论,文章 as the benchmarks show 显示在相同的基础操作上两者不相上下。Redis 比 Memcache 有更多的特性,比如内存存储、磁盘持久化、原子操作、事务以及不用记录每一次变化到磁盘上而是用服务端的数据结构来代替。
在这篇文章中将介绍如何使用 Predis 库所提供的一部分基础但很有用的 Redis 命令。
目录
容易安装
Redis 容易安装,简明的安装说明发表在产品的下载页。从我个人经验来看,如果你运行在 Ubuntu 上而又没有安装 TCL(只需运行 sudo apt-get install tcl 即可安装)将会报错。一旦 Redis 安装完成,你可以运行服务:
1 2 | gafitescu@ubun2:~$ /usr/local/bin/redis-server * The server is now ready to accept connections on port 6379 |
在 Redis 的网站上显示的有很多语言可用的 Redis 客户端,每种语言都有好几个。对于 PHP 来说有 5 个。在本文中我使用的是 Predis 库,但是你可能也需要作为 PHP 模块编译、安装的 phpredis 扩展。
译者注:有些人可能在 Predis 库和 phpredis 扩展中难于选择,但两者在一般场景下相差不大,目前都支持 PHP7。phpredis 扩展在性能上可能有一些优势,而 Predis 库源码更加优雅(适合学习、阅读),支持 PHP 新的语法特征,但文档较少(这也是我翻译这篇文章的主要原因)。还有,Predis 也有扩展支持(但作者好像没精力维护了,目前还不支持 PHP7),用于提高性能和提供一下其它的特性。
如果你向我一样在机器上安装的有 Git,你只需克隆 Predis 仓库。否则你就要下载 ZIP 包然后解压。(译者注:可以使用更加简便的 composer 安装:composer require predis/predis)
1 | gafitescu@ubun2:~$ git clone git://github.com/nrk/predis.git |
测试一下,创建一个如下内容的 test.php 文件,测试是否能通过 Predis 连接上运行着的 Redis。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <?php require "predis/autoload.php"; PredisAutoloader::register(); // since we connect to default setting localhost // and 6379 port there is no need for extra // configuration. If not then you can specify the // scheme, host and port to connect as an array // to the constructor. try { $redis = new PredisClient(); /* $redis = new PredisClient(array( "scheme" => "tcp", "host" => "127.0.0.1", "port" => 6379)); */ echo "Successfully connected to Redis"; } catch (Exception $e) { echo "Couldn't connected to Redis"; echo $e->getMessage(); } |
当你运行这个脚本,你应该能如愿的看见(输出)信息:"Successfully connected to Redis"。
译者注:这里可能有点小问题,代码不会按照预期捕获 Redis 连接失败的异常,因为在初始化 Redis 客户端类的时候并没有真正的连接,而是在运行第一个命令时才做连接,所以需要额外运行一下 $redis->connect(); 来触发连接操作。详见:https://github.com/predis/predis/issues/61
使用 Redis
在这个章节你将了解众多 Redis 提供的命令的概况。Memcache 也有相似的命令,如果你熟悉 Memcache,这些列表对你来说看起来都不会陌生。
SET,GET,和 EXISTS
使用 Redis 最重要的命令要属 SET、GET 和 EXISTS 了。你能使用这些命令存储和检查那些经常被访问的、通常是键值对格式的临时信息。例:
1 2 3 4 5 6 | <?php $redis->set("hello_world", "Hi from php!"); $value = $redis->get("hello_world"); var_dump($value); echo ($redis->exists("Santa Claus")) ? "true" : "false"; |
set() 方法用来设置某个键的值,在本例中键是“hello_world”,值是“Hi from php!”。get() 方法(是用来)获取某个键的值,在本例中键同是“hello_word”。exists() 方法返回传入的键是否存在于 Redis 存储中。
key 值不受字母、数字字符和下划线限制。下面这个例子一样能很好的运行:
1 2 3 | <?php $redis->set("I 2 love Php!", "Also Redis now!"); $value = $redis->get("I 2 love Php!"); |
译者注:key 的命名不想 PHP 变量有一些限制,怎么样都行(二进制安全),但建议遵守团队内部已有的规范,比如使用「:」作命名空间分隔符,「#」做 id 部分的分隔符,还有 key 不能太长(节省内存)。更多:https://redis.io/docs/latest/develop/data-types/
INCR (INCRBY) 和 DECR (DECRBY)
INCR 和 DECR 命令用来增加和减少值,这是一个维持一个计数器的好方法。INCR 和 DECR 会自增/自减 1(译者注:类似 $i++ 和 $i--)。你可以使用 INCRBY 和 DECRBY 命令设置更大的(自增/自建)幅度。这里有一些例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?php // 译者注:文章阅读数计数器 // 比MySQL需要把字段拿出来+1然后存进去更加便捷,而且 Redis 能保证原子性 // increment the number of views by 1 for an article // with id 234 $redis->incr("article_views_234"); // increment views for article 237 by 5 $redis->incrby("article_views_237", 5); // decrement views for article 237 $redis->decr("article_views_237"); // decrement views for article 237 by 3 $redis->decrby("article_views_237", 3); |
Redis 数据结构
就像我早先提及的,Redis 有内建的数据结构。你可能会觉得奇怪像 Redis 这样的 NoSQL 键值对存储系统有数据结构,但是这对于开发者用有意义的方式构造信息是有益的,当数据有结构时执行特定的操作一般快很多。Redis 的数据结构有这些:
- String - Redis 中基础的数据结构,你可以用来存储几个字符到整个文件的内容。
- List - 一个按照插入的元素(先后)排序的简单字符串列表。你在列表的头部和尾部都可以增加和移除元素。
- Hash - 字符串键和字符串值的映射。使用这种方法,你可以表示对象(想一下一级深度的 JSON 对象)。
- Set - 你能增加、移除、测试一个元素是否存在的无序字符串集合。唯一的限制条件是不允许有重复的元素。
- Sorted set - 一个特定的集合数据结构。不同的是每个元素关联着一个叫score浮动数值(floating number value),这能很有用地将集合从小到大排序。
到目前为止我只展示了字符串操作,但还有一些一样容易使用,关于其它数据结构的操作指令。
HSET,HGET 和 HGETALL,HINCRBY 和 HDEL
这些指令用于 Redis 的 hash 数据结构。
- HSET - 设置 hash 对象中一个键的值。
- HGET - 获取 hash 对象中一个键的值。
- HINCRBY - 在 hash 对象中一个键的值的基础上增加特定的值。
- HDEL - 从 hash 对象中移除这个键(包括值)。
- HGETALL - 从 hash 对象中获取所有的键及(对应的)值。
下面是一个演示其用法的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <?php $redis->hset("taxi_car", "brand", "Toyota"); $redis->hset("taxi_car", "model", "Yaris"); $redis->hset("taxi_car", "license number", "RO-01-PHP"); $redis->hset("taxi_car", "year of fabrication", 2010); $redis->hset("taxi_car", "nr_starts", 0); // 译者注:hmset - 批量设置 hash 数据结构中字段(key)的值。 /* $redis->hmset("taxi_car", array( "brand" => "Toyota", "model" => "Yaris", "license number" => "RO-01-PHP", "year of fabrication" => 2010, "nr_stats" => 0) ); */ echo "License number: " . $redis->hget("taxi_car", "license number") . "<br>"; // remove license number $redis->hdel("taxi_car", "license number"); // increment number of starts $redis->hincrby("taxi_car", "nr_starts", 1); // 译者注:hash 的存储结构很像 PHP 中的关联数组(array),可以很方便的进行数据交互 $taxi_car = $redis->hgetall("taxi_car"); echo "All info about taxi car"; echo "<pre>"; var_dump($taxi_car); echo "</pre>"; |
LPUSH,RPUSH,LPOP,RPOP,LLEN,LRANGE
这些是在 Redis 中使用 list 数据结构的重要指令。Redis 的 list 和 PHP 中的 array 类似,对执行队列、堆栈、有限数据的集合提供很大的支持。
- LPUSH - 在 list 的前部(左边)插入一个或一些元素
- RPUSH - 在 list 的后部插(右边)入一个或一些元素
- LPOP - 删除并取出 list 中的第一个(左边)元素
- RPOP - 删除并取出 list 中的最后一个(右边)元素
- LLEN - 获取 list 的长度
- LRANGE - 从 list 中(按索引范围)批量获取元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?php $list = "PHP Frameworks List"; $redis->rpush($list, "Symfony 2"); $redis->rpush($list, "Symfony 1.4"); $redis->lpush($list, "Zend Framework"); echo "Number of frameworks in list: " . $redis->llen($list) . "<br>"; $arList = $redis->lrange($list, 0, -1); echo "<pre>"; print_r($arList); echo "</pre>"; // the last entry in the list echo $redis->rpop($list) . "<br>"; // the first entry in the list echo $redis->lpop($list) . "<br>"; |
EXPIRE,EXPIREAT,TTL,和 PERSIST
当你设置一个 key,你很有可能不想要一直存储,因为一段时间周期后这些数据可能不相关了。为了更好的性能你需要更新 key 的值或者删除 key 减少内存使用。Redis 提供 4 条指令让你轻松的处理数据持久化的问题。
- EXPIRE - 给 key 设置一个过期超时时间(秒),时间到后删除 key 及其 value。(译者注:设置一个负数,key 马上会被删除,高并发的场景下会有设置失败的可能,需要自己做容错 https://github.com/phpredis/phpredis/issues/751)
- EXPIREAT - 设置 unix 时间戳过期超时时间,表示 key 及其 value 将被删除的时间。(译者注:注意点同上)
- TTL - 获取 key 能存活的剩余时间。
- PERSIST - 移除 key 的过期时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 | <?php // set the expiration for next week $redis->set("expire in 1 week", "I have data for a week"); $redis->expireat("expire in 1 week", strtotime("+1 week")); $ttl = $redis->ttl("expire in 1 week"); // will be 604800 seconds // set the expiration for one hour $redis->set("expire in 1 hour", "I have data for an hour"); $redis->expire("expire in 1 hour", 3600); $ttl = $redis->ttl("expire in 1 hour"); // will be 3600 seconds // never expires $redis->set("never expire", "I want to leave forever!"); |
总结
在文章中我们只了解了一部分 Redis 指令,但是在 Redis 的网站上我们能查看整个命令集。事实上 Redis 提供得更对,不仅仅是作为 Memcache 的替代品。
Redis 从长远来看有增长的社区,支持主要的语音,提供基于主从的持久的、高可用的服务。Redis 是开源的,如果你是 C 语音专家你可以从 GitHub 上 fork 源码,成为贡献者。
如果你想获取超出(Redis)项目网站更多的信息。你可能需要考虑查阅一下这两本 Redis 书籍:Redis Cookbook 和 Redis: The Definitive Guide(译者注:原文链接已失效,换了个 Amazon 的商品链接。中文资料推荐:《Redis设计与实现》)。
译者注:这篇文章有点老了(2012年),现在(2016)Redis 可以说已经取代了 Memcache,不管是小型的大型的系统都有 Redis 的应用场景,应用非常广泛,特别是 3.0 后开始支持集群,更是如虎添翼。建议没有接触过 Redis 的小伙伴可以了解和使用一下 Redis。