Redis学习笔记

更新时间:2024-02-02 02:09:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

Redis学习笔记

?

下载文件列表 o Redis

o Redis - PHP 模块 o Redis - 第三方包 o igbinary - PHP模块

? 安装

o Redis

o igbinary

o phpredis ? 配置

o 部分Redis配置参数说明

o Redis部分参数建议

o 部分Sentinel 配置参数说明 o Sentinel部分参数建议 ? 运行

o 直接运行 Redis

o 直接运行 Sentinel o 使用工具脚本 ? 持久化

o 快照(Snapshotting) o aof(Append-only file)

o 取消持久化 ? 简单性能测试 o Apache ab

o redis-benchmark ? 简单集群

o 主从复制 o 链式结构 o 使用sentinel o Redis Cluster ? 应用场景

o 计数器

o 排行榜

o 最新的N各数据

o Pub/Sub(发布/订阅)构建的实时消息系统 o 缓存(以及和Memcached的比较) ? 参考资料

o Redis命令参考

o Redis经验谈 o Redis监控技巧 o 其他

1. 下载文件列表

1. Redis

描述

链接

官网下载地址 http://download.redis.io/releases/redis-2.8.3.tar.gz官网下载列表 http://www.redis.io/download

GitHub源码 https://github.com/antirez/redis2. 3.

4. Redis - PHP 模块

描述

链接

官网下载地址 http://pecl.php.net/get/redis-2.2.4.tgz官网下载列表 http://pecl.php.net/package/redis

GitHub源码 https://github.com/nicolasff/phpredis5. 6.

7. Redis - 第三方包

描述

链接

GitHub源码 https://github.com/nrk/predis8. 9.

10. igbinary - PHP模块

描述

链接

官网下载地址 http://pecl.php.net/get/igbinary-1.1.1.tgz官网下载列表 http://pecl.php.net/package/igbinaryGitHub源码 https://github.com/phadej/igbinary2. 安装

1. Redis

步骤 命令

解压redis安装包 tar -zxvf redis-2.8.3.tar.gz 移动至应用目录 mv redis-2.8.3 $REDIS_DIR 进入redis目录

cd $REDIS_DIR

编译redis make

安装redis make install PREFIX=$REDIS_DIR 完善目录mkdir db && mkdir logs && mkdir run &&

结构 mkdir conf

移动confmv redis.conf conf/redis.conf.default 文件 && mv sentinel.conf

conf/sentinel.conf.default 复制sentinel

cp src/redis-sentinel bin/

2. 注意:$REDIS_DIR为Redis的放置目录,如 /usr/local/redis-2.8.3

备注

PREFIX一定要大写 数据库/日志/进程/配置 文

件夹

移动并备份设置文件

sentinel 功能还不稳定

3. ① 关于在32位机器上的安装,如果出现问题可以先参考解压后文件夹里面的 README。 4. ② make后可以make test,不过需要安装tk库,暂时不知道不安装这个库会有什么影响,

不过我没有安装。

5. ③ 经过单独安装后,发现安装文件和源码文件没有冲突,于是安装目录就直接写源码目录

了。 6.

7. igbinary

步骤 解压igbinary安装包

进入解压后的目录

运行下Phpize 配置编译安装 修改php.ini 重启php

命令

备注

tar -zxvf igbinary-1.1.1.tgz cd igbinary-1.1.1

Phpize ./configure && make && make

install

- -

增加extension = igbinary.so

让igbinary模块生效

8. ① 修改 php.ini 时还有一些可选功能

9. # Load igbinary extension 10. extension=igbinary.so 11.

12. # Use igbinary as session serializer 13. session.serialize_handler=igbinary 14.

15. # Enable or disable compacting of duplicate strings 16. # The default is On.

17. igbinary.compact_strings=On

18. 19.

20. phpredis

步骤 解压phpredis安装包

进入解压后的目录

运行下Phpize 配置编译安装 修改php.ini 重启php

命令

备注

tar -zxvf redis-2.2.4.tgz cd redis-2.2.4

Phpize

./configure && make && make 这里没有开启igbinary

支持 install

- -

增加extension = redis.so

让redis模块生效

21. ① phpredis提供了igbinary的序列化支持,需要的话就在设置的时候

22. ./configure --enable-redis-igbinary

23. ② phpredis模块提供了让redis存储phpsession的功能,修改

php.ini后即可使用。具体参考

https://github.com/nicolasff/phpredis#php-session-handler

24.

3. 配置

1. 部分Redis配置参数说明

参数

daemonize pidfile port bind timeout loglevel logfile databases

SNAPSHOTTING

save * *

stop-writes-on-bgsave-error rdbcompression rdbchecksum dbfilename dir

REPLICATION

slaveof masterauth

pid文件位置 监听的端口号

说明

是否以后台daemon方式运行

重用 √

绑定ip。默认绑定所有本机ip,一般用在服务器多ip下,可以只监听内网服务器ip,保证服√ 务安全。 请求超时时间 log信息级别 log文件位置 开启数据库的数量

保存快照的频率,save 60 100 表示 60秒内有100次操作则保存一次快照,可设置多个条√ 件。

是否允许在后台存储错误时让数据库可写 是否使用压缩

存储和加载rdb文件时是否校验 数据快照的保存目录(这个是目录)

根据给定的master ip 和master port,启动时

自动slaveof

如果master有密码,就填个密码呗

当slave失去与master的连接,或正在拷贝中,如果为yes,slave会响应客户端的请求,数据可能不同步甚至没有数据,如果为no,√ slave会返回错误\progress\。(过期数据集) slave是否只可读

slave发送ping给master的时间间隔,单位秒 √

√ √ √ √

√ √ √

