redis快速入门手册

Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。从2010年3月15日起,Redis的开发工作由VMware主持。它在企业级应用中被广泛的使用,已经成为web开发者必须具备的技能之一。

目录

1. 概述

1.1 介绍

1.2 优缺点

1.3 应用场景

1.4 Raft协议

1.5 安装

2. 特性

2.1 数据库

2.2 文件夹

2.3  管道

2.4 发布订阅

2.5 事务

2.6 文件导入

3. 命令集

3.1 常见命令

3.1.1 connection

3.1.2 server

3.1.3 key

3.2 string

3.2.1 bitmap

3.3 list

3.4 hash

3.5 set

3.6 sorted set

3.7 hyperLogLog

3.8 geospatial

4. 持久化配置

4.1 rdb

4.2 aof

4.3 数据恢复

5. 集群 

5.1 redis sentinel

5.2 redis cluster

6. 客户端 Jedis

7. 示例:位图法统计活跃用户

8. Q&A


1. 概述

1.1 介绍

Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。

1.2 优缺点

优点:

  1. 性能极高,Redis能支持10万每秒的读写频率
  2. 丰富的数据类型及对应的操作
  3. Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行,也即支持事务
  4. 丰富的特性,Redis还支持publish/subscribe,key过期等特性

性能(以下摘自官方测试描述):

在50个并发的情况下请求10W次,写的速度是11W次/s,读的速度是8.1w次/s。测试环境:

1)50个并发,请求10W次

2)读和写大小为256bytes的字符串

3)Linux2.6 Xeon X3320 2.5GHz的服务器上

4)通过本机的loopback interface接口上执行

缺点:

  1. Redis具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复;
  2. 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性;
  3. Redis的主从复制采用全量复制,复制过程中主机会fork出一个子进程对内存做一份快照,并将子进程的内存快照保存为文件发送给从机,这一过程需要确保主机有足够多的空余内存。若快照文件较大,对集群的服务能力会产生较大的影响,而且复制过程是在从机新加入集群或者从机和主机网络断开重连时都会进行,也就是网络波动都会造成主机和从机间的一次全量的数据复制,这对实际的系统运营造成了不小的麻烦;
  4. Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费;

1.3 应用场景

  • Counting(计数)
  • Reverse cache(反向cache)
  • Top 10 list
  • Last Index
  • Relation List/Message Queue
  • Fast transaction with Lua
  • Instead of Memcache

1.4 Raft协议

redis sentinel实现了著名的RAFT选主协议

1.5 安装

wget http://download.redis.io/releases/redis-3.2.5.tar.gz
tar xzf redis-3.2.5.tar.gz
cd redis-3.2.5
make
#启动服务器端:
src/redis-server
#启动客户端:
src/redis-cli
#redis> set foo bar
#OK
#redis> get foo
#"bar"

2. 特性

2.1 数据库

Redis中默认设置了16个数据库,编号为0~15,可以通过修改配置文件来修改数据库个数.可以使用select(databaseNo)方法来选择使用的数据库。

使用场景:

一台服务器上都快开启200个redis实例了,看着就崩溃了。这么做无非就是想让不同类型的数据属于不同的应用程序而彼此分开。那么,redis有没有什么方法使不同的应用程序数据彼此分开同时又存储在相同的实例上呢?就相当于mysql数据库,不同的应用程序数据存储在不同的数据库下。

2.2 文件夹

Redis的Key中使用冒号作为分隔符,在RedisDesktopManager中查看可以看到分级的文件夹.需要注意的是,这种方式只有在客户端中查看才能看到分级效果,实际的Key并没有变化。

2.3  管道

Redis是一种基于客户端-服务端模型以及请求/响应协议的TCP服务。

而管道(Pipelining):学习如何一次发送多个命令,节省往返时间。客户端向服务端发送一个查询请求,并监听Socket返回,通常是以阻塞模式,等待服务端响应。

2.4 发布订阅

发布端: publish 频道名称 发布内容

订阅端: subscribe 频道名称

Redis的发布/订阅目前是即发即弃(fire and forget)模式的,因此无法实现事件的可靠通知。也就是说,如果发布/订阅的客户端断链之后又重连,则在客户端断链期间的所有事件都丢失了。

数据库与作用域

发布/订阅与key所在空间没有关系,它不会受任何级别的干扰,包括不同数据库编码。 发布在db 10,订阅可以在db 1。 如果你需要区分某些频道,可以通过在频道名称前面加上所在环境的名称(例如:测试环境,演示环境,线上环境等)。

模式匹配订阅

