赞
踩
Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。从2010年3月15日起,Redis的开发工作由VMware主持。它在企业级应用中被广泛的使用,已经成为web开发者必须具备的技能之一。
目录
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。
优点:
性能(以下摘自官方测试描述):
在50个并发的情况下请求10W次,写的速度是11W次/s,读的速度是8.1w次/s。测试环境:
1)50个并发,请求10W次
2)读和写大小为256bytes的字符串
3)Linux2.6 Xeon X3320 2.5GHz的服务器上
4)通过本机的loopback interface接口上执行
缺点:
redis sentinel实现了著名的RAFT选主协议
- 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"
Redis中默认设置了16个数据库,编号为0~15,可以通过修改配置文件来修改数据库个数.可以使用select(databaseNo)方法来选择使用的数据库。
使用场景:
一台服务器上都快开启200个redis实例了,看着就崩溃了。这么做无非就是想让不同类型的数据属于不同的应用程序而彼此分开。那么,redis有没有什么方法使不同的应用程序数据彼此分开同时又存储在相同的实例上呢?就相当于mysql数据库,不同的应用程序数据存储在不同的数据库下。
Redis的Key中使用冒号作为分隔符,在RedisDesktopManager中查看可以看到分级的文件夹.需要注意的是,这种方式只有在客户端中查看才能看到分级效果,实际的Key并没有变化。
Redis是一种基于客户端-服务端模型以及请求/响应协议的TCP服务。
而管道(Pipelining):学习如何一次发送多个命令,节省往返时间。客户端向服务端发送一个查询请求,并监听Socket返回,通常是以阻塞模式,等待服务端响应。
发布端: publish 频道名称 发布内容
订阅端: subscribe 频道名称
Redis的发布/订阅目前是即发即弃(fire and forget)模式的,因此无法实现事件的可靠通知。也就是说,如果发布/订阅的客户端断链之后又重连,则在客户端断链期间的所有事件都丢失了。
数据库与作用域:
发布/订阅与key所在空间没有关系,它不会受任何级别的干扰,包括不同数据库编码。 发布在db 10,订阅可以在db 1。 如果你需要区分某些频道,可以通过在频道名称前面加上所在环境的名称(例如:测试环境,演示环境,线上环境等)。
模式匹配订阅:
Redis 的Pub/Sub实现支持模式匹配。客户端可以订阅全风格的模式以便接收所有来自能匹配到给定模式的频道的消息。比如:PSUBSCRIBE news.*
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监听 |
导入使用cat和redis-cli命令组合,使用--pipe 这个参数来启用pipe协议,它不仅仅能减少返回结果的输出,还能更快的执行指令。更多
ping | 测试连接是否存活 |
select 1 | 选择数据库。Redis数据库编号从0~15,我们可以选择任意一个数据库来进行数据的存取 |
quit | 退出连接 |
echo HongWan | 在命令行打印一些内容 |
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 | 返回当前服务器时间 |
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] 查询尾字符为e或t的字符 |
move key db | redis.conf默认开启了16个databases 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作用类似,但不会阻塞服务器 |
set、get、decr、incr、mget、setnx等
String是最常用的一种数据类型,普通的key/value存储
String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr,decr等操作时会转成数值型进行计算,此时redisObject的encoding字段为int.
set key value |
get key |
getrange key start end |
getset key value |
getbit key offset |
mget key1 [key2..] |
setbit key offset value |
setex key seconds value |
setnx key value |
setrange key offset value |
strlen key |
mset key value [key value ...] |
msetnx key value [key value ...] |
psetex key milliseconds value |
incr key |
incrby key increment |
incrbyfloat key increment |
decr key |
decrby key decrement |
append key value |
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 | 交集、并集、非集、异或的数量 |
简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。一个列表最多可以包含 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 |
rpop key |
rpoplpush source destination |
rpush key value1 [value2] |
rpushx key value |
一个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 |
hget key field |
hgetall key |
hincrby key field increment |
hincrbyfloat key field increment |
hkeys key |
hlen key |
hmget key field1 [field2] |
hmset key field1 value1 [field2 value2 ] |
hset key field value |
hsetnx key field value |
hvals key |
hscan key cursor [match pattern] [count count] |
集合成员是唯一的,这就意味着集合中不能出现重复的数据。集合中最大的成员数为 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] |
sinter key1 [key2] |
sinterstore destination key1 [key2] |
sismember key member |
smembers key |
smove source destination member |
spop key |
srandmember key [count] |
srem key member1 [member2] |
sunion key1 [key2] |
sunionstore destination key1 [key2] |
不同的是每个元素都会关联一个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,使用跳跃表的结构可以获得比较高的查找效率
HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。
比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。
地理坐标信息,更多
Redis的持久化有2种方式:
工作原理: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的放置路径
AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库状态的。文件刷新的方式有三种:
默认并推荐每秒刷新,这样在速度和安全上都做到了兼顾。
- 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
RDB文件的载入工作是在服务器启动时自动执行的,没有专门用于载入RDB文件的命令,只要Redis服务器在启动时检测到RDB文件存在,它就会自动载入RDB文件,服务器在载入RDB文件期间,会一直处于阻塞状态,直到载入工作完成为止。
服务器在启动时,通过载入和执行AOF文件中保存的命令来还原服务器关闭之前的数据库状态,具体过程:
(1)载入AOF文件
(2)创建模拟客户端
(3)从AOF文件中读取一条命令
(4)使用模拟客户端执行命令
(5)循环读取并执行命令,直到全部完成
如果同时启用了RDB和AOF方式,AOF优先,启动时只加载AOF文件恢复数据。
稳定版本的redis(2.8)只支持简单的master-slave replication: 一个master写,多个slave读。只能通过客户端一致性哈希自己做sharding(感觉用这个集群在生产环境用途不大)。
它推出了一个集群管理工具,叫作哨兵(Sentinel)。Redis Sentinel是社区版本推出的原生高可用解决方案,Redis Sentinel部署架构主要包括两部分:
Sentinel功能
sentinels and slaves autodiscovery
sdown、odown、failover
故障检测一般都是通过ping-pong机制,sentinel引入sdown(主观下线)和odown(客观下线)机制,目的应该是在集群规模较大时,检测更客观。
sentinel集群
执行failver
参考配置
Master配置:
1. 关闭rdb快照(备份工作交给slave)
2. 可以开启aof
slave配置:
1. 声明slave-of
2. 配置密码[如果master有密码]
3. [某1个]slave打开 rdb快照功能
4. 配置是否只读[slave-read-only]
缺点:
建议:
down-after-milliseconds 30000
failover-timeout 180000
maxclient
timeout
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存储分布在多个节点,节点间数据共享,可动态调整数据分布
核心部分
moved重定向 | client向任意一个node发送请求,若对应slot不在本地,返回movedresponse,携带正确的地址,client继续向新地址发送请求,可能move多次,客户端最好缓存key-node映射表; |
故障迁移 | 指的是在cluster运行期间添加或删除nodes,本质是把hash slot从一个节点移动到另一个节点(与故障迁移是不同的,这里处理的是集群的伸缩性)。 移动hash slot的步骤如下: 1. 停止new keys:向对应的两个nodes发送migrating\importing命令:
2. 移动old keys: 然后启动另一个程序redis-trib,逐步的移动old keys |
ask | ask与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 |
缺点:
使用Jedis连接Redis服务器有四种方式
Jedis/JedisPool连接 | 这种方式针对单个Redis服务器建立连接,Jedis是单个连接,JedisPool即Jedis连接池,为了优化连接性能而生 |
ShardedJedis/ShardedJedisPool连接 | 这种方式可以连接互不相通的一组Redis服务器.即Redis服务器因为数据量太大在数据上进行了水平拆分,但是服务器间并不通信,也没有副本备份.同样的道理,ShardedJedisPool是针对ShardedJedis单个连接所做的优化 采用一致性hash |
JedisSentinelPool连接 | 这种方式可以连接Sentinel集群.感觉和单redis没啥不一致的 |
JedisCluster连接 | 使用这种方式时,默认Redis已经进行了集群处理,JedisCluster即针对整个集群的连接. 采用hash槽 |
场景描述: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.1 redis是个单线程的程序,为什么会这么快?
总体来说快速的原因如下:
这3个条件不是相互独立的,特别是第一条,如果请求都是耗时的(都会导致卡顿和其他命令的执行),采用单线程吞吐量及性能可想而知了。应该说redis为特殊的场景选择了合适的技术方案。
我怎么提高多核CPU的利用率?
为了最大限度的使用CPU,可以在同一个服务器部署多个Redis的实例,并把他们当作不同的服务器来使用,在某些时候,无论如何一个服务器是不够的,所以,如果你想使用多个CPU,你可以考虑一下分片(shard)
注:对于请求耗时的命令,可采用提供异步命令(scan、bgsave、bgrewriteaof)。
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和cow。fork是指redis通过创建子进程来进行bgsave操作,cow指的是copy on write,子进程创建后,父子进程共享数据段,父进程继续提供读写服务,写脏的页面数据会逐渐和子进程分离开来。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。