数据快照文件名(只是文件名,不包括目录)

slave-serve-stale-data

slave-read-only repl-ping-slave-period

repl-timeout slave-priority

SECURITY

requirepass rename-command

LIMITS

maxclients maxmemory maxmemory-policy maxmemory-samples

APPEND ONLY MODE appendonly appendfilename

appendfsync

no-appendfsync-on-rewrite auto-aof-rewrite-percentage auto-aof-rewrite-min-size

LUA SCRIPTING

lua-time-limit

SLOW LOG

slowlog-log-slower-than

响应超时判断(ping 或 I/O buffer等的响应),单

位秒

当master挂掉的时候slave成为master的优先值,需要配合sentinel使用,越小越优先,√ 值为0的slave不会成为master

设置密码

给一些危险的指令重命名,重命名为\空)则不

执行

客户端并发连接数上限,到达上限,会关闭所

有链接并返回错误

设置最大内存,到达上限,服务器会根据驱逐

政策(eviction policy)删除某些键值。

设置驱逐政策,根据不同的算法策略删除一些

键,或者不让写。

驱逐参数,驱逐算法会在一个有n个键值的样

本中选择较没用的那个

是否开启aof,开启的话会按一定频率log每条

操作

指定aof文件名

aof记录的频率,always是每一次操作马上就同步到日志中,everysec是每隔一秒强制

fsync,no是不调用fsync(),让操作系统自己√

决定何时同步。

如果为yes,当BGSAVE或

BGREWRITEAOF指令运行时,即把AOF文件转写到RDB文件中时,会阻止调用√

fsync()。

Redis会将AOF文件最初的大小记录下来,如果当前的AOF文件的大小增加100%并且超过64mb时,就会自动触发Redis改写AOF文件√ 到RDB文件中,如果auto-aof-rewrite-percentage为0表示取消自动rewrite功能

一个Lua脚本最长的执行时间为5000毫秒(5

秒),如果为0或负数表示无限执行时间。

当某个请求执行时间(不包括IO时间)超过10000微妙(10毫秒),把请求记录在慢日志

中 ,如果为负数不使用慢日志,如果为0强制√

记录每个指令。

slowlog-max-len

ADVANCED CONFIG hash-max-ziplist-entries hash-max-ziplist-value list-max-ziplist-entries list-max-ziplist-value set-max-intset-entries zset-max-ziplist-entries zset-max-ziplist-value activerehashing

INCLUDES

include 2. 3.

慢日志的最大长度是128,当慢日志超过128时,最先进入队列的记录会被踢出来,慢日志

会消耗内存,你可以使用SLOWLOG RESET清空队列回收这些内存。

较小的hash可以通过某种特殊的方式进行编

码,以节省大量的内存空间,我们指定最大的√ 条目数为512,每个条目的最大长度为64。 较小的list可以通过某种特殊的方式进行编

码,以节省大量的内存空间,我们指定最大的√ 条目数为512,每个条目的最大长度为64。 较小的由特殊元素组成(全部元素都是以10为底的整数)的set可以通过某种特殊的方式进行√ 编码,这里指定最大条目数

较小的Sort set 可以通过某种特殊的方式进行编码,以节省大量的内存空间,我们指定最大√ 的条目数为512,每个条目的最大长度为64。 重新哈希the main Redis hash table(the one mapping top-level keys to values),这样会节省更多的空间。

包含一些可以重用的配置文件

√ √

4. Redis部分参数建议

参数

daemonize yes bind 127.0.0.1 timeout 30 pidfile $REDIS_DIR/run/$PORT_redis.pid logfile $REDIS_DIR/logs/$PORT_redis.log dbfilename $REDIS_DIR/db/$PORT_redis.rdb dir $REDIS_DIR/db/ rename-command FLUSHDB “” maxmemory

不超过内存的30%

slowlog-max-len 1024 5. 6. 7.

8. 部分Sentinel 配置参数说明

参数

默认值

说明

port sentinel monitor sentinel auth-pass 26379

mymaster 127.0.0.1 6379 2

Sentinel运行的端口 监视一个名为mymaster 的主服务器,这个主服务器的 IP 地址为127.0.0.1,端口号为