Redis 的Pub/Sub实现支持模式匹配。客户端可以订阅全风格的模式以便接收所有来自能匹配到给定模式的频道的消息。比如:PSUBSCRIBE news.*

2.5 事务

Redis与 mysql事务的对比

 

Mysql

Redis

开启

start transaction

multi

语句

普通sql

普通命令

失败

rollback

discard

成功

commit

exec

注:Redis使用的是队列,没有回滚只有取消(队列)

Redis的事务中,Watch命令启用的是乐观锁,只负责监测key没有被改动,如果有其他客户端修改了key的值,当前客户端的事务就要失败。

watch key1 key2  ... keyN

监听key1 key2..keyN有没有变化,如果有变, 则事务取消

unwatch

取消所有watch监听

2.6 文件导入

导入使用cat和redis-cli命令组合,使用--pipe 这个参数来启用pipe协议,它不仅仅能减少返回结果的输出,还能更快的执行指令。更多

3. 命令集

3.1 常见命令

3.1.1 connection

ping

测试连接是否存活

select 1

选择数据库。Redis数据库编号从0~15,我们可以选择任意一个数据库来进行数据的存取

quit

退出连接

echo HongWan

在命令行打印一些内容

3.1.2 server

bgrewriteaof

执行一个 AOF文件重写操作

bgsave

在后台异步(Asynchronously)保存当前数据库的数据到磁盘。命令执行之后立即返回 OK ,然后 Redis fork 出一个新子进程,原来的 Redis 进程(父进程)继续处理客户端请求,而子进程则负责将数据保存到磁盘,然后退出。

config get parameter

config set parameter

获取/设置服务器的配置参数

dbsize

返回当前数据库中key的数目

flushdb

删除当前选择数据库中的所有key

flushall

删除所有数据库中的所有key

info

返回Redis服务器的各种信息和统计数值

monitor

实时打印出 Redis 服务器接收到的命令,调试用

slowlog

用来记录查询执行时间的日志系统。slow log保存在内存里面,读写速度非常快,因此你可以放心地使用它,不必担心因为开启slow log而损害Redis的速度。

CONFIG SET slowlog-log-slower-than 1000 #大于1000微秒的查询

CONFIG SET slowlog-max-len 1000 #最多保存1000条日志(FIFO队列)

SLOWLOG GET

time

返回当前服务器时间

3.1.3 key

del key1 key2...keyn

删除多个key,返回正确删除key的数量

dump key

序列化给定 key ,并返回被序列化的值

exists key

判断key师傅存在,返回1/0

expire key 10

设置key的有效10秒数,pexpire类似(代表毫秒)

keys 通配(*,?,[])

查找

keys *       查询全部

keys site  查询某个key        

keys s*     查询以s开头的key    

keys ????         查询四个字符的key

keys sit[e|t]     查询尾字符为et的字符

move key db

redis.conf默认开启了16databases

move site 1  #site移动到一号服务器

persist key

设置为永久有效

pttl key

以毫秒为单位返回 key 的剩余生存时间

ttl key

以秒为单位返回 key 的剩余生存时间, -1代表永久生效,-2代表不存在的key

rename key newkey

重命名key(会覆盖)

renamenx key newkey

重命名key,如果new key不存在成功(1),存在不成功(0)

restore key ttl serialized-value

反序列化给定的序列化值,并将它和给定的 key 关联

sort key desc

返回或保存给定列表、集合、有序集合 key 中经过排序的元素

type key

返回key存储的值得类型(string/link/set/order/set/hash)

scan

异步支持命令,与keys作用类似,但不会阻塞服务器

3.2 string

  • 常用命令

set、get、decr、incr、mget、setnx等

  • 应用场景

String是最常用的一种数据类型,普通的key/value存储

  • 实现方式

String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr,decr等操作时会转成数值型进行计算,此时redisObject的encoding字段为int.

  • 命令及描述

set key value
设置指定 key 的值

get key
获取指定 key 的值。

getrange key start end
返回 key 中字符串值的子字符

getset key value
将给定 key 的值设为 value ,并返回 key 的旧值(old value)。

getbit key offset
对 key 所储存的字符串值,获取指定偏移量上的位(bit)。

mget key1 [key2..]
获取所有(一个或多个)给定 key 的值。

setbit key offset value
对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。

setex key seconds value
将值 value 关联到 key ,并将 key 的过期时间设为 seconds (以秒为单位)。

setnx key value
只有在 key 不存在时设置 key 的值。

setrange key offset value
用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始。

strlen key
返回 key 所储存的字符串值的长度。

mset key value [key value ...]
同时设置一个或多个 key-value 对。

