一、简介
redis就是一个数据库,不过是在内存中的,存取速度快,被广泛用于缓存,分布式锁,支持事务,集群。
高性能,高并发。
1.1 为什么用redis
缓存分为本地缓存和分布式缓存,以java为例,使用自带的map或者guava实现的是本地缓存,最主要特点是轻量和快速,声明周期随着jvm的销毁而结束。
redis或memcached被称为分布式缓存,缓存具有一致性,缺点是要保持redis或memcached服务的高可用,程序架构上比较复杂。
1.2 redis与memcached区别
- redis支持更丰富的数据类型,k/v,list,set,zset,hash。memcache支持简单的数据类型,String.
- Redis支持数据的持久化,将内存中的数据保存再磁盘中,重启的时候可以再次加载使用,Memcached把数据全部存在内存中
- 集群模式,redis目前是原生支持cluster模式。
- Memcached是多线程,非阻塞IO复用的网络模型,Redis使用单线程的多路IO复用模型
1.3 redis设置过期时间
数据过期后,如何删除数据
定期删除+惰性删除
默认每隔100ms随机抽取一些设置了过期时间的key,检查是否过期,过期则删除
当系统查到key到期了,删除
1.4 redis 内存淘汰机制
大量key堆积在内存里导致内存块耗尽
- volatile-lru: 从已设置过期时间的数据集中挑最近最少使用的数据淘汰
- volatile-ttl: 从已设置过期时间的数据集中挑选将要过期的数据淘汰
- volatile-random: 从已设置过期数据的数据集中任意选择数据淘汰
- allkeys-lru: 当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(最常使用)
- allkeys-random: 从数据集中任意选择数据淘汰
- no-eviction: 禁止驱逐数据,当内存不足禁止写入数据
1.5 redis 持久化机制
快照(snapshotting)持久化(RDB)
Redis可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本。Redis创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本(Redis主从结构,主要用来提高Redis性能),还可以将快照留在原地以便重启服务器的时候使用
快照持久化是Redis默认采用的持久化方式
追加文件(append-only file)
开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是appendonly.aof。
在Redis的配置文件中存在三种不同的 AOF 持久化方式,它们分别是:
appendfsync always #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度
appendfsync everysec #每秒钟同步一次,显示地将多个写命令同步到硬盘
appendfsync no #让操作系统决定何时进行同步
1.6 缓存雪崩和缓存穿透问题解决方案
缓存雪崩
缓存同一时间大面积失效,后面的请求落在数据库上,造成数据库短时间内承受大量请求而崩掉
- 事前:尽量保证整个redis集群的高可用性,发现机器宕机尽快补上,选择合适的内存淘汰策略
- 事中: 本地缓存,限流+降级,避免MySQL崩掉
- 事后: 利用redis持久化机制保存的数据尽快恢复
缓存穿透
一般是黑客故意去请求缓存中不存在的数据, 导致所有请求都落在数据库上
- 一般是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个拦截,从而避免对底层存储系统的查询压力
- 简单粗暴,如果一个查询返回的数据为空,我们仍然将这个空结果缓存,过期时间很短,不超过五分钟。
1.7 如何解决redis的并发竞争Key问题
多个系统同时对一个key进行操作,但是执行顺序和我们期望的不同
分布式锁(zookeeper和redis度可以实现分布式锁)
分布式锁
1.8 如何保证缓存与数据库双写时的数据一致性
一般来说,就是如果你的系统不是严格要求缓存+数据库必须一致性的话,缓存可以稍微的跟数据库偶尔有不一致的情况,最好不要做这个方案,读请求和写请求串行化,串到一个内存队列里去,这样就可以保证一定不会出现不一致的情况
串行化之后,就会导致系统的吞吐量会大幅度的降低,用比正常情况下多几倍的机器去支撑线上的一个请求。
二、数据结构
String
set,get,decr,incr
简单的key-value类型
set key value
get key
del key
mset key1 value1 key2 value2
mget key1 key2
expire key1 5 # 5秒后过期
setex key2 5 value2
setnx name value # 不存在就创建
set age 30
incr age
incrby age 5
incrby age -5
Hash
hget,hset
hset maps java "think in java" # valu如果包含空格,需要引号
hgetall maps # 获取key,value
hlen maps
hget maps java
hmset maps java "v1" python "v2"
类似于java的hashMap
List
lpush,rpush,lpop,rpop,lrange
链表,应用场景很多,双向链表,反向查找,遍历
rpush books python java golang # 3个元素
llen books
lpop books
rpop books
rpush books value
ltrim books startindex endindex # -1表示倒数第一个
Set
sadd,spop,smembers
sadd books python
smembers books # 无序
sismember books java# 查看是否存在
scard books # 获取长度
spop books # 弹出一个
类似于列表的功能,特殊之处在于set是可以自动排重的
Sorted Set
zadd,zrange,zrem,zcard
zadd books 9.0 "a"
zadd books 8.0 "b"
zrange books 0 -1 # 按score排序从小到大输出
zcard books # 长度
zrangebyscore books 0 8.0 # 根据区间输出 后面可以加 withscores
比set增加了权重参数score,使得集合中的元素能够按score进行有序排列
三
3.1 位图
redis提供了位图数据结构,可以自动拓展
可以支持零整存取操作
setbit w 1 1
getbit w 1 1
set w h
get w
bitcount 统计指定位置范围1的个数 [start, end]
bitpos 用来查找指定范围内出现的第一个0或1
3.2 HyperLogLog统计UV
Redis提供的用于不精确的去重技术方案,标准误差0.81%。一个log需要占据12k的存储空间,所以不适合统计单个用户的相关数据。
原理:给定一系列的随机整数,我们记录下低位连续零位的最大长度 k,通 过这个 k 值可以估算出随机数的数量
pfadd 增加计数
pfcount 获取计数
3.3 布隆过滤器-推荐去重
布隆过滤器能准确过滤掉那些已经看过的内容,那些没有看过 的新内容,它也会过滤掉极小一部分 (误判),但是绝大多数新内容它都能准确识别。这样就 可以完全保证推荐给用户的内容都是无重复的。原理就是使用位图和几个无偏哈希函数,本质上就是取哈希,如果哈希冲突就认为重合,所以存在误判。
bf.add codehole user1
bf.exists codehole user2
3.4 限流-断尾求生
1 简单实现
zset加上时间窗口
2 漏斗限流
redis-cell模块
cl.throttle laoqian:reply 15 30 60 1
laoqian:reply : key
初始容量为15 频率行为60s最多30次,1是可选参数默认值为1
3.4 进水楼台-GeoHash
GeoHash算法,将二维经纬度映射到一维。
Geo内部就是一个普通的zset结构
geoadd company 116.43 39.99 juejin
geodist company a b km
geopos company juejin
geohash company juejin # 获取哈希编码
georadiusbymember company juejin 20 km count 3 asc #获取最近公司
georadius company 116.5345 39.434 20 km withdist count 3 asc 根据经纬度
3.5 大海捞针-sacn
- keys算法可以支持正则匹配所有key
keys codehole*
sacn 0 match key99* count 10
// count表示单次遍历的槽数- //0表示游标,服务器不记住寻找的状态