6379, 而将这个主服务器判断为失效至少需要 2个 Sentinel 同意 (只要同意 Sentinel 的数量不达标,自动故障迁移就不会执行 链接对应master-name的redis实例所需要的密码

sentinel down-after-milliseconds mymaster 30000

如果服务器在给定的毫秒数之内, 没有返回 Sentinel 发送的 PING 命令的回复, 或者返回一个错误, 那么 Sentinel 将这个服务器标记为主观下线(subjectively down,简称 SDOWN )。 不过只有一个 Sentinel 将服务器标记为主观下线并不一定会引起服务器的自动故障迁移: 只有在足够数量的 Sentinel 都将一个服务器标记为主观下线之后, 服务器才会被标记为客观下线(objectively down, 简称 ODOWN ), 这时自动故障迁移才会执行。 将服务器标记为客观下线所需的 Sentinel 数量由对主服务器的配置决定。

sentinel parallel-syncs mymaster 1

选项指定了在执行故障转移时, 最多可以有多少个从服务器同时对新的主服务器进行同步, 这个数字越小, 完成故障转移所需的时间就越长。 故障重启延迟

sentinel failover-timeout 9. 10.

mymaster 180000

11. Sentinel部分参数建议

参数

daemonize yes pidfile $REDIS_DIR/run/$PORT_redis.pid logfile $REDIS_DIR/logs/$PORT_redis.log sentinel monitor mymaster 127.0.0.1 6379 1 12.

4. 运行

1. 直接运行 Redis

假设这个实例运行在6379端口,按照建议,配置文件为 6379_redis.conf,使用 redis-server 启动实例

cd $REDIS_DIR/bin

./redis-server $REDIS_DIR/conf/6379_redis.conf

启动之后可以使用 redis-cli 打开客户端测试是否成功(我在conf文件中设置了密码为123456)

./redis-cli -h 127.0.0.1 -p 6379 -a 123456 127.0.0.1:6379> ping PONG

127.0.0.1:6379> set user_score:1143 500 OK

127.0.0.1:6379> get user_score:1143 \

按照建议,日志文件为6379_redis.log,查看日志文件

tailf $REDIS_DIR/logs/6379_redis.log

#会发现这样一行

[29461] 07 Jan 17:48:36.500 WARNING overcommit_memory is set to 0!

Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.

#有root权限的同学可以按照提示去修改一下设置

至此,redis直接运行完毕。使用 --help查看帮助获取更多信息

./redis-server --help ./redis-cli --help

2. 直接运行 Sentinel

假设Sentinel运行在 26379 端口,按照建议,其配置文件为 conf/26379_redis.conf

#使用--sentinel可以让redis-server启动sentinel

./redis-server $REDIS_DIR/conf/26379_redis.conf --sentinel

#或直接使用redis-sentinel启动(默认的bin文件夹中没有redis-sentinel,需要到$REDIS_DIR/src文件夹中复制)

./redis-sentinel $REDIS_DIR/conf/26379_redis.conf

redis-sentinel的帮助和redis-server的是一样的。。

运行sentinel时记得先设置好监视的主节点(配置文件中的sentinel monitor)

3. 使用工具脚本

在安装目录的utils文件夹中有一部分工具脚本。

其中运行install_server.sh脚本可以根据提示完成redis的配置,不过需要root权限。 运行redis_init_script可以方便地启动redis服务。 于是,我们可以按照需要对脚本做一些更改作为己用。 redis_init_script

# 原来的redis_init_script配置

REDISPORT=6379

EXEC=/usr/local/bin/redis-server CLIEXEC=/usr/local/bin/redis-cli

PIDFILE=/var/run/redis_${REDISPORT}.pid CONF=\

# 更改后的redis_init_script配置

$REDIS_DIR = $HOME/local/redis-2.8.3 EXEC=$REDIS_DIR/bin/redis-server CLIEXEC=$REDIS_DIR/bin/redis-cli #MASTER

MASTER_REDISPORT=6379

MASTER_PIDFILE=$REDIS_DIR/run/${MASTER_REDISPORT}_redis.pid MASTER_CONF=\#SLAVE

SLAVE_REDISPORT=6380

SLAVE_PIDFILE=$REDIS_DIR/run/${SLAVE_REDISPORT}_redis.pid SLAVE_CONF=\

同样的,添加上redis slave配置后可以使用该脚本快速启动redis服务。 通过简单的复制黏贴修改而成的脚本

#!/bin/sh #

# Simple Redis init.d script conceived to work on Linux systems # as it does use of the /proc filesystem.

EXEC=/home/wure/local/redis-2.8.3/bin/redis-server CLIEXEC=/home/wure/local/redis-2.8.3/bin/redis-cli REDIS=/home/wure/local/redis-2.8.3 # MASTER

MASTER_REDISPORT=11694

MASTER_PIDFILE=$REDIS/run/master.pid MASTER_CONF=\# SLAVE

SLAVE_A_REDISPORT=11695

SLAVE_A_PIDFILE=$REDIS/run/slave-a.pid SLAVE_A_CONF=\SLAVE_B_REDISPORT=11696

SLAVE_B_PIDFILE=$REDIS/run/slave-b.pid SLAVE_B_CONF=\SLAVE_C_REDISPORT=11697

SLAVE_C_PIDFILE=$REDIS/run/slave-c.pid SLAVE_C_CONF=\SLAVE_D_REDISPORT=11698

SLAVE_D_PIDFILE=$REDIS/run/slave-d.pid SLAVE_D_CONF=\case \ begin)

if [ -f $MASTER_PIDFILE ] then

echo \already running or crashed\ else

echo \ $EXEC $MASTER_CONF fi

if [ -f $SLAVE_A_PIDFILE ] then

echo \already running or crashed\ else

echo \ $EXEC $SLAVE_A_CONF fi

if [ -f $SLAVE_B_PIDFILE ] then

echo \already running or crashed\ else

echo \ $EXEC $SLAVE_B_CONF fi

if [ -f $SLAVE_C_PIDFILE ] then

echo \already running or crashed\ else

echo \ $EXEC $SLAVE_C_CONF fi

if [ -f $SLAVE_D_PIDFILE ] then

echo \already running or crashed\ else

echo \ $EXEC $SLAVE_D_CONF fi ;; start)

if [ -f $MASTER_PIDFILE ] then

echo \or crashed\ else

echo \ $EXEC $MASTER_CONF fi ;; end)

if [ ! -f $MASTER_PIDFILE ] then

echo \running\

else

PID=$(cat $MASTER_PIDFILE) echo \

$CLIEXEC -p $MASTER_REDISPORT shutdown while [ -x /proc/${PID} ] do

echo \ sleep 1 done

echo \ fi

if [ ! -f $SLAVE_A_PIDFILE ] then

echo \not running\ else

PID=$(cat $SLAVE_A_PIDFILE) echo \

$CLIEXEC -p $SLAVE_A_REDISPORT shutdown while [ -x /proc/${PID} ] do

echo \ sleep 1

done

echo \ fi

if [ ! -f $SLAVE_B_PIDFILE ] then

echo \not running\ else

PID=$(cat $SLAVE_B_PIDFILE) echo \