msetnx key value [key value ...]
同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。

psetex key milliseconds value
这个命令和 setex 命令相似,但它以毫秒为单位设置 key 的生存时间,而不是像 setex 命令那样,以秒为单位。

incr key
将 key 中储存的数字值增一。

incrby key increment
将 key 所储存的值加上给定的增量值(increment) 。

incrbyfloat key increment
将 key 所储存的值加上给定的浮点增量值(increment) 。

decr key
将 key 中储存的数字值减一。

decrby key decrement
key 所储存的值减去给定的减量值(decrement) 。

append key value
如果 key 已经存在并且是一个字符串, append 命令将 指定value 追加到改 key 原来的值(value)的末尾。

3.2.1 bitmap

Bitmaps本身不是一种数据结构,实际上就是字符串,但是它可以对字符串的位进行操作。可以把Bitmaps想象成一个以位为单位数组,数组中的每个单元只能存0或者1,数组的下标在bitmaps中叫做偏移量。单个bitmaps的最大长度是512MB,即2^32个比特位。

getbit key offset value

设置值,Value(1表示访问,0表示未访问)

getbit key offset

获取值

bitcount key [start] [end]

获取指定范围值为1的个数

bitop and destkey key[key…]

bitop or…

bitop not

bitop xor

交集、并集、非集、异或的数量

3.3 list

简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。一个列表最多可以包含 232- 1 个元素 (4294967295, 每个列表超过40亿个元素)。

  • 常用命令

lpush、rpush、lpop、rpop、lrang等;

  • 应用场景

Redis list应用场景非常多,也是Redis最重要的数据结构之一,比如twitter的关注列表,粉丝列表等都可以用Redis的list结构来实现;

  • 实现方式

Redis list的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用的这个数据结构;

  • 命令及描述

blpop key1 [key2 ] timeout
移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。

brpop key1 [key2 ] timeout
移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。

brpoplpush source destination timeout
从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。

lindex key index
通过索引获取列表中的元素

linsert key before|after pivot value
在列表的元素前或者后插入元素

llen key
获取列表长度

lpop key
移出并获取列表的第一个元素

lpush key value1 [value2]
将一个或多个值插入到列表头部

lpushx key value
将一个值插入到已存在的列表头部

lrange key start stop
获取列表指定范围内的元素

lrem key count value
移除列表元素

lset key index value
通过索引设置列表元素的值

ltrim key start stop
对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。

rpop key
移除并获取列表最后一个元素

rpoplpush source destination
移除列表的最后一个元素,并将该元素添加到另一个列表并返回

rpush key value1 [value2]
在列表中添加一个或多个值

rpushx key value
为已存在的列表添加值

3.4 hash

一个string类型的field和value的映射表,hash特别适合用于存储对象,每个 hash 可以存储 232 - 1 键值对(40多亿)。

  • 常用命令

hget,hset,hgetall 等;

  • 应用场景

比如,我们存储供应商酒店价格的时候可以采取此结构,用酒店编码作为Key,RatePlan+RoomType作为Filed,价格信息作为Value;

  • 实现方式

Hash对应Value内部实际就是一个HashMap,实际这里会有2种不同实现,这个Hash的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,对应的value redisObject的encoding为zipmap;当成员数量增大时会自动转成真正的HashMap,此时encoding为ht;

  • 命令及描述

hdel key field1 [field2]
删除一个或多个哈希表字段

hexists key field
查看哈希表 key 中,指定的字段是否存在。

hget key field
获取存储在哈希表中指定字段的值。

hgetall key
获取在哈希表中指定 key 的所有字段和值

hincrby key field increment
为哈希表 key 中的指定字段的整数值加上增量 increment 。

hincrbyfloat key field increment
为哈希表 key 中的指定字段的浮点数值加上增量 increment 。

hkeys key
获取所有哈希表中的字段

hlen key
获取哈希表中字段的数量

hmget key field1 [field2]
获取所有给定字段的值

hmset key field1 value1 [field2 value2 ]
同时将多个 field-value (域-值)对设置到哈希表 key 中。

hset key field value
将哈希表 key 中的字段 field 的值设为 value 。

hsetnx key field value
只有在字段 field 不存在时,设置哈希表字段的值。

hvals key
获取哈希表中所有值

hscan key cursor [match pattern] [count count]
迭代哈希表中的键值对。

3.5 set

集合成员是唯一的,这就意味着集合中不能出现重复的数据。集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。唯一性,无序性,确定性

  • 常用命令

sadd、spop、smembers、sunion等;

  • 应用场景

