存档

‘redis’ 分类的存档

Redis协议详细规范

2019年6月29日 没有评论

Redis客户端和服务器端通信使用名为 RESP (REdis Serialization Protocol) 的协议。虽然这个协议是专门为Redis设计的,它也可以用在其它 client-server 通信模式的软件上。

RESP 是下面条件的折中:

  • 实现起来简单。
  • 解析速度快。
  • 有可读性。

RESP 能序列化不同的数据类型,例如整型(integers)、字符串(strings)、数组(arrays)。额外还有特殊的错误类型。请求从客户端以字符串数组的形式发送到redis服务器,这些字符串表示要执行的命令的参数。Redis用特定于命令的数据类型回复。

RESP 是二进制安全的,并且不需要处理从一个进程发到另外一个进程的批量数据,因为它使用前缀长度来传输批量数据。 注意:这里概述的协议仅用于客户机-服务器通信。Redis集群使用不同的二进制协议在节点之间交换消息。

网络层

连到Redis服务器的客户端建立了一个到6379端口的TCP连接。

虽然RESP在技术上不特定于TCP,但是在Redis的上下文中,该协议仅用于TCP连接(或类似的面向流的连接,如unix套接字)。

请求-响应模型

Redis接受由不同参数组成的命令。一旦收到命令,就会对其进行处理,并将应答发送回客户端。

这是最简单的模型,但是有两个例外:

  • Redis 支持管道pipelining。所以,客户端可以一次发送多个命令,然后再等待应答。
  • 当一个Redis客户端订阅一个频道,那么协议会改变语义并变成pushprotocol, 也就是说,客户客户端不再需要发送命令,因为服务器端会一收到新消息,就会自动发送给客户端。

除了上面两个例外情况,Redis协议是一个简单的请求-响应协议。

RESP 协议解释

RESP 协议在Redis1.2被引入,直到Redis2.0才成为和Redis服务器通信的标准。这个协议需要在你的Redis客户端实现。

RESP 是一个支持多种数据类型的序列化协议:简单字符串(Simple Strings),错误( Errors),整型( Integers), 大容量字符串(Bulk Strings)和数组(Arrays)。

RESP在Redis中作为一个请求-响应协议以如下方式使用:

  • 客户端以大容量字符串RESP数组的方式发送命令给服务器端。
  • 服务器端根据命令的具体实现返回某一种RESP数据类型。

在 RESP 中,数据的类型依赖于首字节:

  • 简单字符串(Simple Strings): 响应的首字节是 “+”
  • 错误(Errors): 响应的首字节是 “-“
  • 整型(Integers): 响应的首字节是 “:”
  • 大容量字符串(Bulk Strings): 响应的首字节是“$”
  • 数组(Arrays): 响应的首字节是 “*

另外,RESP可以使用大容量字符串或者数组类型的特殊变量表示空值,下面会具体解释。RESP协议的不同部分总是以 “\r\n” (CRLF) 结束。

RESP 简单字符串

简单字符串编码方法: 加号后面跟着一个不包含回车或换行字符的字符串 (不允许出现换行),以CRLF(“\r\n”)结尾。

简单字符串通常被用来传输非二进制安全字符串并且消耗极小。例如,许多redis命令在成功时回复“OK”,即简单字符串用以下5个字节编码:

"+OK\r\n"

为了发送二进制安全的字符串,需要使用RESP的大容量字符串(Bulk Strings)替代。

当Redis返回简单字符串(Simple String)时,客户端lib应该返回去掉首字符加号和结尾CRLF字符的字符串给调用者。

RESP Errors

RESP 有特殊类型来处理错误。errors类型除了首字符是减号 ‘-‘不是加号以外,其它跟简单字符串一样。RESP中简单字符和错误的真正区别是:错误被客户端当作异常处理,组成错误类型的字符串是错误消息自身。

基本格式如下:

"-Error message\r\n"