$CLIEXEC -p $SLAVE_B_REDISPORT shutdown while [ -x /proc/${PID} ] do

echo \ sleep 1 done

echo \ fi

if [ ! -f $SLAVE_C_PIDFILE ] then

echo \not running\ else

PID=$(cat $SLAVE_C_PIDFILE) echo \

$CLIEXEC -p $SLAVE_C_REDISPORT shutdown while [ -x /proc/${PID} ] do

echo \ sleep 1 done

echo \ fi

if [ ! -f $SLAVE_D_PIDFILE ] then

echo \not running\ else

PID=$(cat $SLAVE_D_PIDFILE) echo \

$CLIEXEC -p $SLAVE_D_REDISPORT shutdown while [ -x /proc/${PID} ] do

echo \ sleep 1 done

echo \ fi ;; stop)

if [ ! -f $MASTER_PIDFILE ] then

echo \running\

else

PID=$(cat $MASTER_PIDFILE) echo \

$CLIEXEC -p $MASTER_REDISPORT shutdown while [ -x /proc/${PID} ] do

echo \ sleep 1 done

echo \ fi ;; *)

echo \ ;; esac

另外 redis 里面没有重启,同理可以自己建一个 restart。

5. 持久化

1. 快照(Snapshotting)

快照是默认的持久化方式,redis重启时通过加载快照文件来实现数据库内容的恢复。 如果数据比较大的话加载需要一定的时间,估计1G需要10-20秒(网络数据,未验证)。 Redis将内存中的数据以快照的方式写入到二进制文件中实现持久化。

每次持久化都是将内存数据完整写入到磁盘一次,并不是增量,所以当数据大时会有IO压力。

快照持久化过程

1.redis调用fork,现在有了子进程和父进程。

2.父进程继续处理client请求,子进程负责将内存内容写入到临时文件。由于os的写时复制机制(copy on write)父子进程会共享相同的物理页面,当父进程处理写请求时os会为父进程要修改的页面创建副本,而不是写共享的页面。所以子进程的地址空间内的数 据是fork时刻整个数据库的一个快照。

3.当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出。

可以通过bin文件夹里面的redis-check-dump可以检验快照文件的一致性和修复文件。

相比aof机制,如果数据集很大,快照的启动效率会更高。

鉴于快照的保存策略,宕机时会存在一定的数据丢失。

save/bgsave操作可以手动进行快照备份。但是save会阻塞client请求,不推荐使用。

2. aof(Append-only file)

aof持久化方式默认是关闭的。这种方式通过记录对Redis的操作到文件实现持久化。

当redis重启时会通过重新执行文件中保存的写命令来重建数据库的内容。 aof测试 - client命令

127.0.0.1:11694> keys * (empty list or set)

127.0.0.1:11694> set name Obama OK

127.0.0.1:11694> set age 67 OK

127.0.0.1:11694> set gender unsure OK

127.0.0.1:11694> keys * 1) \2) \3) \

127.0.0.1:11694> del age

aof测试 - aof文件

*2 $6

SELECT $1 0 *3 $3 set $4 name $5 Obama *3 $3 set $3 age $2 67 *3 $3 set $6

gender $6

unsure *2 $3 del $3 age

可以通过bin文件夹里面的redis-check-aof来检验aof文件的一致性和修复aof文件。 修改配置文件可以更改aof的写入频率。 使用bgrewriteaof可以压缩aof文件。 使用bgrewrite后的aof文件

gender

$6

unsure *3 $3 SET $4 name $5 Obama

当同时使用rdb和aof持久化时,redis会根据aof来重建数据。

3. 取消持久化

通过注释所有save配置项以取消Snapshotting持久化 通过更改配置appendoly为no取消aof持久化

6. 简单性能测试

1. Apache ab

单个文件,向list中添加数据 PHP Code

$conn = new Redis();

$conn->pconnect('127.0.0.1','11694'); $id = uniqid(md5(microtime())); $message = 'test message'; $log = $id.$message;

$conn->lpush('test',$log);

Server Software: nginx/1.4.1

Server Hostname: dev7.ucweb.local Server Port: 11691

Document Path: /redis/index.php Document Length: 0 bytes Concurrency Level: 1

Time taken for tests: 23.206 seconds Complete requests: 10000 Failed requests: 0

Total transferred: 1460000 bytes HTML transferred: 0 bytes