Set对外提供的功能与list类似,当你需要存储一个列表数据,又不希望出现重复数据时,set 是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的接口,这个也是list所不能提供的;

  • 实现方式

Set的内部实现是一个value永远为null的HashMap,实际就是通过计算hash的方式来快速排除重复的,这也是set能提供判断一个成员是否在集合内的原因;

  • 命令及描述

sadd key member1 [member2]
向集合添加一个或多个成员

scard key
获取集合的成员数

sdiff key1 [key2]
返回给定所有集合的差集

sdiffstore destination key1 [key2]
返回给定所有集合的差集并存储在 destination 中

sinter key1 [key2]
返回给定所有集合的交集

sinterstore destination key1 [key2]
返回给定所有集合的交集并存储在 destination 中

sismember key member
判断 member 元素是否是集合 key 的成员

smembers key
返回集合中的所有成员

smove source destination member
将 member 元素从 source 集合移动到 destination 集合

spop key
移除并返回集合中的一个随机元素

srandmember key [count]
返回集合中一个或多个随机数

srem key member1 [member2]
移除集合中一个或多个成员

sunion key1 [key2]
返回所有给定集合的并集

sunionstore destination key1 [key2]
所有给定集合的并集存储在 destination 集合中

sscan key cursor [match pattern] [count count]
迭代集合中的元素

3.6 sorted set

不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但分数(score)却可以重复。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。

  • 常用命令

zadd,zrange,zrem,zcard等;

  • 应用场景

Sorted set的使用场景与set类似,区别是set不是自动有序的,而sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序.当你需要一个有序的并且不重复的集合列表,那么可以选择sorted set数据结构;

  • 实现方式

Sorted set的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率

  • 命令及描述

zadd key score1 member1
向有序集合添加一个或多个成员,或者更新已存在成员的分数

zcard key
获取有序集合的成员数

zcount key min max
计算在有序集合中指定区间分数的成员数

zincrby key increment member
有序集合中对指定成员的分数加上增量 increment

zinterstore destination numkeys key [key ...]
计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中

zlexcount key min max
在有序集合中计算指定字典区间内成员数量

zrange key start stop [withscores]
通过索引区间返回有序集合成指定区间内的成员

zrangebylex key min max [limit offset count]
通过字典区间返回有序集合的成员

zrangebyscore key min max [withscores] [limit]
通过分数返回有序集合指定区间内的成员

zrank key member
返回有序集合中指定成员的索引

zrem key member [member ...]
移除有序集合中的一个或多个成员

zremrangebylex key min max
移除有序集合中给定的字典区间的所有成员

zremrangebyrank key start stop
移除有序集合中给定的排名区间的所有成员

zremrangebyscore key min max
移除有序集合中给定的分数区间的所有成员

zrevrange key start stop [withscores]
返回有序集中指定区间内的成员,通过索引,分数从高到底

zrevrangebyscore key max min [withscores]
返回有序集中指定分数区间内的成员,分数从高到低排序

zrevrank key member
返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序

zscore key member
返回有序集中,成员的分数值

zunionstore destination numkeys key [key ...]
计算给定的一个或多个有序集的并集,并存储在新的 key 中

zscan key cursor [match pattern] [count count]
迭代有序集合中的元素(包括元素成员和元素分值)

3.7 hyperLogLog

HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。

比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。

3.8 geospatial

地理坐标信息,更多

4. 持久化配置

Redis的持久化有2种方式:

  • RDB:对内存中数据库状态进行快照
  • AOF:把每条写命令都写入文件,类似mysql的binlog日志

4.1 rdb

工作原理:rdb主进程满足触发条件调用rdbdump子进程

优势:从内存dump数据形成rdb二进制文件,恢复很快

缺陷:触发条件有时间间隔(会丢失几分钟)

  • 执行命令手动生成

有两个Redis命令可以用于生成RDB文件,一个是SAVE,另一个是BGSAVE,

SAVE命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在服务器进程阻塞期间,服务器不能处理任何命令请求;

BGSAVE命令会派生出一个子进程,然后由子进程负责创建RDB文件,服务器进程(父进程)继续处理命令请求,创建RDB文件结束之前,客户端发送的BGSAVE和SAVE命令会被服务器拒绝。

  • 通过配置自动生成

可以设置服务器配置的save选项,让服务器每隔一段时间自动执行一次BGSAVE命令;

可以通过save选项设置多个保存条件,但只要其中任意一个条件被满足,服务器就会执行BGSAVE命令。

