Redis

Redis介绍

Redis是一个基于BSD开源的高性能键值缓存服务,支持数据结构string,list,hash,set,sorted,bitmaps,hyperloglog。

官方站点:http://www.redis.io

Redis特点:

kv存储,存储在内存中,

redis可以持久化周期性的存储在磁盘上(冗余作用)

​ 快照,内存数据异步传输到磁盘(RDB)。

​ AOF,每次写操作附加在文件中。

​ Master-Slave方式,主写,从读。

redis支持主从模式,借助哨兵(Sentinel实现一定意义上的高可用)

redis3.0开始支持群集(分布式)

Redis和Memcache区别

memcache是一个分布式内存对象缓存系统,且不可持久化,redis支持 持久存储

memcache是基于LRU cache(最近最少使用) ,redis有不同特性以及跟多数据类型

memcache是多线程,redis是单线程,两者的性能几乎相同

Redis优点:

​ 丰富(资料形式)操作(Hashs,Lists,Sorted,Sets,HyperLoglog)

​ 内建replication及cluster

​ 就地更新(in-place update)操作

​ 支持持久化(磁盘),避免雪崩

Memcache优点

​ 多线程(善用多核cpu,更少堵塞操作)

​ 更少内存开销

​ 更少内存分配压力

​ 内存碎片更少

其他资料

常见的存储系统分为三类:

RDBMS:如Oracle,SQLServer,MySQL。

NoSQL:如Hbash,MongoDB,Redis

NewSQL:分布式关系型事物系统

NoSQL四种流派:

键值NoSQL:如Redis,Memcache

列族NoSQL:如Hbash,MongoDB,Redis

文档NoSQL:MongoDB

图形NoSQL:Neo4J

Redis案例:

​ 一百万的key,内存使用约100M

​ 单线程,虽然单线程,却能承受500k的并发请求

Redis安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#安装redis需要安装jemalloc内存管理工具(google研发)
yum install tcl

#下载源码地址:http://download.redis.io/releases/

#安装步骤
tar zxvf redis-VERSION.tar.gz
cd redis-VERSION
make

#安装完成后,生成如下文件
redis.conf redis配置文件
sentinel.conf 主从架构配置文件
src/redis-server redis服务端
src/redis-cli redis客户端
src/redis-check-aof redis检查工具(AOF)
src/redis-check-dump redis检查工具(快照)
src/redis-benchmark redis性能工具
src/redis-sentinel redis主从架构提供高性能工具
Redis基本配置文件
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
cat redis.conf | grep -v "^#" | grep -v "^$"
#全局设置
####INCLUDE####
daemonize no
pidfile /var/run/redis.pid
port 6379
tcp-backlog 511 #tcp-backlog长度 (backlog是个队列缓冲,tcp通常会有backlog)
bind 126.0.0.1 192.168.2.21
unixsocket /tmp/redis.sock #如果不使用tcp方式,可以直接使用unixsocket方式,效率比tcp高
timeout 0 #客户端连接超时时间(0表示不会超时),根据需求设定
tcp-keepalive 0
loglevel notice
logfile "/var/log/redis/redis.log"
syslog-enabled no #是否启用系统日志
syslog-ident redis #日志识别
syslog-facility local0 #日志设施
databases 16 #redis是否支持多内部数据集合,默认放在0,在redis集群中只支持16

#快照,持久化配置信息
####SNAPSHOTTING####
#格式 save seconds changes
save 900 1 #900s(15分钟),如果有1个键值发生改变,就存储
save 300 10 #300s(5分钟),如果有10个键值发生改变,就存储
save 60 10000 #60s(1分钟),如果有1000个键值发生改变,就存储
#save "" #取消注释表示禁用持久化功能
stop-writes-on-bgsave-error yes #在RDB方式下,如果使用bgsave保存是否检查如果检查错误是否停止
rdbcompression yes #RDB是否压缩,压缩会消耗cpu资源
rdbchecksum yes #是否对rdb进行校验码检测
dbfilename dump.rdb #DBfile文件名称
dir ./ #指明文件保存目录

#主从配置信息
####REPLICATION####
#slaveof <masterip> <master-ports> #如果这个注释,说明是master模式,如果开启需要指定master的IP和端口
slave-serve-stale-data yes #是否从服务器使用过期数据
slave-read-only yes #是否只读,如果slaveof注释,这里是没有用的,
repl-diskless-sync no #是否给予diskess同步,如果网络快,磁盘写慢建议开启
repl-diskless-sync-delay 5 #延迟时间多久
repl-disable-tcp-nodelay no #tcp nodelay功能
slave-priority 100 #slave优先级
min-slaves-to-write 3 #从节点至少3个节点,将禁止主服务器写请求
min-slaves-max-lag 10 #从节点如果相差10s,主服务器拒绝执行写入操作