Requests per second: 430.92 [#/sec] (mean) Time per request: 2.321 [ms] (mean)

Time per request: 2.321 [ms] (mean, across all concurrent requests) Transfer rate: 61.44 [Kbytes/sec] received Connection Times (ms)

min mean[+/-sd] median max Connect: 0 0 0.0 0 0 Processing: 1 2 0.5 2 5 Waiting: 1 2 0.5 2 5 Total: 1 2 0.5 2 5

Percentage of the requests served within a certain time (ms) 50% 2 66% 2 75% 3 80% 3 90% 3 95% 3 98% 3 99% 3

100% 5 (longest request)

单一文件,每次读取一个list中的第500个数据(毫无压力= =) PHP Code

$conn = new Redis();

$conn->pconnect('127.0.0.1','11694'); $test = $conn->lGet('test',500);

Simple Read Benchmark

Server Software: nginx/1.4.1

Server Hostname: dev7.ucweb.local Server Port: 11691

Document Path: /redis/index.php Document Length: 0 bytes Concurrency Level: 1

Time taken for tests: 7.692 seconds Complete requests: 10000 Failed requests: 0

Total transferred: 1460000 bytes HTML transferred: 0 bytes

Requests per second: 1300.08 [#/sec] (mean) Time per request: 0.769 [ms] (mean)

Time per request: 0.769 [ms] (mean, across all concurrent requests) Transfer rate: 185.36 [Kbytes/sec] received Connection Times (ms)

min mean[+/-sd] median max Connect: 0 0 0.0 0 1 Processing: 1 1 0.6 1 62 Waiting: 0 1 0.6 1 62 Total: 1 1 0.6 1 62

Percentage of the requests served within a certain time (ms) 50% 1 66% 1 75% 1 80% 1 90% 1 95% 1 98% 1 99% 1

100% 62 (longest request)

2. redis-benchmark

在bin文件目录中有redis的自带测试工具 redis-benchmark 。使用默认参数进行测试的结果(1主4从星型结构,单机) Default Benchmark

PING_INLINE: 86206.90 requests per second PING_BULK: 86206.90 requests per second SET: 44642.86 requests per second GET: 80000.00 requests per second INCR: 54644.81 requests per second LPUSH: 51020.41 requests per second LPOP: 29325.51 requests per second SADD: 85470.09 requests per second SPOP: 83333.34 requests per second

LPUSH (needed to benchmark LRANGE): 41666.67 requests per second LRANGE_100 (first 100 elements): 32258.06 requests per second LRANGE_300 (first 300 elements): 15503.88 requests per second LRANGE_500 (first 450 elements): 11350.74 requests per second LRANGE_600 (first 600 elements): 7552.87 requests per second MSET (10 keys): 16207.46 requests per second

redis-benchmark 会使CPU负载很高( 以下是单个实例的测试)

# -d 设定每次GET/SET的值的大小,单位byte(默认2b) ./redis-benchmark -p 11694 -q -d 1024

PING_INLINE: 35842.29 requests per second PING_BULK: 77519.38 requests per second SET: 73529.41 requests per second GET: 57142.86 requests per second INCR: 83333.34 requests per second LPUSH: 79365.08 requests per second LPOP: 16611.29 requests per second SADD: 84033.61 requests per second SPOP: 71942.45 requests per second

LPUSH (needed to benchmark LRANGE): 78125.00 requests per second LRANGE_100 (first 100 elements): 6756.76 requests per second LRANGE_300 (first 300 elements): 1847.06 requests per second LRANGE_500 (first 450 elements): 1133.14 requests per second LRANGE_600 (first 600 elements): 863.93 requests per second MSET (10 keys): 43859.65 requests per second

7. 简单集群

1. 主从复制

在从库的client输入命令

127.0.0.1:11694> slaveof 127.0.0.1 6379 OK

或者打开slave实例配置文件中的 slaveof 选项,这样在slave启动的时候会自动作为master的从库

查看这两个实例的日志 Slave

[20051] 13 Jan 17:12:56.196 * SLAVE OF 127.0.0.1:6379 enabled (user request)

[20051] 13 Jan 17:12:57.019 * Connecting to MASTER 127.0.0.1:6379 [20051] 13 Jan 17:12:57.019 * MASTER <-> SLAVE sync started

[20051] 13 Jan 17:12:57.019 * Non blocking connect for SYNC fired the event.

[20051] 13 Jan 17:12:57.020 * Master replied to PING, replication can continue...

[20051] 13 Jan 17:12:57.020 * Partial resynchronization not possible (no cached master)

[20051] 13 Jan 17:12:57.021 * Full resync from master: 9dc4e8eeeaa608d94e2f71aa700bee5ea2f0e443:1

[20051] 13 Jan 17:12:57.095 * MASTER <-> SLAVE sync: receiving 18 bytes from master

[20051] 13 Jan 17:12:57.097 * MASTER <-> SLAVE sync: Flushing old data [20051] 13 Jan 17:12:57.127 * MASTER <-> SLAVE sync: Loading DB in memory [20051] 13 Jan 17:12:57.127 * MASTER <-> SLAVE sync: Finished with success

Master

[20032] 13 Jan 17:12:57.020 * Slave asks for synchronization [20032] 13 Jan 17:12:57.020 * Full resync requested by slave. [20032] 13 Jan 17:12:57.020 * Starting BGSAVE for SYNC

[20032] 13 Jan 17:12:57.020 * Background saving started by pid 20211 [20211] 13 Jan 17:12:57.024 * DB saved on disk

[20211] 13 Jan 17:12:57.024 * RDB: 0 MB of memory used by copy-on-write [20032] 13 Jan 17:12:57.095 * Background saving terminated with success [20032] 13 Jan 17:12:57.095 * Synchronization with slave succeeded

从日志可以看出,当从库请求同步时:

①主库通过BGSAVE命令,异步将数据dump出成rdb文件(DB saved on disk)

②然后再将rdb文件传送给slave

③slave将数据加载到内存,同步数据。

在2.8版本之前,当数据同步时,如果网络发生中断,会全部数据重新dump重新传,现在可以续传了。

如果关闭了snapshotting,redis依然会读取配置文件中的配置来生成rdb文件进行主从同步间的数据交换,如果没有设置则使用默认值。

默认配置的从节点是无法进行写的,可以更改配置slave-read-only使其可写。 可写的从节点数据会保存在客户端本地而不会与主节点同步,从而会导致脏数据。

#主节点宕机

[30641] 14 Jan 11:03:02.275 * Connecting to MASTER 127.0.0.1:11694 [30641] 14 Jan 11:03:02.275 * MASTER <-> SLAVE sync started

[30641] 14 Jan 11:03:02.275 # Error condition on socket for SYNC: Connection refused

#主节点重启

[30641] 14 Jan 11:03:03.276 * Connecting to MASTER 127.0.0.1:11694 [30641] 14 Jan 11:03:03.276 * MASTER <-> SLAVE sync started

[30641] 14 Jan 11:03:03.276 * Non blocking connect for SYNC fired the event.

[30641] 14 Jan 11:03:03.276 * Master replied to PING, replication can continue...

[30641] 14 Jan 11:03:03.276 * Trying a partial resynchronization (request c7572af9af8b358c15b11517757e7f217eed66f9:4936).

[30641] 14 Jan 11:03:03.276 * Full resync from master: e1ab33bdb416d8226dfec8110cc05b7360c1d044:1

[30641] 14 Jan 11:03:03.277 * Discarding previously cached master state. [30641] 14 Jan 11:03:03.290 * MASTER <-> SLAVE sync: receiving 47 bytes from master

[30641] 14 Jan 11:03:03.290 * MASTER <-> SLAVE sync: Flushing old data [30641] 14 Jan 11:03:03.290 * MASTER <-> SLAVE sync: Loading DB in memory [30641] 14 Jan 11:03:03.290 * MASTER <-> SLAVE sync: Finished with success

[30641] 14 Jan 11:03:03.291 * Background append only file rewriting started by pid 6300

[6300] 14 Jan 11:03:03.295 * SYNC append only file rewrite performed

[6300] 14 Jan 11:03:03.295 * AOF rewrite: 0 MB of memory used by copy-on-write

[30641] 14 Jan 11:03:03.377 * Background AOF rewrite terminated with success

[30641] 14 Jan 11:03:03.377 * Parent diff successfully flushed to the rewritten AOF (0 bytes)

[30641] 14 Jan 11:03:03.377 * Background AOF rewrite finished successfully

当主节点下线时,从节点会不断重复上面三句而不会自动成为主节点。

在从节点的client端输入slave of no one 可以使其成为主节点(但是需要更改其他从节点的slaveof来与这个节点同步)。

这时候根据设置slave-serve-stale-data,从节点可以继续服务或者暂停服务,默认是继续服务。

当重启主节点时,slave会再次同步新master的数据(可以更换原来master的rdb或者aof文件来造成新旧数据不一)。

从而旧的slave的数据会被新的master数据覆盖。

2. 链式结构

普通的redis一主多从的方式存在单点失效问题。

根据redis的从节点可以作为其他从节点的主节点这个特性,可以使用链式结构一定程度缓解这种单点失效的问题。

主 - 从1 - 从2

在启动时,如果同时启动,在从1成为从节点之前从2是无法连接到从1的,查看log的信息如下

从1和主节点未完成同步时,从2请求同步的错误信息

[29329] 15 Jan 18:25:36.297 * Connecting to MASTER 127.0.0.1:11695

[29329] 15 Jan 18:25:36.297 * MASTER <-> SLAVE sync started

[29329] 15 Jan 18:25:36.297 * Non blocking connect for SYNC fired the event.

[29329] 15 Jan 18:25:36.298 * Master replied to PING, replication can continue...

[29329] 15 Jan 18:25:36.298 * Partial resynchronization not possible (no cached master)

[29329] 15 Jan 18:25:36.298 * Master does not support PSYNC or is in error state (reply: -ERR Can't SYNC while not connected with my master) [29329] 15 Jan 18:25:36.298 * Retrying with SYNC...

[29329] 15 Jan 18:25:36.298 # MASTER aborted replication with an error: ERR Can't SYNC while not connected with my master

[29329] 15 Jan 18:25:37.299 * Connecting to MASTER 127.0.0.1:11695

搭建链式结构式,参考的设计是

①主节点不开启持久化,负责写操作。 ②从1节点不开持久化,负责读操作。 ③从2节点开启持久化,负责备份。

当出现主节点宕机时,使从1节点变作主节点(在客户端输入命令 slaveof no one),从2节点负责原来从1节点的职责。

当主节点修复完毕后,作为从2节点的从节点,负责备份。链中其他主机出现问题时也按照这种思路重启服务。

链式结构的这种恢复机制能够一定程度缓解redis单点失效造成的写操作失效。

3. 使用sentinel

sentinel(哨兵)是redis自带的用以监视集群状态的服务。在redis的2.8.3版本中这个功能并没有加入release。所以默认的bin文件夹目录中也没有redis-sentinel。 在使用sentinel前,先根据前面的配置说明按照需要做好sentinel的配置。 测试sentinel的配置

daemonize yes

pidfile /home/wure/local/redis-2.8.3/run/sentinel.pid loglevel notice

logfile \port 11699

sentinel monitor mymaster 127.0.0.1 11694 1

sentinel down-after-milliseconds mymaster 30000 sentinel parallel-syncs mymaster 1

sentinel failover-timeout mymaster 180000

sentinel可以根据已经制定的主节点自动寻找其从节点。

sentinel比较适用于星型结构的集群,而对于链式结构的集群则无法做到自动检测和更换主从节点。

在一主四从的结构,已经开启集群服务的状态下启动sentinel,日志如下

[5456] 17 Jan 11:26:20.072 # Sentinel runid is 6f04e2c78a960700a8de2febe22af098945fd174

[5456] 17 Jan 11:26:20.073 * +slave slave 127.0.0.1:11695 127.0.0.1 11695 @ mymaster 127.0.0.1 11694

[5456] 17 Jan 11:26:20.073 * +slave slave 127.0.0.1:11696 127.0.0.1 11696 @ mymaster 127.0.0.1 11694

[5456] 17 Jan 11:26:20.073 * +slave slave 127.0.0.1:11697 127.0.0.1 11697 @ mymaster 127.0.0.1 11694

[5456] 17 Jan 11:26:20.073 * +slave slave 127.0.0.1:11698 127.0.0.1 11698 @ mymaster 127.0.0.1 11694

现在使用kill -9 结束主节点的进程,经过配置的延迟时间(sentinel down-after-milliseconds mymaster 30000),Sentinel会确认主机掉线 Sentinel转换主节点过程

[5456] 17 Jan 11:28:54.850 # +sdown master mymaster 127.0.0.1 11694 [5456] 17 Jan 11:28:54.850 # +odown master mymaster 127.0.0.1 11694 #quorum 1/1

[5456] 17 Jan 11:28:54.850 # +new-epoch 1

[5456] 17 Jan 11:28:54.850 # +try-failover master mymaster 127.0.0.1 11694

[5456] 17 Jan 11:28:54.850 # +vote-for-leader 6f04e2c78a960700a8de2febe22af098945fd174 1

[5456] 17 Jan 11:28:54.850 # +elected-leader master mymaster 127.0.0.1 11694

[5456] 17 Jan 11:28:54.850 # +failover-state-select-slave master mymaster 127.0.0.1 11694

[5456] 17 Jan 11:28:54.903 # +selected-slave slave 127.0.0.1:11697 127.0.0.1 11697 @ mymaster 127.0.0.1 11694

[5456] 17 Jan 11:28:54.903 * +failover-state-send-slaveof-noone slave 127.0.0.1:11697 127.0.0.1 11697 @ mymaster 127.0.0.1 11694

[5456] 17 Jan 11:28:54.980 * +failover-state-wait-promotion slave 127.0.0.1:11697 127.0.0.1 11697 @ mymaster 127.0.0.1 11694

[5456] 17 Jan 11:28:55.891 # +promoted-slave slave 127.0.0.1:11697 127.0.0.1 11697 @ mymaster 127.0.0.1 11694

[5456] 17 Jan 11:28:55.892 # +failover-state-reconf-slaves master mymaster 127.0.0.1 11694

[5456] 17 Jan 11:28:55.946 * +slave-reconf-sent slave 127.0.0.1:11696 127.0.0.1 11696 @ mymaster 127.0.0.1 11694

[5456] 17 Jan 11:28:56.959 * +slave-reconf-inprog slave 127.0.0.1:11696 127.0.0.1 11696 @ mymaster 127.0.0.1 11694

[5456] 17 Jan 11:28:56.959 * +slave-reconf-done slave 127.0.0.1:11696 127.0.0.1 11696 @ mymaster 127.0.0.1 11694

[5456] 17 Jan 11:28:57.024 * +slave-reconf-sent slave 127.0.0.1:11698 127.0.0.1 11698 @ mymaster 127.0.0.1 11694

[5456] 17 Jan 11:28:57.921 * +slave-reconf-inprog slave 127.0.0.1:11698 127.0.0.1 11698 @ mymaster 127.0.0.1 11694

[5456] 17 Jan 11:28:57.921 * +slave-reconf-done slave 127.0.0.1:11698 127.0.0.1 11698 @ mymaster 127.0.0.1 11694

[5456] 17 Jan 11:28:57.997 * +slave-reconf-sent slave 127.0.0.1:11695 127.0.0.1 11695 @ mymaster 127.0.0.1 11694

[5456] 17 Jan 11:28:57.998 * +slave-reconf-inprog slave 127.0.0.1:11695 127.0.0.1 11695 @ mymaster 127.0.0.1 11694

[5456] 17 Jan 11:28:59.030 * +slave-reconf-done slave 127.0.0.1:11695 127.0.0.1 11695 @ mymaster 127.0.0.1 11694

[5456] 17 Jan 11:28:59.087 # +failover-end master mymaster 127.0.0.1 11694

[5456] 17 Jan 11:28:59.087 # +switch-master mymaster 127.0.0.1 11694 127.0.0.1 11697

[5456] 17 Jan 11:28:59.087 * +slave slave 127.0.0.1:11696 127.0.0.1 11696 @ mymaster 127.0.0.1 11697

[5456] 17 Jan 11:28:59.090 * +slave slave 127.0.0.1:11698 127.0.0.1 11698 @ mymaster 127.0.0.1 11697

[5456] 17 Jan 11:28:59.092 * +slave slave 127.0.0.1:11695 127.0.0.1 11695 @ mymaster 127.0.0.1 11697

[5456] 17 Jan 11:28:59.094 * +slave slave 127.0.0.1:11694 127.0.0.1 11694 @ mymaster 127.0.0.1 11697

[5456] 17 Jan 11:29:29.151 # +sdown slave 127.0.0.1:11694 127.0.0.1 11694 @ mymaster 127.0.0.1 11697

Sentinel经过投票确定主机已挂,然后选出成为主节点的slave(可以配置slave的slave-priority来控制节点成为主节点的优先度),发生命令使其成为主节点。 然后再轮流向从节点发送更改配置信息,从而形成新的集群。 Sentinel在更改slave的配置时会影响配置文件,如下 新主节点的配置

# Generated by CONFIG REWRITE daemonize yes bind 127.0.0.1 save 900 1 save 300 10 save 60 10000

dir \maxmemory 500000000 appendonly yes

auto-aof-rewrite-min-size 64mb

在测试中sentinel的配置更改会将配置文件中的aof文件名删掉,导致aof持久化受影响。 当主节点修复完毕,再上线时,sentinel会将其作为新的主节点的从节点加入集群。

[5456] 17 Jan 11:46:49.444 # -sdown slave 127.0.0.1:11694 127.0.0.1 11694 @ mymaster 127.0.0.1 11697

[5456] 17 Jan 11:46:59.306 * +convert-to-slave slave 127.0.0.1:11694 127.0.0.1 11694 @ mymaster 127.0.0.1 11697

同样的Sentinel也会像上面一样更改原主节点的配置文件,并增加slaveof选项,使其启动时自动成为新主节点的从节点。

4. Redis Cluster

redis cluster目前还处于Alpha版本,没有测试。Redis Cluster是一个可以在多个节点之间交换数据的集群。

8. 应用场景

希望通过给出一些实际使用场景来。参考业务是否需要使用Redis。一般NoSql用来存储粉丝等关系有比较不错的效果,除了NoSQL本身的一些存储优点之外Redis也有他自己适用的由于Redis使用内存作为存储,其实例会占用大量内存,不适合用来存储大容量的或者非常长久的数据,持久化一般也用作故障恢复和数据备份。再者相比MySQL等还是一个相对没那么成熟的数据库,维护起来没那么方便。但是也由于其常驻内存,快速和高性能的特征在一些轻量级的业务里面还是有很大发挥空间的。

1. 计数器

Redis的计数是原子递增的,使用 INCR 和其他类似的命令(INCRYBY ZINCRY等)有挺不错的性能。

常见的应用类似于点赞,播放数,评论数,实时统计信息等变化比较快的计数。

2. 排行榜

使用Redis的有序集(SortSet)可以很方便的实现排行榜应用 。 典型的需求例如: --列出前100名高分 --列出某用户的排名

这些操作使用Redis都可以很方便的实现,而且执行时需求的资源很少。

#有新得分

ZADD leaderboard #取得前100名高分用户

ZREVRANGE leaderboard 0 99 #取得用户的排名

ZRANK leaderboard

3. 最新的N各数据

常见的最新N数据类似于“最新回复”,“最新添加”等之类的查询。 使用Redis的列表结构(List)可以很容易的实现。 一个评论列表的例子

#每次评论发表时,将评论的ID和内容添加到评论,(当这些评论几乎都是“长篇大论”时就不太适合使用redis了)

#如果担心ID和comment不好分离可以hashID然后取前N位,取决于你如何设计 LPUSH latest.comments

#我们将列表裁定为指定长度,因此Redis只需要保存最新的5000条评论 LTRIM latest.comments 0 5000

#最新的50条评论

LRANGE latest.comments 0 50

4. Pub/Sub(发布/订阅)构建的实时消息系统

Redis的Pub/Sub系统可以构建实时的消息系统 。 Pub/Sub例子

# 订阅 news.* 和 tweet.* 两个模式

# 第 1 - 6 行是执行 psubscribe 之后的反馈信息 # 第 7 - 10 才是接收到的第一条信息 # 第 11 - 14 是第二条 # 以此类推。。。

redis> psubscribe news.* tweet.*

Reading messages... (press Ctrl-C to quit)

1) \返回值的类型:显示订阅成功 2) \订阅的模式

3) (integer) 1 # 目前已订阅的模式的数量