错误应答只在发生异常时发送,例如,要执行命令的参数数据类型不匹配或者命令不存在等。当收到错误返回时,客户端lib应该抛出一个异常。

错误返回例子:

-ERR unknown command 'foobar'
-WRONGTYPE Operation against a key holding the wrong kind of value

从”-“后面第一个单词起,直到第一个空格或者换行,表示返回的错误类型。这是Redis的一种约定,并不是RESP协议的要求。

ERR 是一个通用错误, 而 WRONGTYPE 是表示更具体的错误,意味着客户端在错误的数据类型上执行操作。这被叫做错误前缀(Error Prefix), 使客户端不用依赖具体错误消息就知道返回的错误类型,错误消息可能会随着时间而变化。

客户端实现可能会对不同异常返回不同类型的错误,或者可能提供一种通用的方式来捕获错误,通过以字符串的形式直接返回错误名给调用者。

尽管如此,这种特性不能认为很重要,因为它很少被使用。一小部分客户端的实现可能会返回通用错误条件,例如false。

RESP 整型

整型类型是由以冒号开头,CRLF结尾,中间是字符串形式表示的数字。 例如 “:0\r\n”, 或 “:1000\r\n” 都是整型回复。

很多Redis命令返回RESP整数,像 INCRLLEN 和 LASTSAVE.

返回的整数并没有特别的意义, INCR 返回的是一个递增的数字, LASTSAVE 返回的是Unix时间戳等。返回的整数有效值需要在有符号64位整数范围内。

整型返回也被广泛的用来返回 true 或 false。比如 EXISTS 或 SISMEMBER 命令返回1表示true,返回0表示false。

其它命令像 SADDSREM 和 SETNX 如果操作被执行则返回1,否则返回0。

返回整型回复的命令: SETNXDELEXISTSINCRINCRBYDECRDECRBYDBSIZELASTSAVERENAMENXMOVELLENSADDSREMSISMEMBERSCARD.

RESP 大容量字符串

大容量字符串被用来表示最大512MB长的二进制安全字符串。

大容量字符串编码方式:

  • 美元符 “$” 后面跟着组成字符串的字节数(前缀长度),并以 CRLF 结尾。
  • 实际的字符串数据。
  • 结尾是 CRLF。

所以,字符串 “foobar” 编码如下:

"$6\r\nfoobar\r\n"

空字符串编码格式:

"$0\r\n\r\n"

RESP 大容量字符串(Bulk Strings) 也可以使用一个特殊的用来表示空值的格式表示不存在的值。在这种格式里长度值为-1,数据部分不存在,所以空(Null)用如下方式表示:

"$-1\r\n"

叫做空的大容量字符串Null Bulk String。

客户端API库不应该返回空串,当服务器端响应一个空的大容量字符串时,API库可以返回一个空对象给调用者。例如,Ruby库应该返回 ‘nil’ ,而C库应该返回NULL。 

RESP 数组

客户端使用 RESP 数组发送命令到 Redis 服务端。同样地,某些使用 RESP 数组返回元素集合给客户端的 Redis 命令是应答类型。 LRANGE 命令返回元素列表就是一个例子。

RESP 数组使用如下格式发送:

  • 以星号* 为首字符,接着是表示数组中元素个数的十进制数,最后以 CRLF 结尾。
  • 外加数组中每个 RESP 类型的元素。

空数组表示:

"*0\r\n"

有两个 RESP 大容量字符串”foo” 和”bar”元素的 RESP 数组 :

"*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n"

在前缀 *<count>CRLF 的后面,组成数组的其它数据类型一个接在另一个后面。 例如包含三个整数的数组编码方式:

"*3\r\n:1\r\n:2\r\n:3\r\n"

数组可以包含混合类型,不一定必须是同一种类型。例如,4个整型和1个大容量字符串编码方式:

*5\r\n
:1\r\n
:2\r\n
:3\r\n
:4\r\n
$6\r\n
foobar\r\n