#Rdb快照的配置选项(这3个选项都屏蔽,则rdb禁用)redis.conf
save 900 1      # 900秒内,有1条写入,则产生快照
save 300 1000   # 如果300秒内有1000次写入,则产生快照
save 60 10000   # 如果60秒内有10000次写入,则产生快照
stop-writes-on-bgsave-error yes  # 后台备份进程出错时,主进程停不停止写入
rdbcompression yes    # 导出的rdb文件是否压缩
Rdbchecksum   yes     # 导入rbd恢复时数据时,要不要检验rdb的完整性
dbfilename dump.rdb   # 导出来的rdb文件名
dir ./                # rdb的放置路径

4.2 aof

AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库状态的。文件刷新的方式有三种:

  • appendfsync always - 每提交一个修改命令都调用fsync刷新到AOF文件,非常非常慢,但也非常安全;
  • appendfsync everysec - 每秒钟都调用fsync刷新到AOF文件,很快,但可能会丢失一秒以内的数据;
  • appendfsync no - 依靠OS进行刷新,Redis不主动刷新AOF,这样最快,但安全性就差。

默认并推荐每秒刷新,这样在速度和安全上都做到了兼顾。

appendonly no # 是否打开 aof日志功能
appendfsync always   # 每1个命令,都立即同步到aof. 安全,速度慢
appendfsync everysec # 折衷方案,每秒写1次
appendfsync no      #写入工作交给操作系统,由操作系统判断缓冲区大小,统一写入到aof. 同步频率低,速度快,
no-appendfsync-on-rewrite  yes: # 正在导出rdb快照的过程中,要不要停止同步aof
auto-aof-rewrite-percentage 100 #aof文件大小比起上次重写时的大小,增长率100%时,重写
auto-aof-rewrite-min-size 64mb #aof文件,至少超过64M时,重写

#命令方式:
lastsave        #上次保存时间
bgrewriteaof 	#后台进程重写AOF

4.3 数据恢复

  • RDB方式

       RDB文件的载入工作是在服务器启动时自动执行的,没有专门用于载入RDB文件的命令,只要Redis服务器在启动时检测到RDB文件存在,它就会自动载入RDB文件,服务器在载入RDB文件期间,会一直处于阻塞状态,直到载入工作完成为止。

  • AOF方式

     服务器在启动时,通过载入和执行AOF文件中保存的命令来还原服务器关闭之前的数据库状态,具体过程:

   (1)载入AOF文件
   (2)创建模拟客户端
   (3)从AOF文件中读取一条命令
   (4)使用模拟客户端执行命令
   (5)循环读取并执行命令,直到全部完成

如果同时启用了RDB和AOF方式,AOF优先,启动时只加载AOF文件恢复数据。

5. 集群 

5.1 redis sentinel

稳定版本的redis(2.8)只支持简单的master-slave replication: 一个master写,多个slave读只能通过客户端一致性哈希自己做sharding(感觉用这个集群在生产环境用途不大)

它推出了一个集群管理工具,叫作哨兵(Sentinel)。Redis Sentinel是社区版本推出的原生高可用解决方案,Redis Sentinel部署架构主要包括两部分:

  • Redis Sentinel集群,实现了著名的RAFT选主协议,可以实现故障发现、故障自动转移、配置中心和客户端通知,且从而保证了系统选主的一致性。节点数量要满足2n+1(n>=1)的奇数个。
  • Redis数据集群

 

Sentinel功能

  1. monitoring——redis实例是否正常运行;
  2. notification——通知application错误信息;
  3. failover——某个master死掉,选择一个slave升级为master,修改其他slave的slaveof关系,更新client连接;
  4. configurationprovider——client通过sentinel获取redis地址,并在failover时更新地址;

sentinels and slaves autodiscovery

  • sentinels——sentinel之间通过redis pub/sub交换信息获得;
  • slaves——询问master获得;

sdown、odown、failover

故障检测一般都是通过ping-pong机制,sentinel引入sdown(主观下线)和odown(客观下线)机制,目的应该是在集群规模较大时,检测更客观。

  • sdwon——is-master-down-after-milliseconds(可配置)时间内ping-pong失败。sdown的slave不能升级为master。
  • odown——超过一定数目(可配置)的sentinel认为sdown,odown只针对master。
  • failover——多数sentinel认为odown。

sentinel集群

  • 至少需要部署三台以上,形成一个sentinel集群,
  • Sentinels之间的数据同步,包括redis状态(odown,sdown),通过redis pub/sub实现,
  • 执行failover需要选举一个sentinel作为leader去执行(添加sentinel:自动发现)(移除sentinel:stopsentinel process/reset all sentinel/check sentinel num)