1) \2) \3) (integer) 2

#在另一个客户端中输入 publish news.it \1) \返回值的类型:信息 2) \信息匹配的模式

3) \信息本身的目标频道 4) \信息的内容

#在另一个客户端中输入 publish tweet.me \1) \2) \3) \4) \

#在另一个客户端中输入 publish tweet.joe \1) \2) \3) \4) \

#在另一个客户端中输入 publish news.life \1) \2) \3) \

4) \

5. 缓存(以及和Memcached的比较)

加强版的Memcached,丰富的数据类型,性能方面和Memcached相当。 Stackoverflow上一个问题是< Is memcached a dinosaur in comparison to Redis?>

Redis作者对改问题的回答如下:

没有必要过多的关心性能,因为二者的性能都已经足够高了。由于Redis只使用单核,而

Memcached可以使用多核,所以在比较上,平均每一个核上Redis在存储小数据时比Memcached性能更高。而在100k以上的数据中,Memcached性能要高于Redis,虽然Redis最近也在存储大数据的性能上进行优化,但是比起Memcached,还是稍有逊色。说了这么多,结论是,无论你使用哪一个,每秒处理请求的次数都不会成为瓶颈。(比如瓶颈可能会在网卡)

如果要说内存使用效率,使用简单的key-value存储的话,Memcached的内存利用率更高,而如果Redis采用hash结构来做key-value存储,由于其组合式的压缩,其内存利用率会高于Memcached。当然,这和你的应用场景和数据特性有关。