#安全相关配置
####SECURITY#####
requirepass foobared #认证密码foobared
rename-command CONFIG ""

#并发相关配置
####LIMITS######
maxclients 10000 #最大并发数量,多少客户端连接
maxmemory <bytes> #每个客户端内存使用量
maxmemory-policy noeviction
maxmemory-samples 5

#AOF持久化
####APPEND ONLY MODE####
appendonly no #是否使用AOF功能,yes使用,这里是禁用
appendfilename "appendonly.aof" #AOF文件名
appendfsync {always | everysec | no} #AOF追加方式{always没接受一条写一条,everyse每秒写一条,no 表示根据系统调用来写}
no-appendfsync-on-rewrite no #如果是yes表示重写的时候对新写的操作是存在内存
auto-aof-rewrite-percentage 100 #aof文件增长了100%也就是两倍,触发重写操作
auto-aof-rewrite-min-size 64mb #如果重写大小达到64M就重写
aof-load-truncated yes
#redis加载aof文件,发现末尾命令不完整自动截掉,成功加载前面正确数据,如果设置no,遇到不完整redis启动失败,redis-check-aof修复

#lua脚本设置
####LUA SCRIPTING####
lua-time-limit 5000

#redis群集设置
####REDIS CLUSTER####
cluster-enabled yes
cluster-config-file nodes-6379.conf
cluster-node-timeout 15000
cluster-slave-validity-factor 10
cluster-require-full-coverage yes

#慢查询日志设置
####SLOW LOG####
slowlog-log-slower-than 10000
slowlog-max-len 128

#监控设置
####LATENCY MONITOR####
latency-monitor-threshold 0

#事件通知设置 跟发布订阅有关
####EVENT NOTIFICATION####
notify-keyspace-events ""

#高级设置
####ADVANCED CONFIG####
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
aof-rewrite-incremental-fsync yes
Redis命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#启动redis前需要设置
echo 'vm.overcommit_memory=1' >> /etc/sysctl.conf
#内存不足的情况下,后台程序save可能失败,建议将其改成1
echo never > /sys/kernel/mm/transparent_hugepage/enabled
#如果使用透明大页,可能导致redis延迟和内存使用问题
echo 511 > /proc/sys/net/core/somaxconn
#tcp backlog值

#启动
redis-server &

-h HOST : 连接的主机地址或主机名
-p PORT :连接的端口
-s socket : 指定套接字
-a password : 指定连接密码
-r <repeat> : 指定命令运行多次


overcommit_memory参数说明
设置内存分配策略,可以设定0、1、2
0 表示内核将检查是否有足够的内存供应进程应用,如果没有足够的可用内存,内存允许申请;否则内存申请失败,并将错误返回给应用进程
1 表示内核允许分配所有的物理内存,而不管当前的内存状态如何
2 表示内核允许分配超过所有物理内存和交换空间总内存
注:redis在dump数据时,会fork出一个子进程,理论上child进程所占有内存和parent是一样的,如果parent占用内存为8G,这个时候同样分配8G的内存给child,如果内存无法负担,往往会造成redis服务的down机或者IO负载过高,导致效率下降,所有这里比较优化的内存策略应该为1(表示内核运行分配所有的物理内存,而不管当前内存状态)
Redis基本操作
1
2
3
4
5
6
7
8
9
10
11
#redis-cli -h 127.0.0.1
127.0.0.1:6379>help
Type help @<group>
help <tab>
127.0.0.1:6379> help @connection #帮助命令
127.0.0.1:6379> help @STRING #获取字符串操作帮组
127.0.0.1:6379> ping #测试
PONG
127.0.0.1:6379> echo 'hello world' #回显
"hello world"
127.0.0.1:6379> QUIT #退出
server

主要设置redis服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
127.0.0.1:6379> CLIENT SETNAME localconn     #设置名称
OK

127.0.0.1:6379> CLIENT GETNAME #获取名称
"localconn"

127.0.0.1:6379> CLIENT LIST #client列表
"id=2 addr=127.0.0.1:38521 fd=6 name= age=272 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client"

