Redis学习记录
Redis前置知识
数据库表有索引,且数据量很大,性能就会下降?
答:表添加索引后,增删改需要维护索引,速度就会变慢。对与查询数据,少量的、简单的查询依然很快,但面对并发、复杂的查询,磁盘受带宽的影响,速度也会下降。
Redis是单线程、单进程、单实例的key-value型数据库,value支持的类型有5种:
- String
- hash
- list
- set
- scorted set
String
string中可以存放字符型、数值型和位图
具体命令可以查看中文文档:http://redis.cn/commands.htm
重点说一下位图的使用实例:
首先什么是位图?
我的理解是:以操作二进制位上0,1的变化以记录特殊信息的数据类型。
1.统计用户登录天数
用一个二进制位代表一天,一年就是365-366个二进制位,以用户为key,value中存放位图,每登录一天,就将对应天数的进制位变为1,即:
lala在第265天登录
setbit lala 265 1
之后只需统计一年中位数为1的个数即可得到该用户的登录天数。
bitcount lala 0 -1
2.活跃用户统计
将日期当作key,二进制位映射到用户,每一位就代表一个用户,谁登录就将对应位变为1 ,之后只要把一段日期内的值做 bitop or 操作,即可得到活跃用户一段时期内的登录情况的位图,之后统计该位图的1即可得到这段时间的活跃用户人数。
list
redis的list是一个链表,key中存放了head头结点,和tail尾节点。
redis的list有从两端的push和pop方法,也提供了根据索引取的方法。
hash
hash是一个map型的key-value的数据结构,整体是:key - < field-value >,这样的。
set
redis的set特点也是无序、去重,但方法中有不少取交集、并集、差集的方法。
sorted set
与set不同,sorted set是有序的,默认是从小到大排序。而实现排序的关键是多一个score 分值属性,分值的存在就给sorted set的并集操作带来了问题,当几个key中存在不同score的同样value,并集时采用什么策略来计算分值。
ZUNIONSTORE 命令可以人为的解决这个问题。
跳跃表
sorted set的排序底层是通过跳跃表实现的,那什么是跳跃表?
我对其的理解是一个类金字塔的结构,越上层数据越少,底层还是双向链表,只是多出一个维度。
当一个新数据进入,先从顶层开始比较,顶层比完向下一层根据顶层比较结果向前还是向后比,直到最后一层找到对应位置,然后随机造层,改变对应层数的链表指向,完成新增。
这个结构将链表从头到尾的比较通过分层,在一段一段区间内去比,牺牲空间换取速度,对于大数据量的操作,平均速度较快。
更详细的可以查看这个文档
事务
MULTI 开启事务
EXEC 执行事务
当多个客户端同时请求事务,谁的EXEC先到先执行,这样当事务中命令冲突时会使一些无效命令请求到redis,这就需要监听。
WATCH 监听key
WATCH要在MULTI之前,当自身事务的EXEC到达之前,WATCH监听到对应的key发生了改变,就会取消整个事务。
缓存
redis可以作为缓存,也可以作为数据库使用,一般的我们常将其当作缓存来使用。
做为缓存,就不可能存放全量的数据,放的都是热数据。
内存满了,redis如何淘汰冷数据?
在redis的对应端口号 port.conf(eg:6379.conf)中,
可以配置最大内存量:maxmemory < bytes >
还可以配置内存满时的执行策略 maxmemory-policy
- noeviction 默认,直接返回错误
- allkeys - lru 全空间多长时间未使用
- volatile - lru 快过期的多长时间未使用
- allkeys - lfu 全空间使用次数低于多少
- volatile - lfu 快过期的使用次数低于多少
- allkeys - random 全空间随机
- volatile - random 快过期的随机
- volatile - ttl 回收过期的
key的过期时间问题
上面提到过期的key,那访问key是否会重置过期时间呢?
答案是不能,访问key与过期时间无关,但写key即set key时,如果没有附带过期时间的命令,就会剔除过期时间,使一个原来有过期时间的key变为无过期时间的key,需要注意。
redis对过期key的处理策略
被动:当访问的是过期key时,reids才会清除,并返回nil
主动:
- 随机20个key检查过期时间
- 删除过期key
- 如果大于25%的key过期,重复1
持久化
RDB 快照
前置知识 Linux的父子进程
在linux中父子进程的数据是相互隔离的,父进程可以让子进程看到数据,但父子进程的修改都不会影响对方,且对方看不到。
这是通过 fork 实现的,其特点就是速度快、内存小。创建子进程时,并不会直接把数据复制给子进程,而是将当前状态的地址交给子进程,当子进程需要数据时,才会复制数据,即copy on write 写时复制。
redis的RDB就是运用 fork 创建子进程,让子进程去持久化,父进程保持正常的通讯请求。这样就可以保证快照是时点的数据,且不会干扰正常的请求访问。
redis的RDB分为save和dbsave,dbsave就是上面说的fork形式。
save则是正常理解的全量复制,一般只有在明确要关机维护时才会用到。
RDB的弊端
- 不支持拉链,即只会保存一个 dump.rdb文件,无法根据时间保持多个副本。
- 丢失数据相对多一些,在两个RDB时间内,发生的操作越多,RDB丢失的也越多。
为解决RDB的弊端,redis还有
AOF
AOF会将redis的写操作记录到文件中,这样就详细记录了每一条数据,但无疑加大了保持成本,为应对这个问题redis会将AOF中的命令进行重写。
4.0以前的AOF:抵消冲突命令,合并相似命令,最终还是一个纯指令的日志文件。
4.0以后:先将老的数据RDB存入AOF中,再将之后的命令以指令的方式append到AOF文件中。
同时开启RDB和AOF时,reids只会使用AOF恢复,不过在4.0以后AOF中和了RDB。
.aof指令
*:表示有几行操作
$:表示有几个字符
集群(以下就都是我半懂不懂的了,之后会根据学习慢慢完善。)
AKF
CAP原则
一致性、可用性、分区容错性,只能同时实现两点,不可能三者兼顾。
主从复制
从服务器链接主服务器时,会先把自身的所有数据删除再与主同步,设置为从服务器后默认是禁止存入的,可以在配置文件中修改。
配置文件
replica-serve-stale-data yes 复制主机时,老数据是否继续支持
replica-read-only yes 从机是否只读
repl-diskless-sync no 是否通过磁盘来传输RDB数据 yes走网络
repl-backlog-size 1mb 增量复制队列大小
min-replicas-to-write 3 健康的slave的个数小于N,mater就禁止写入
min-replicas-max-lag 10 延迟小于min-replicas-max-lag秒的slave才认为是健康的slave
哨兵
分布式策略
hash取模(modula)、随机(random)、哈希环(kemala)
哈希环
通过hash,将节点映射为位点,之后数据经过hash计算后,离哪个位点近就存入那个节点。这样可以合理分配节点压力,且再新增节点时不会造成全局洗牌。但新增节点后,不可避免会造成一部分数据不能命中,因为新增的节点吸收了附件的数据hash,原来附近的数据是存在其他节点中的,此时查找这些数据,就都落在了新节点里。
数据倾斜
如果恰好,hash计算后都落在了一个节点附近,发生了数据倾斜,可以将在hash计算时加10个数字,产生一些虚拟节点的位点,减少数据倾斜的情况。
击穿
key的过期造成并发访问数据库。
具体讲就是并发请求一个过期的key,redis中没有,造成大数据量访问数据库。
解决:
1.get key,发现没有key
2.setnx key,nx 只有空时才能成功,这样就相当于一个锁,只有一个可以成功set key
3.成功set的去数据库拿数据并存入redis
4.未成功的休眠一段时间,继续执行1
为了防止抢到setnx的挂了,就要为key设置过期时间,但访问数据库超时又会带来新的问题。就需要多线程执行去数据库,一个线程去db,一个监控是否取回来,实时更新锁过期时间。
穿透
访问数据库中不存在的数据。
解决:布隆过滤器
布隆过滤器
数据经过哈希计算,映射到一个位图的一个位点上,当访问到达,经过hash计算,去位图上查,不是1就是没有的数据,直接返回nil,不去db中查找。可以通过增加hash次数来减少巧合,但不可能百分百阻挡无效请求。
雪崩
大量key同时过期,正好并发请求这些key
解决:当业务中没有固定过期时间时,可以设置随机过期时间解决。
当业务中明确要求在固定时间过期时,可以参考击穿方案,也可以在业务层使其在对应时间随机延迟,让出后台操作时间。