执行failver

  • slave leader升级为master、
  • 其他slave修改为新master的slave、
  • 客户端修改连接、
  • 老的master如果重启成功,变为新master的slave、

参考配置

Master配置:
1. 关闭rdb快照(备份工作交给slave)
2. 可以开启aof
slave配置:
1. 声明slave-of
2. 配置密码[如果master有密码]
3. [某1个]slave打开 rdb快照功能
4. 配置是否只读[slave-read-only]

缺点:

  • 资源浪费,Redis数据节点中slave节点作为备份节点不提供服务
  • Redis Sentinel主要是针对Redis数据节点中的主节点的高可用切换,对Redis的数据节点做失败判定分为主观下线和客观下线两种,对于Redis的从节点有对节点做主观下线操作,并不执行故障转移
  • 不能解决读写分离问题,实现起来相对复杂

建议:

  • 同一业务可以选择一套Sentinel集群监控多组Redis数据节点的方案
  • sentinel monitor配置中的<quorum>建议设置成Sentinel节点的一半加1
  • 合理设置参数,防止误切,控制切换灵敏度控制

down-after-milliseconds 30000
failover-timeout 180000
maxclient
timeout

  • 部署的各个节点服务器时间尽量要同步,否则日志的时序性会混乱
  • 自行搞定配置中心(zookeeper),方便客户端对实例的链接访问
  • Redis建议使用pipeline和multi-keys操作,减少RTT次数,提高请求效率

5.2 redis cluster

Redis在3.0也引入了集群的概念,也叫无中心高可用架构,用于解决一些大数据量和高可用的问题。

设计目标

1高性能,可扩展性,可线性扩展到1000多个节点,节点可动态添加或删除
2高可用性,部分节点不可用时,集群仍可用。通过增加Slave做standby数据副本,能够实现故障自动failover,节点之间通过gossip协议交换状态信息,用投票机制完成Slave到Master的角色提升
3

一致性:高可用和一致性之间做的权衡,最终一致性,不支持强一致性集群,使用的是异步复制,在数据到主节点后,主节点返回成功,数据被异步地复制给从节点。部分场景存在写丢失的可能

(1)异步replication,写主成功给client,在replication之前主挂掉

(2)网络故障partition,failover时也可能发生写丢失

4可以接受的写安全(acceptabledegree of write safety)
5集群实现了所有处理单个数据库键的命令,没有实现针对多个数据库键的复杂计算。不支持多数据库,只使用默认的0号数据库

数据分布

数据按照slot存储分布在多个节点,节点间数据共享,可动态调整数据分布

  • 不使用一致性hash,使用hashslot(哈希槽),共有16384个slot。
  • hash_slot = crc16(key) mod 16384
  • 每个node负责slot的一个子集,增加或者删除节点修改slot分布
  • hash-slot灵活性不如一致性hash,但便于实现数据迁移

核心部分

moved重定向client向任意一个node发送请求,若对应slot不在本地,返回movedresponse,携带正确的地址,client继续向新地址发送请求,可能move多次,客户端最好缓存key-node映射表;
故障迁移

指的是在cluster运行期间添加或删除nodes,本质是把hash slot从一个节点移动到另一个节点(与故障迁移是不同的,这里处理的是集群的伸缩性)。

移动hash slot的步骤如下:

1. 停止new keys:向对应的两个nodes发送migrating\importing命令:

  • Migrating——key不在本地,ask重定向,
  • Importing——只接受ask请求,否者moved
  • 从而停止new key的导入

2. 移动old keys: 然后启动另一个程序redis-trib,逐步的移动old keys

askask与moved不同,ask重定向下一次请求, ask只在数据迁移阶段有用;
容错

使用master-slave模型,与sentinel类似,对节点失效的检测方法与sentinel有些不同;

1.节点失效检测

ping无返回标记为pfail(possible failure)、

随机广播3个已知节点信息、

失效报告,记录接收到的节点信息、

大部分节点认为pfail,标记为fail、

广播fail,所有的节点都标记其为fail

2.fail状态的清除

从节点重新上线时标记清除----所以对于下线的节点,也进行检测、

主节点fail,超限时间内未故障转移,并且主节点重新上线,清除标记

3.集群状态检测

部分哈希槽不能正常使用,整个集群停止工作-----为什么要做这个设计??

4.从节点的选举

满足一定条件(主已下线、主槽非空、本节点可靠)的从节点向集群中其他主节点发送授权请求、

其他主节点返回授权(id最小等)、

从节点得到授权,开始故障转移;

5.执行故障转移

ping其他节点,我是主了、

接管已下线主的哈希槽、

其他节点更新;