如果你对数据持久化和数据同步有所要求,那么推荐你选择Redis,因为这两个特性Memcached都不具备。即使你只是希望在升级或者重启系统后缓存数据不会丢失,选择Redis也是明智的。

当然,最后还得说到你的具体应用需求。Redis相比Memcached来说,拥有更多的数据结构和并支持更丰富的数据操作,通常在Memcached里,你需要将数据拿到客户端来进行类似的修改再set回去。这大大增加了网络IO的次数和数据体积。在Redis中,这些复杂的操作通常和一般的GET/SET一样高效。所以,如果你需要缓存能够支持更复杂的结构和操作,那么Redis会是不错的选择。

9. 参考资料

1. Redis命令参考

一份关于Redis命令的详细介绍,类似于WIKI。client端的命令都可以找到,免费的翻译,作者会跟进官网的文档更新,并且解答相关疑问,赞!

2. Redis经验谈

文章时间大概为2013年1月,文章来自新浪。里面说新浪是世界最大的Redis用户,并介绍了在开发和运维中遇到的几个问题以及解决思路。

3. Redis监控技巧

2013年1月,虽然文章内容基本没介绍监控技巧,但是列出了几个用于监控Redis的工具。由于Redis一般集群的单点失效,以及相对于MySQL还不那么成熟和稳定,监控显得尤为重要。

4. 其他

redis中的key是可以使用空格的,特别是在使用库进行插入操作时。如果这样做,在读取的时候需要为key加上引号(如 get \),在PHP中就没办法读取了。

本文来源:https://www.bwwdw.com/article/vrfw.html

Top