简介
为什么这么快?
- 基于内存,不受硬盘io
- 数据结构,操作简单
- 单线程(主线程(io时间处理、集群协调等)是单线程,所有读写请求由一个线程处理)。
可以处理并发吗? - 多路io复用(根据不同系统选择不同多路复用函数,优先选择O(1),select保底)
memchace区别
- memcache
- 支持简单数据类型
- 不支持持久化
- 不支持分片
- 不支持主从
- redis
- 类型丰富
- 支持持久化
- 支持主从
- 支持分片
类型&命令
String
set strkey “这是一个字符串”
get strkey
命令 | 含义 | 使用场景 |
---|---|---|
set key value | 设定指定的值 | |
get key | 获取指定key的值 | |
getrange key start end | 返回 key 中字符串值的子字符 | |
getset key value | 将给定 key 的值设为 value ,并返回 key 的旧值(old value) | |
mget key1 key2 … | 获取所有(一个或多个)给定 key 的值 | |
strlen key | 返回 key 所储存的字符串值的长度 | |
mset key value key value | 同时设置一个或多个 key-value 对 | |
incr key | 对应key的值加1 | 记录每天用户访问多少次,用户id+时间作为键,每访问一次incr就好 |
一个键最大能存储512MB
hash
hmset hashKey feild1 “hello” feild2 “world”
hget hashKey feild1
每个 hash 可以存储 2^32 -1 键值对(40多亿)
命令 | 含义 |
---|---|
hdel key field1 field2 … | 删除一个或多个哈希表字段 |
hexists key field | 查看哈希表key中,指定的字段是否存在 |
hget key field | 获取存储在哈希表中指定字段的值 |
hgetall key | 获取在哈希表中制定key的所有字段和值 |
hkeys key | 获取所有哈希表的字段 |
hlen key | 获取哈希表中字段的数量 |
hmget key field1 field2 … | 获取所有给定字段的值 |
hmset key field1 value1 field2 value2 | 同时将多个field-value设置到哈希表key中 |
hset key field value | 将哈希表key中的字段field的值设置为value |
hvals key | 获取哈希表中所有值 |
List
字符串列表,插入顺序,可以在列表头部或者尾部插入一个元素
lpush listKey “one”
lpush listKey “two”
lrange listKey 0 10
列表最多可存储 2^32 - 1 元素 (4294967295, 每个列表可存储40多亿)。
命令 | 含义 |
---|---|
blpop key1 key2 timeout | 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止 |
brpop key1 key2 timeout | 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止 |
brpoppush sou des 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 | 通过索引设置列表元素的值 |
rpop key | 移除列表的最后一个元素,返回值为移除的元素 |
rpoplpush sou des | 移除列表的最后一个元素,并将该元素添加到另外一个列表返回 |
rpush key value1 value2 。。。 | 在列表中添加一个或多个值 |
rpushx key value | 为已存在的列表添加值 |
set
无序集合,元素唯一
集合中最大的成员数为 232 - 1(4294967295, 每个集合可存储40多亿个成员)。
sadd setKey “one”
sadd setKey “two”
smembers setKey
命令 | 含义 | |
---|---|---|
sadd key member1 member2 。。。 | 向集合中添加一个或多个成员 | |
scard key | 获取集合成员数 | |
sdiff key1 key2 。。。 | 返回给定所有集合的差集 | 微博存储用户的关注或者粉丝,方便计算共同关注等 |
sdiffstore des key1 key2 。。。 | 返回给定所有集合的差集并存储再des中 | |
sinter key1 key2 。。。 | 返回所有给定集合的交集 | |
sinterstore des key1 key2 。。 | 返回所有给定集合的交集并存储在des中 | |
sismemner key member | 判断member元素是否存在集合key的成员 | |
smembers key | 返回集合中所有成员 | |
smove source des member | 将member元素从sou移到des集合 | |
spop key | 移除并返回集合中的一个随机元素 | |
srem key member1 member2 。。。 | 移除集合中的一个或多个成员 | |
sunion key1 key2 。。。 | 返回所有给定集合的并集 | |
sunionstore des key1 key2 。。。 | 所有给定集合的并集并存储在des集合中 |
zset
有序集合,元素唯一
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
zadd zsetKey 0 “one”
zadd zsetKey 0 “two”
zrangebyscore zsetKey 0 100
命令 | 含义 |
---|---|
zadd key score1 member1 score2 member2 。。。 | 向有序集合中添加一个或多个成员,或者更新已存在成员的分数 |
zcard key | 获取集合成员数 |
zcount key min max | 计算指定区间分数的成员数 |
zrank key member | 返回有序集合中指定成员的索引 |
zrem key member。。。 | 移除有序集合中一个或多个成员 |
zscore key member | 返回有序集中成员的分数 |
键命令
命令 | 含义 | |
---|---|---|
del key | 删除键 | |
dump key | 序列化key | |
exists key | 判断key是否存在 | |
Keys pattern | 查找所有符合条件的key | 一次性返回所有满足的key,可能导致阻塞服务器 |
TYPE key | 返回key所存储的值的类型 | |
scan surdor match pattern count xx | 以0开始迭代,每次输入上次返回的游标值,最后返回0结束,每次返回的数量不确定,大概率符合count参数 | 127.0.0.1:6379> scan 0 match my* count 1 1) “10” 2) 1) “myset” 127.0.0.1:6379> scan 0 match my* count 1 |
- 海量数据找出固定前缀的key?
HyperLogLog
做基数统计的算法,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。
命令 | 含义 |
---|---|
pfadd key element1 element2.。。 | 添加指定元素到HyperLogLog中 |
pfcount key1 key2.。。 | 返回给定HyperLogLog的基数估算值 |
pfmerge deskey sourcekey1 sourcekey2.。。 | 多个合并成一个 |
Geo
存储地理位置信息
发布订阅
redis发布订阅是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。redis客户端可以订阅任意数量的频道。
命令 | 含义 |
---|---|
psubscribe pattern pattern。。。 | 订阅一个或多个符合给定模式的频道 |
pubsub subcommand argument | 查看订阅与发布系统状态 |
publish channel message | 将信息发送到指定的频道 |
punsubscribe pattern pattern。。。 | 退订所有给定模式的频道 |
subscribe channel channel。。。 | 订阅给定的一个或多个频道的信息 |
unsubscribe channel channel… | 指退订给定的频道 |
事务
一次执行多个命令,并且:
- 批量操作在发送exec命令前被放入队列缓存
- 收到exec命令后进入事务执行,事物中任意命令执行失败,其他命令依然被执行
- 在事物执行过程中,其他客户端提交的命令请求不会插入到事物执行命令序列中
命令 | 含义 |
---|---|
discard | 取消事务,放弃执行事务块所有命令 |
exec | 执行所有事务块内的命令 |
multi | 标记一个事务块的开始 |
unwatch | 取消watch命令对所有key的监视 |
watch key key。。。 | 监视一个或多个key,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。 |
分布式锁
访问共享资源时,防止彼此干扰
需要解决问题:
- 互斥性
- 安全性
- 死锁
- 容错
setnx key value 如果key不存在,设置成功,如果key已存在,不成功
如何解决长期有效的问题?
expire key timeout
原子性得不到满足
例子:
long status = setnx.(key,“1”);
//执行完挂了,key一直被占用
if(status == 1){
expire.(key,10);
doWork();
}
优化:
set key value [ex sencods] [px milisencods] [nx|xx]
nx:只在键不存在时才对键进行设置操作
xx:只在键存在时才对键进行设置操作
集中处理大量过期key暂时卡顿现象,在设置key的过期时间时加个随机值。
异步队列
使用list做为队列,rpush生产消息,lpop消费消息
缺点:没有等待队列里有值就直接消费
优化:可以在应用层使用sleep机制去调用lpop重试
blpop 阻塞直到有值再消费
缺点:只有一个消费者
优化:pub/sub
缺点:无状态,无法保证到达
持久化
-
RDB(快照)持久化:保存某个时间点的全量数据快照
- save:阻塞redis的服务进程,直到rdb文件创建完毕
- bgsave:子进程创建rdb文件,不阻塞服务进程
- lastsave:上次保存时间
-
自动化触发RDB持久化的方式
- redis.conf里配置save m n 定时触发(使用的时bgsave)
- 主从复制时,主节点主动触发
- 制定debug reload
- 执行shutdown且没有开启AOF持久化
-
BGCAVE原理
- 检查是否子进程在AOF/RDB正在执行
- 不存在触发持久化
- fork()
-
缺点
- 内存数据的全量同步,数据量大时由于I/O严重影响性能
- 可能由于redis挂掉丢失当前到最近一次的快照时间内的数据
-
AOF持久化:保存写
-
记录除查询以外的变更数据库状态的命令
-
以append的方式追加到AOF文件中(增量)appendonly.aof
-
写操作增加,日志文件越来越大?解决:日志重写
- fork()
- 子进程把新的AOF写到一个文件中,不依赖原来的AOF
- 主进程持续把新的变动写到内存和原来的AOF中
- 主进程获取子进程完成重写AOF信号,往新AOF同步增量变动
- 使用新的AOF文件替换掉旧的
优点 | 缺点 | |
---|---|---|
RDB | 全量数据快照,文件小,恢复快 | 无法保存最近一次快照之后的数据 |
AOF | 可读性高,适合增量保存,不容易丢失数据 | 文件体积大,恢复时间长 |
- RDB-AOF混合持久化方式
Pipeline及主从
- pipeline:批量执行命令,节省I/O时间
- 主从同步机制
- 全同步
- slave发送sync命令到master
- master启动一个后台进程,将redis的数据快照保存到文件中
- master将同步期间接受到的写命令缓存起来
- master完成写文件后将文件发送给slave
- 使用新的rdb文件替换久的rdb文件
- master将增量写命令发给slave
- 增量同步
- master接收到用户指令后,判断会否需有传播到slave
- 将操作记录追加导AOF文件
- 将操作传播到其他slave,1、对其主库;2、往响应缓存写入
- 将缓存中数据发给slave
- 全同步
- master挂掉不能写操作,解决:redis sentinel
- 监控:检查主从是否正常
- 提醒:通过API向管理或其他应用发送故障通知
- 自动故障迁移:主从切换(流言协议Gossip)
redis集群
如何海量数据快速找到所需?
- 一致性哈希算法:对2^32取模,将哈希值空间组成虚拟圆环。
- 将key按相同的hash函数计算哈希值,顺时针找到最近服务器
哈希环数据倾斜问题? - 虚拟节点
Redis Cluster
Redis Cluster是Redis官方提供的Redis集群功能。
为什么要实现?
1、主从复制不能实现高可用
2、随着用户数量增多,并发越来越多,业务需要更高的QPS,主从复制的单机QPS可能无法满足业务需求
3、服务器内存不能业务需要时,单纯的向服务器添加内存不能达到要求,此时需要充分考虑分布式需求,把数据分布到不同机器上
4、网络流量需求:业务流量超过服务器的网卡限制,可以使用分布式来进行分流
5、离线计算,需要中间环节缓冲等别的需求
虚拟槽分区
redis cluster采用虚拟槽分区,把所有的物理结点映射到[0-16383]slot上,cluster负责维护node<->slot<->value。当存放key-value时,根据哈希函数(CRC16[key]&16384)映射到0-16383槽(slot)内,共16384个槽位,每个节点维护部分槽及槽所映射的键值数据。
例如:
节点A覆盖0-5460
节点B覆盖5461-10922
节点C覆盖10923-16383
获取数据,存入一个值,按照(CRC16[key]&16384)= 6782,那么会存在结点B。当获取key是也会计算去结点B取。
新增一个主结点,从各个结点的前面各取一部分slot到D上,同样删除也是。
为了保证高可用,加了主从模式,一个主结点对应一个或多个从结点,主结点提供数据存取,从结点从主结点拉去备份,当主结点挂掉后,会选一个从结点变成主结点,保证集群不会挂掉。