客户端因为节点可以转向所有的错误信息,因此客户端无须保存集群状态信息;

客户端保存键和节点的映射信息,可以有效的减少转向次数,由此提升效率;

cluster-slot命令获取hash-slot分布,来提高client效率;

需要client支持,目前支持的client比较少,jedis支持,finagle-redis不支持,redis-cli

缺点:

  1. 节点会因为某些原因发生阻塞(阻塞时间大于clutser-node-timeout),被判断下线,这种failover是没有必要的
  2. 数据通过异步复制,不保证数据的强一致性
  3. Slave在集群中充当“冷备”,不能缓解读压力,当然可以通过SDK的合理设计来提高Slave资源的利用率
  4. 多个业务使用同一套集群时,无法根据统计区分冷热数据,资源隔离性较差,容易出现相互影响的情况
  5. Client实现复杂,驱动要求实现Smart Client,缓存slots mapping信息并及时更新,提高了开发难度,客户端的不成熟影响业务的稳定性。目前仅JedisCluster相对成熟,异常处理部分还不完善,比如常见的“max redirect exception”。
  6. key批量操作限制,如使用mset、mget目前只支持具有相同slot值的key执行批量操作
  7. key事务操作支持有限,只支持多key在同一节点上的事务操作
  8. key作为数据分区的最小粒度,因此不能将一个很大的键值对象如hash、list等映射到不同的节点
  9. Redis Cluster不建议使用pipeline和multi-keys操作,减少max redirect产生的场景

6. 客户端 Jedis

使用Jedis连接Redis服务器有四种方式

Jedis/JedisPool连接

这种方式针对单个Redis服务器建立连接,Jedis是单个连接,JedisPool即Jedis连接池,为了优化连接性能而生

ShardedJedis/ShardedJedisPool连接

这种方式可以连接互不相通的一组Redis服务器.即Redis服务器因为数据量太大在数据上进行了水平拆分,但是服务器间并不通信,也没有副本备份.同样的道理,ShardedJedisPool是针对ShardedJedis单个连接所做的优化

采用一致性hash

JedisSentinelPool连接

这种方式可以连接Sentinel集群.感觉和单redis没啥不一致的

JedisCluster连接

使用这种方式时,默认Redis已经进行了集群处理,JedisCluster即针对整个集群的连接.

采用hash槽

7. 示例:位图法统计活跃用户

场景描述:1亿个用户,用户有频繁登陆的,也有不经常登陆的,如果记录用户的登陆信息?

如何查询用户的活跃度,1周内登陆三次的?

思路:

UserId      dt                        active

1                2015-3-21         1

1                2015-5-5-5        1

不合理,表数据增长快,量大,group,sum运算计算较慢

用:位图法 bit*map

log0021: 01100...0

......

log0723: 01101...0

log0726: 01100...1

1、记录用户登陆

每天按日期生成一个位图,用户登陆后把user_id位的bit值设置为1

2、把1周的位图 and计算

位上为1的即是连续登陆的用户

>setbit mon 0000000000

>setbit mon 3 1

>setbit mon 5 1

>setbit mon 7 1

>bitop and res mon tur wen

以上优点

1、节约空间,1亿人每天登陆情况,用1亿bit约1200wbyte,约10M的字节表示

2、位运算计算方便

8. Q&A

8.1 redis是个单线程的程序,为什么会这么快?

总体来说快速的原因如下:  

  1. 绝大部分请求是纯粹的内存操作(非常快速);
  2. 采用单线程,避免了不必要的上下文切换和竞争条件;
  3. 非阻塞IO;内部实现采用epoll,采用了epoll+自己实现的简单的事件框架。epoll中的读、写、关闭、连接都转化成了事件,然后利用epoll的多路复用特性,绝不在io上浪费一点时间。

这3个条件不是相互独立的,特别是第一条,如果请求都是耗时的(都会导致卡顿和其他命令的执行),采用单线程吞吐量及性能可想而知了。应该说redis为特殊的场景选择了合适的技术方案。

我怎么提高多核CPU的利用率?

为了最大限度的使用CPU,可以在同一个服务器部署多个Redis的实例,并把他们当作不同的服务器来使用,在某些时候,无论如何一个服务器是不够的,所以,如果你想使用多个CPU,你可以考虑一下分片(shard)

注:对于请求耗时的命令,可采用提供异步命令scanbgsavebgrewriteaof)。

8.2 如何1亿个key中找某些前缀开头的?

假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如果将它们全部找出来?

使用keys指令可以扫出指定模式的key列表。对方接着追问:如果这个redis正在给线上的业务提供服务,那使用keys指令会有什么问题?