(为了方便阅读,应答分成多行来展示)

第一个行表示 *5\r\n 说明后面有5个应答。这些应答组成一个大的应答一起发送。

空数组的概念也是存在的,另一个表示空值的方式(通常使用大容量空字符串,历史遗留导致有这两种格式)。

例如,当 BLPOP 命令超时,它会返回一个空数组,数组的计数器是-1 :

"*-1\r\n"

当 Redis 返回一个空数组的时候,Redis客户端库API应该返回一个空对象而不是返回一个空数组。 这对区分空列表和其它不同情况(像 BLPOP 命令超时情况)是必要的。

数组的数组也是可行的。例如,一个含有两个数组元素的数组编码方式:

*2\r\n
*3\r\n
:1\r\n
:2\r\n
:3\r\n
*2\r\n
+Foo\r\n
-Bar\r\n

(为了方便阅读,分成多行来展示).

上面的 RESP 数据类型包含两个数组,一个数组包含三个整数1, 2, 3 ,另一个是简单字符串和一个错误类型。

数组中的空元素

数组中可以有为空的元素。主要使用在Redis应答中,为了表示这个元素丢失并且不是一个空的字符串。当SORT命令使用GET 模式选项,并且特定的key丢失的时会出现这种应答。 含有有空元素的应答数组例子:

*3\r\n
$3\r\n
foo\r\n
$-1\r\n
$3\r\n
bar\r\n

第二个元素是空,客户端库应该返回像下面这样的数据:

["foo",nil,"bar"]

这不是前面提到的异常情况,这只是说明协议的一个例子。

发送命令到Redis服务器

至此,我们已经很熟悉RESP序列化格式,写一个Redis客户端库的实现会变得很容易。我们可以进一步说明客户端和服务端如何交互工作:

  • 客户端发送包含只有大容量字符串的数组给Redis服务器。
  • Redis 服务器给客户端发送任意有效的 RESP 数据类型作为应答。

下面是一个典型的交互过程例子:

客户端发送命令 LLEN mylist 来获取存储在 mylist 键中列表的长读,然后服务器端返回整数应答(C: 代表客户端, S: 代表服务器端).

C: *2\r\n
C: $4\r\n
C: LLEN\r\n
C: $6\r\n
C: mylist\r\n

S: :48293\r\n

为了方便理解我们用换行把协议分成不同部分,实际上客户端发送的是一个整体没有换行:*2\r\n$4\r\nLLEN\r\n$6\r\nmylist\r\n as a whole.

管道和多个命令

客户端可以使用同一个连接发送多个命令。通过管道客户端可以一次写操作发送多个命令,发送下一个命令前不需要等待前一个命令的应答。所有应答可以在最后被读取。

关于管道详细参考 page about Pipelining.

内联命令

有时你手边只能操作telnet 并且需要给Redis 服务器端发送命令。虽然Redis协议是容易实现的,但并不适合用在交互会话。redis-cli 也不是随时都能可用。因此,redis还以一种特殊的方式接受为人类设计的命令,称为内联命令格式。 以下是使用内联命令进行服务器/客户端聊天的示例(服务器聊天以s开头,客户端聊天以c开头)。

C: PING
S: +PONG

以下是返回整数的内联命令的另一个示例:

C: EXISTS somekey
S: :0

基本上,您只需在telnet会话中编写空格分隔的参数。由于统一请求协议中没有以*开头的命令,因此Redis能够检测到这种情况并解析您的命令。

Redis 协议的高性能解析器

虽然redis协议是非常容易被人阅读和实现的,但是它可以以类似于二进制协议的性能来实现。

RESP 使用带前缀的长度来传输批量数据,因此不需要像使用json那样扫描有效负载以查找特殊字符,也不需要引用需要发送到服务器的有效负载。

批量和多批量长度可以使用代码进行处理,代码对每个字符执行单个操作,同时扫描CR字符,如以下C代码:

RESP 使用带前缀的长度来传输大容量数据,因此不需要像使用json那样扫描有效负载以查找特殊字符,也不需要引用需要发送到服务器的有效负载。

大容量和多个大容量长度可以使用代码进行处理,代码对每个字符执行单个操作,同时扫描CR字符,如以下C代码:

#include <stdio.h>

int main(void) {
    unsigned char *p = "$123\r\n";
    int len = 0;

    p++;
    while(*p != '\r') {
        len = (len*10)+(*p - '0');
        p++;
    }

    /* Now p points at '\r', and the len is in bulk_len. */
    printf("%d\n", len);
    return 0;
}

在识别出第一个CR之后,可以跳过它和下面的LF,而不需要任何处理。然后,可以使用不以任何方式检查有效负载的单个读取操作读取大容量数据。最后,剩余的CR和LF字符将被丢弃,而不进行任何处理。

Redis协议有着与二进制协议可比的性能,更重要的是易于在大多数高级语言中实现,从而减少了客户端软件中的错误数量。

分类: redis 标签:

redis常用命令

2015年1月9日 2 条评论

1. redis查看当前所有的key

2. 查看当前redis的配置信息

3. MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error.

强制停止redis快照导致,redis运行用户没有权限写rdb文件或者磁盘空间满了,解决办法:

例如:

阅读全文...

分类: redis 标签: ,

动态更新运行中程序的配置信息

2014年10月18日 3 条评论

一个程序投产线上使用基本不会停下来,一旦业务需求改变或者增加需求,需要修改配置文件时,往往需要停机修改配置后重新启动服务。

这个过程进程少还可以接受,如果停一次机要很长时间而且需要授权更新,这个是难以接受的方法。

想了几个办法
1.放到内存,例如redis这种字典,这样可以动态修改redis的值来实现动态更新,问题是如何保证redis出问题不影响原程序呢?
重redis里读到map,如果需要的配置不在,到内存里去读,这样只能增加配置,想要删除配置好像不理想。

2.定时load配置文件,

3.通过信号load,

2.和3.不好协调多个进程服务一致性问题。

真伤脑筋啊。

有人知道好方法,告诉我一下。

memcache和redis区别

2014年4月27日 没有评论

memcache官方定义

Free & open source, high-performance, distributed memory object caching system, generic in nature, but intended for use in speeding up dynamic web applications by alleviating database load.

redis官方定义
Redis is an open source, BSD licensed, advanced key-value store. It is often referred to as a data structure server since keys can contain strings, hashes, lists, sets and sorted sets.

版权相同

它们都是使用的bsd协议,使用它的项目可以用于商业用户,不必发布二次修改的代码,可以修改源代码。

数据类型

redis数据类型丰富,支持set liset等类型
memcache支持简单数据类型,需要客户端自己处理复杂对象

持久性

redis支持数据落地持久化存储
memcache不支持数据持久存储

分布式存储

redis支持master-slave复制模式
memcache可以使用一致性hash做分布式

value大小不同

memcache是一个内存缓存,key的长度小于250字符,单个item存储要小于1M,不适合虚拟机使用

数据一致性不同

redis使用的是单线程模型,保证了数据按顺序提交。
memcache需要使用cas保证数据一致性。CAS(Check and Set)是一个确保并发一致性的机制,属于“乐观锁”范畴;原理很简单:拿版本号,操作,对比版本号,如果一致就操作,不一致就放弃任何操作

cpu利用

redis单线程模型只能使用一个cpu,可以开启多个redis进程

参考

http://www.cnblogs.com/qunshu/p/3196972.html
http://www.blogjava.net/chhbjh/archive/2012/02/21/370472.html
http://maoyidao.iteye.com/blog/1846089

分类: redis 标签: ,

redis和redis php扩展安装

2013年5月29日 1 条评论

redis是一个内存数据库,比memcache支持更丰富的value类型,新浪微博就使用redis来做缓存。