127.0.0.1:6379> CLIENT KILL ip:port #剔除一个client

127.0.0.1:6379> INFO #获取信息

127.0.0.1:6379> INFO MEMORY #获取memory信息,如获取cpu,执行info cpu

127.0.0.1:6379> CONFIG RESETSTAT #重置所有配置信息

127.0.0.1:6379> CONFIG SET #设置仅在内存生效

127.0.0.1:6379> CONFIG REWRITE #同步到配置文件中,如果用CONFIG SET方式设置redis参数需要REWRITE同步到配置文件中
SELECT

使用0的命名空间,默认是0,使用SELECT [空间名进行跳转最大16个]

同一个名称空间不能使用相同的键值

1
2
3
4
5
127.0.0.1:6379>SELECT 0
127.0.0.1:6379>SELECT 15
OK
127.0.0.1:6379[15]>SELECT 16
(error)ERR invalid DB index
SET
1
2
3
4
5
6
7
8
9
10
11
12
13
127.0.0.1:6379> help set
格式:SET key value [EX seconds] [PX milliseconds] [NX|XX]
#set key value [过期时间] [标识] [如果键值不存在才会创建|如果键值存在覆盖]

127.0.0.1:6379> set 01 name
OK
127.0.0.1:6379> set 02 name2
OK
127.0.0.1:6379> set 01 name nx
(nil)
127.0.0.1:6379> set 03 name nx
OK
127.0.0.1:6379> set 04 name ex 5 设置5s过期
GET
1
2
3
4
5
6
127.0.0.1:6379> get 02
"name2"
127.0.0.1:6379> get 01
"name"
127.0.0.1:6379> get 04 过期后会提示nil
(nil)
APPEND
1
2
3
4
5
127.0.0.1:6379> append 01 haha 
(integer) 8

127.0.0.1:6379> get 01
"namehaha"
STRLEN
1
2
127.0.0.1:6379> STRLEN 01 	#长度
(integer) 8
INCR

只针对整数增加生效

1
2
3
4
5
6
7
8
127.0.0.1:6379> set count 0 
OK

127.0.0.1:6379> INCR count #增加
(integer) 1

127.0.0.1:6379> INCR count
(integer) 2
DECR
1
2
3
4
5
127.0.0.1:6379> DECR  count #减
(integer) 1

127.0.0.1:6379> DECR count
(integer) 0
事物

通过MULTI,EXEC,WATCH等命令实现事物功能: redis事物只是将一个或者多个命令打包一个操作服务端按顺序执行机制
redis事务不支持回滚操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
127.0.0.1:6379> MULTT	#启动一个事务
OK
127.0.0.1:6379> SET IP 192.168.2.21
QUEUED
127.0.0.1:6379> GET IP
QUEUED
127.0.0.1:6379> SET PORT 8080
QUEUED
127.0.0.1:6379> GET PORT
QUEUED
127.0.0.1:6379> EXEC #执行事务,一次性将事务中的所有操作执行完成后返回给客户端
1) OK
2) "192.168.2.21"
3) OK
4) "8080"
清空操作:)

for _ in range(1000):print(“不要在生产使用”)

1
2
FLUSHDB          清空当前数据库
FLUSHALL 清空所有数据库0~15
WATCH 乐观锁

WATCHEXEC命令执行之前,用于监视指定数量的键,如果监视中的某任意键数据被修改,则服务器拒绝执行事务

1
2
3
4
5
6
7
8
9
10
127.0.0.1:6379> WATCH IP	#监控IP键
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SET IP 10.0.0.1
QUEUED
127.0.0.1:6379> GET IP
QUEUED
127.0.0.1:6379> EXEC #如果在MULTI之后,EXEC之前有个客户端修改了IP,这里EXEC的话,就会拒绝事务
(nil)

Redis认证

如果使用认证功能需要编辑配置文件找到requirepass

1
2
3
4
5
6
7
8
9
10
11
12
格式:requirepass PASS 
$vi /etc/redis.conf
requirepass zhuxyid

$redis-server /etc/redis.conf
redis-cli -h 127.0.0.1
127.0.0.1:6379> select 0
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth zhuxyid
OK
127.0.0.1:6379> select 0
OK

Redis发布订阅

频道:消息队列

1
2
3
4
5
6
7
8
9
10
11
12
SUBSCRIBE:订阅一个或多个队列
PUBLISH:向频道发布消息