这个时候你要回答redis关键的一个特性:redis的单线程的。keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时候可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用keys指令长。

8.3 做异步队列你是怎么用的?

一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。

如果对方追问可不可以不用sleep呢?list还有个指令叫blpop,在没有消息的时候,它会阻塞住直到消息到来。

如果对方追问能不能生产一次消费多次呢?使用pub/sub主题订阅者模式,可以实现1:N的消息队列。

如果对方追问pub/sub有什么缺点?在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。

如果对方追问redis如何实现延时队列?我估计现在你很想把面试官一棒打死如果你手上有一根棒球棍的话,怎么问的这么详细。但是你很克制,然后神态自若的回答道:使用sortedset,拿时间戳作为score,消息内容作为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒之前的数据轮询进行处理。

8.4 大量的key设置同一时间过期需要注意什么?

如果大量的key过期时间设置的过于集中,到过期的那个时间点,redis可能会出现短暂的卡顿现象。一般需要在时间上加一个随机值,使得过期时间分散一些。

8.5 如何做持久化的?

bgsave做快照全量持久化,aof做增量持久化。因为bgsave会耗费较长时间,不够实时,在停机的时候会导致大量丢失数据,所以需要aof来配合使用。在redis实例重启时,会使用bgsave持久化文件重新构建内存,再使用aof重放近期的操作指令来实现完整恢复重启之前的状态。

对方追问那如果突然机器掉电会怎样?取决于aof日志sync属性的配置,如果不要求性能,在每条写指令时都sync一下磁盘,就不会丢失数据。但是在高性能的要求下每次都sync是不现实的,一般都使用定时sync,比如1s1次,这个时候最多就会丢失1s的数据。

对方追问bgsave的原理是什么?你给出两个词汇就可以了,fork和cowfork是指redis通过创建子进程来进行bgsave操作,cow指的是copy on write,子进程创建后,父子进程共享数据段,父进程继续提供读写服务,写脏的页面数据会逐渐和子进程分离开来。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 想要快速入门Redis,可以按照以下步骤进行: 1. 了解Redis的基本概念和特性,例如Redis是一种内存数据存储系统,可以用来缓存数据和作为消息队列等。 2. 下载并安装Redis,可以从Redis官网上下载对应操作系统的Redis安装包进行安装。 3. 掌握Redis的命令行界面,可以使用Redis自带的命令行工具redis-cli来与Redis进行交互。 4. 学习Redis的数据结构,例如字符串、列表、哈希表等,了解每种数据结构的用途和常用操作。 5. 使用编程语言连接Redis并进行操作,Redis支持多种编程语言的客户端,例如Python、Java、C#等。 6. 实践练习,可以使用Redis进行一些常见的场景,例如缓存、排行榜、分布式锁等,来加深对Redis的理解和应用。 以上是快速入门Redis的基本步骤,建议在学习过程中多做练习和实践,以加深对Redis的理解和掌握。 ### 回答2: 要快速入门Redis,需要按照以下步骤进行: 1. 安装并配置Redis:首先在官方网站下载并安装Redis,然后进行基本的配置。在配置文件中,可以设置密码、端口号、数据存储路径等参数。 2. 了解Redis的基本概念:Redis是一种键值存储数据库,它将数据存储在内存中,以提供高速读写访问。了解Redis的基本概念,如键、值、数据类型、过期时间等,是入门的关键。 3. 学习Redis的数据类型:Redis支持多种数据类型,如字符串、哈希、列表、集合和有序集合。掌握每种数据类型的特性、用法和常见操作,可以更有效地使用Redis。 4. 掌握Redis的命令:Redis通过使用命令行工具或客户端库与其进行交互。了解并熟悉常用的Redis命令,如SET、GET、DEL、HSET、HGET、LPUSH、LPOP、SADD、SREM等,是日常使用的基础。 5. 实践Redis的用例:通过实际应用场景来加深对Redis的理解。可以尝试使用Redis来存储会话数据、缓存数据、排行榜数据等。在实践中,不断探索和学习Redis的各种用法。 6. 学习Redis的高级功能:Redis还提供了许多高级功能,如发布订阅、事务、Lua脚本、管道操作等。进一步学习这些高级功能,可以提升Redis的使用能力。 7. 阅读官方文档和参考资料:Redis官方文档和各种博客、教程等都提供了丰富的资料,可以帮助快速入门和深入理解Redis的各个方面。 总之,快速入门Redis需要学习其基本概念和数据类型,并掌握常用的命令和用例。通过实际实践和进一步学习高级功能,可以更好地应用Redis
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值