redis的源码安装

1.make时可能会报如下错误:

解决办法:
编辑src/.make-settings里的OPT,改为OPT=-O2 -march=i686。

2.make test报错:

解决办法安装tcl

redis命令介绍

Redis 由四个可执行文件:redis-benchmark、redis-cli、redis-server、redis-stat 这四个文件,加上一个redis.conf就构成了整个redis的最终可用包。它们的作用如下:

redis-server:Redis服务器的daemon启动程序
redis-cli:Redis命令行操作工具。当然,你也可以用telnet根据其纯文本协议来操作
redis-benchmark:Redis性能测试工具,测试Redis在你的系统及你的配置下的读写性能
redis-stat:Redis状态检测工具,可以检测Redis当前状态参数及延迟状况
现在就可以启动redis了,redis只有一个启动参数,就是他的配置文件路径。

启动redis

复制源码包里的redis.conf到/etc
# cd redis-stable
# cp redis.conf /etc/redis.conf

编辑/etc/redis.conf ,修改
daemaon no 为daemaon yes ,以守护进程方式启动进程。

# redis-server /etc/redis.conf

关闭redis
# redis-cli shutdown //关闭所有
关闭某个端口上的redis
# redis-cli -p 6397 shutdown //关闭6397端口的redis
说明:关闭以后缓存数据会自动dump到硬盘上,硬盘地址见redis.conf中的dbfilename dump.rdb

redis配置

注意,默认复制过去的redis.conf文件的daemonize参数为no,所以redis不会在后台运行,这时要测试,我们需要重新开一个终端。修改为yes则为后台运行redis。另外配置文件中规定了pid文件,log文件和数据文件的地址,如果有需要先修改,默认log信息定向到stdout.

下面是redis.conf的主要配置参数的意义:

daemonize:是否以后台daemon方式运行
pidfile:pid文件位置
port:监听的端口号
timeout:请求超时时间
loglevel:log信息级别
logfile:log文件位置
databases:开启数据库的数量
save * *:保存快照的频率,第一个*表示多长时间,第三个*表示执行多少次写操作。在一定时间内执行一定数量的写操作时,自动保存快照。可设置多个条件。
rdbcompression:是否使用压缩
dbfilename:数据快照文件名(只是文件名,不包括目录)
dir:数据快照的保存目录(这个是目录)
appendonly:是否开启appendonlylog,开启的话每次写操作会记一条log,这会提高数据抗风险能力,但影响效率。
appendfsync:appendonlylog如何同步到磁盘(三个选项,分别是每次写都强制调用fsync、每秒启用一次fsync、不调用fsync等待系统自己同步)
这时你可以打开一个终端进行测试了,配置文件中默认的监听端口是6379

redis开机自动启动

用这个脚本管理之前,需要先配置下面的内核参数,否则Redis脚本在重启或停止redis时,将会报错,并且不能自动在停止服务前同步数据到磁盘上:

# vi /etc/sysctl.conf

vm.overcommit_memory = 1

然后应用生效:

# sysctl –p

建立redis启动脚本:

# vim /etc/init.d/redis

然后增加服务并开机自启动:

redis php扩展安装

wget https://github.com/nicolasff/phpredis/zipball/master -O php-redis.zip
unzip php-redis.zip
cd nicolasff-phpredis-2d0f29b/
/usr/local/php/bin/phpize
./configure --with-php-config=/usr/local/php/bin/php-config
make && make install

完成后redis.so被安装到
/usr/local/php/lib/php/extensions/no-debug-non-zts-20100525/

vi /usr/local/php/lib/php.ini

添加
extension=redis.so

重启php-fpm即可。

configure时可能会遇到,添加--with-php-config参数可以解决。

configure: error: Cannot find php-config. Please use --with-php-config=PATH

./configure --with-php-config=/usr/local/php/bin/php-config

分类: redis 标签: , , ,