例子
>SUBSCRIBE www.zhuxyid.com #订阅www.zhuxyid.com频道
>PUBLISH www.zhuxyid.com hello #向www.zhuxyid.com频道发送

PSUBSCRIBE:订阅多个队列
>PSUBSCRIBE www.zhuxy.i[to] #订阅www.zhuxy.io 和 www.zhuxy.it频道

>PUBLISH www.zhuxy.io hello io
>PUBLISH www.zhuxy.it hello it

Redis持久化

#####RDB和AOF

RDB:snapshot,二进制格式:被事先定制的策略,如save 900 1,周期性将数据保存到磁盘:数据文件默认为dump.rdb;

​ 客户端也可以使用SAVE或者BGSAVE命令启动快照保持机制

​ SAVE:在主线程中保存快照,此时会堵塞所有用户请求,如果数据量大,严重影响性能

​ BGSAVE:异步,不会被堵塞,只是创建子进程,保存到临时文件,主进程依然处理客户端请求

AOF:Append Only File. 记录每一次写操作至指定的文件尾部实现持久化,当redis重启时,可通过重新执行文件中的命令,在内存中重建数据库。

​ BGREWRITEAOF:AOF文件重写;

​ 不会读取正在使用的AOF文件,在通过将内存中的数据以命令的方式保存在临时文件中,完成后替换原来的AOF文件

RDB:

配置:redis-cli可以用config get dir查看

1
2
3
4
5
6
7
8
9
配置如下:
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir ./
AOF:

AOF重写过程:

redis主进程调用fork生成子进程

子进程根据redis内存中的数据创建数据库重建命令列于临时文件中

父进程继承clinet请求,并会把这些请求写操作继续追加至原来的AOF文件,额外的这些新的请求会被放置于一个缓冲队列中

子进程重写完成会通知父进程,父进程会把缓冲中的命令写到临时文件中

父进程用临时文件替换老的AOF文件

1
2
3
4
5
6
7
8
配置如下:
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
RDB和AOF注意事项:

redis如果同时使用两种持久化会导致IO影响大。

需要注意的是,就算可以持久化也不要忘记备份,万一磁盘坏了持久化也没什么用,对redis持久化文件进行备份。

RDB和AOF同时使用:

BGSAVE和BGREWRITEAOF不会同时执行

在redis服务器启动用于恢复数据时,优先是有AOF,因为RDB是周期性的,数据不能保证为最新的

Redis复制

特点:

一个master可以有多个slave

支持链式复制:slave可以有多个slave

master以非阻塞方式同步至slave

master&slave工作原理:

启动一个slave,会请求同步master,master启动子进程,将快照保存在文件中,将文件传送给slave,slave接受文件保存本地加载至内存,完成同步

配置过程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
配置master&slave
192.168.2.21(master)
修改配置文件
bind 192.168.2.21
启动redis

192.168.2.23(slave)
修改配置文件
bind 192.168.2.23
slaveof 192.168.2.21 6379
启动redis

或者直接在redis-cli输入slaveof 192.168.2.21 6379
建议master写,slave读。

如果master使用requirepass开启认证功能,从服务器要使用masterauth <PASSWORD> 来连入服务请求使用此密码进行认证
主从复制缺点:

如果redismaster离线了,怎么办?
可以使用redis-sentinel(主从架构实现高可用)

Redis sentinel

sentinel主要作用:

用于管理多个redis服务实现HA

监控,通知,故障转移

留言协议,投票协议。

启动流程:

服务器自身初始化,运行redis-server中专用于sentinel功能代码
初始化sentinel状态,根据给定配置文件,初始化监控的master服务器列表
创建连向master的链接

1
2
3
4
5
依赖配置文件sentinel.conf
redis-sentinel /path/to/sentinel

启动
redis-server /path/to/sentinel --sentinel
sentinel配置文件说明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
port 26379
dir /tmp
sentinel monitor mymaster 127.0.0.1 6379 2
#格式:sentinel monitor <master-name> <ip> <port> <quorum>
#<quorum>票数,sentinel至少两票启动,如果只有一个则改成1> !!如果还有其他应用使用redis主从也可以做监控,改变master-name就可以

sentinel down-after-milliseconds mymaster 3000
#格式:sentinel down-after-milliseconds <master-name> <milliseconds>
#判断节点离线超过多少秒认为离线的。单位毫秒,

sentinel parallel-syncs mymaster 1
#格式:sentinel parallel-syncs <master-name> <numslaves>
#执行故障转移时候允许多少从服务器向新的主服务器同步请求

sentinel failover-timeout mymaster 180000
#格式:sentinel failover-timeout <mymaster> <failover-timeout>
#当主服务器出现故障时候,从服务器提升主服务器的超时时间,单位毫秒
sentinel下线机制:

主观下线和客观下线

主观下线:一个sentinel实例判断出某个节点下线

客观下线:多个sentinel节点协商后判断出某节点下线

专用命令
1
2
3
4
5
6
redis-clit -h sentinelip -p sentinelport 
SENTINEL masters #列出所有主服务器
SENTINEL slaves <master name> #获取当前redis示例中的从节点信息
SENTINEL get-master-addr-by-name <master name> #直接获取当前redis实例主节点IP地址和端口
SENTINEL reset #重置
SENTINEL failover <master name> #手动切换
示例
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
环境如下:
172.16.36.70 : redis主节点
172.16.36.71 : redis从节点
172.16.36.72 : redis从节点
172.16.36.74 : sentinel节点1
172.16.36.75 : sentinel节点2
172.16.36.76 : sentinel节点3

####配置redis主节点
操作主机: 172.16.36.70
#vim /etc/redis.conf
bind 172.16.36.70
daemonize yes
#启动服务
#redis-server /etc/redis.conf

####配置redis从节点
#操作主机: 172.16.36.71
#vim /etc/redis.conf
bind 172.16.36.71
daemonize yes
#启动服务
#redis-server /etc/redis.conf
#配置主节点信息
#redis-cli -h 172.16.36.71 -p 6379
172.16.36.71:6379> SLAVEOF 172.16.36.70 6379 OK

####配置redis从节点
#操作主机: 172.16.36.72
#vim /etc/redis.conf
bind 172.16.36.72
daemonize yes
#启动服务
#redis-server /etc/redis.conf
#配置主节点信息
#redis-cli -h 172.16.36.71 -p 6379
172.16.36.71:6379> SLAVEOF 172.16.36.70 6379 OK

####配置sentinel节点
#操作主机: 172.16.36.74
#vim /etc/redis-sentinel.conf
port 26379
dir "/tmp"
daemonize yes
sentinel monitor mymaster 172.16.36.70 6379 2
sentinel parallel-syncs mymaster 3
sentinel down-after-milliseconds mymaster 30000
sentinel failover-timeout mymaster 180000
#启动服务
#redis-sentinel /etc/redis-sentinel.conf
#查看服务启动状态 users:(("master",2112,14))

#操作主机: 172.16.36.75
# vim /etc/redis-sentinel.conf
port 26379
dir "/tmp"
daemonize yes
sentinel monitor mymaster 172.16.36.70 6379 2
sentinel parallel-syncs mymaster 3
sentinel down-after-milliseconds mymaster 30000
sentinel failover-timeout mymaster 180000
#启动服务
#redis-sentinel /etc/redis-sentinel.conf
#查看服务启动状态 users:(("master",2112,14))

操作主机: 172.16.36.76
# vim /etc/redis-sentinel.conf
port 26379
dir "/tmp"
daemonize yes sentinel monitor mymaster 172.16.36.70 6379 2
sentinel parallel-syncs mymaster 3
sentinel down-after-milliseconds mymaster 30000
sentinel failover-timeout mymaster 180000
启动服务 redis-sentinel /etc/redis-sentinel.conf
查看服务启动状态 users:(("master",2112,14))



#这里有个问题,当一个redis挂掉后,如果连接主redis?可以使用vip(虚拟ip(keepalived)来实现)
$sentinel client-reconfig-script myredis /opt/notify_myredis.sh
$more /opt/notify_myredis.sh
#!/bin/bash
MASTERIP=$6 #第六个参数是redis的ip地址
LOCALIP='192.168.0.101' #另外一台按需填写
VIP='192.168.0.100' #client连接的redis
NETMASK='24'
INTERFACE='eth1'
if [ ${MASTERIP} = ${local_IP} ];then
/sbin/ip addr add ${VIP}/${NETMASK} dev $INTERFACE #将vip绑定到服务器上
/sbin/arping -q -c 3 -A ${VIP} -I ${INTERFACE}
exit 0
else
/sbin/ip addr del ${VIP}/${NETMASK} dev $INTERFACE #删除
exit 0
fi
exit 1 #如果返回1,sentinel会一致执行这个脚本
看完了?赏个鸡腿钱,谢谢老板!