在学习Redis之前,首先了解关系型数据库和非关系型数据库
关系型数据库,是指采用了关系模型来组织数据的数据库,其以行和列的形式存储数据,以便于用户理解,关系型数据库这一系列的行和列被称为表,一组表组成了数据库。用户通过查询来检索数据库中的数据,而查询是一个用于限定数据库中某些区域的执行代码。关系模型可以简单理解为二维表格模型,而一个关系型数据库就是由二维表及其之间的关系组成的一个数据组织。主流的关系型数据库有Oracle、DB2、MySQL、[Microsoft SQL Server](https://baike.baidu.com/item/Microsoft SQL Server/2947866)、[Microsoft Access](https://baike.baidu.com/item/Microsoft Access/2862464)等。
非关系型数据库,NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,是一项全新的数据库革命性运动,早期就有人提出,发展至2009年趋势越发高涨。NoSQL的拥护者们提倡运用非关系型的数据存储,相对于铺天盖地的关系型数据库运用,这一概念无疑是一种全新的思维的注入。主流的包括redis,MongoDB等
那么这两种数据库的区别是什么:
- 适用场景不同:sql数据库适合用于关系特别复杂的数据查询场景,nosql反之
- 事务 特性的支持:sql对事务的支持非常完善,而nosql基本不支持事务
- 两者在不断地取长补短,呈现融合趋势
redis特点:
- Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
- Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
- Redis支持数据的备份,即master-slave模式的数据备份
redis的优势
- 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
- 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
- 原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
- 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性
核心配置选项
-
绑定ip:如果需要远程访问,可将此⾏注释,或绑定⼀个真实ip
bind 127.0.0.1
-
端⼝,默认为6379
port 6379
-
是否以守护进程运⾏
- 如果以守护进程运⾏,则不会在命令⾏阻塞,类似于服务
- 如果以⾮守护进程运⾏,则当前终端被阻塞
- 设置为yes表示守护进程,设置为no表示⾮守护进程
- 推荐设置为yes
daemonize yes
-
数据⽂件
dbfilename dump.rdb
-
数据⽂件存储路径
dir /var/lib/redis
-
⽇志⽂件
logfile “/var/log/redis/redis-server.log”
-
数据库,默认有16个
database 16
-
主从复制,类似于双机备份。
slaveof
数据类型
- string
- 设置键值
set key value
- s设置键值及过期时间
setex key seconds value
以秒为单位 - 设置多个键值
set key1 value1 key2 value2 ...
- 根据键获取值
get key
- 根据多个键获取值
mget key1 key2 ...
- 查看所有键
keys *
- 删除键和对应的值
del key1 key2 ...
- 设置过期时间
expire key seconds
以秒为单位 - 查看有效时间
ttl key
- 设置键值
- hash
- 设置单个属性
hset key field value
- 设置多个属性
hmset key field1 value1 field2 value2 ...
- 获取键所有的属性
hkeys key
- 获取一个属性的值
hget key field
- 获取多个属性的值
hmget key field1 field2 ...
- 获取所有属性的值
hvals key
- 删除属性,属性对应的值会一起被删除
hdel key field1 field2 ...
- 设置单个属性
- list
- 在左侧插入数据
lpush key value1 value2 ...
- 在右侧插入数据
rpush key value1 value2 ...
- 在指定元素的前后插入数据
linsert key before或after 现有元素 新元素
- 获取列表内指定范围的数据
lrange key start stop
- 修改指定索引位置的元素值
lset key index value
- 将列表中前
count
次出现的值为value
的元素移除
lrem key count value
- count > 0: 从头往尾移除
- count < 0: 从尾往头移除
- count = 0: 移除所有
- 在左侧插入数据
- set
- 增加元素
sadd key member1 member2 ...
- 获取所有元素
smembers key
- 删除指定元素
srem key value
- 增加元素
- zset
- 增加元素
zadd key score1 member1 score2 member2 ...
- 获取指定范围的元素
zrange key start stop
- 返回
score
值在min
和max
之间的成员
zrangebyscore key min max
- 返回成员member的score值
zscore key member
- 删除指定元素
zrem key member1 member2 ...
- 删除权值在指定范围的元素
zremrangebyscore key min max
- 增加元素
redis主从
- ⼀个master可以拥有多个slave,⼀个slave⼜可以拥有多个slave,如此下去,形成了强⼤的多级服务器集群架构
- master用来写数据,slave用来读数据,经统计:网站的读写比率是10:1
- 通过主从配置可以实现读写分离
- master和slave都是一个redis实例(redis服务)
redis事务
-
Redis提供了一定的事务支持,可以保证一组操作原子执行不被打断,但是如果执行中出现错误,事务不能回滚,Redis未提供回滚支持。
-
操作命令
multi
开启事务exec
执行事务
-
watch监视
- 若在构建的redis事务在执行时依赖某些值,可以使用watch对数据值进行监视。
-
注意:Redis Cluster 集群不支持事务
-
redis cluster 不支持多键操作,如mset
-
持久化存储
-
RDB快照持久化
redis默认开启了快照持久化机制,其时机如下:
-
定期触发
- 配置文件
save 900 1 save 300 10 save 60 10000
-
BGSAVE
执行
BGSAVE
命令,手动触发RDB持久化- SHUTDOWN
关闭redis时触发
-
-
AOF 追加文件持久化
- redis可以将执行的所有指令追加记录到文件中持久化存储,这是redis的另一种持久化机制。
- redis默认未开启,可使用一下命令开启
appendonly yes # 是否开启AOF appendfilename "appendonly.aof" # AOF文件
-
AOF记录操作的时机
# appendfsync always # 每个操作都写到磁盘中 appendfsync everysec # 每秒写一次磁盘,默认 # appendfsync no # 由操作系统决定写入磁盘的时机
-
使用AOF机制的缺点是随着时间的流逝,AOF文件会变得很大。但redis可以压缩AOF文件。
-
redis高可用
为了保证redis最大程度上能够使用,redis提供了主从同步+Sentinel哨兵机制。redis提供的哨兵是用来看护redis实例进程的,可以自动进行故障转移
- 监控。Sentinel会不断检查主实例和从实例是否按预期工作。
- 通知。Sentinel可以通过API通知系统管理员(另一个计算机程序),监视的那一个Redis实例出了问题。
- 自动故障转移。如果主服务器没有按照预期工作,Sentinel可以启动故障转移进程,其中从服务器升级为主服务器,其他附加的从服务器将重新配置为使用新主服务器,使用Redis服务器的应用程序将在连接时得到要使用的新地址的通知。
- 配置提供者。Sentinel充当客户端服务发现的授权源:客户端连接到Sentinel,以询问当前负责给定服务的Redis master的地址。如果发生故障转移,哨兵将报告新地址
redis缓存
-
数据的保存方式
-
序列化字符串
-
setex('user:1:info', expiry, json.dumps(user_dict))
-
节省空间
-
序列化有时间开销
-
更新不方便(一般直接删除)
-
-
redis的其他数据类型(hash,set,zset)
-
hmset('user:1:info', user_dict)
-
读写时不需要序列化转换
-
可以更新内部数据
-
相比字符串,采用复合结构存储空间占用大
-
-
缓存有效期与淘汰策略
-
有效期TTL(time to live)
- 节省空间
- 做到数据弱一致性,当缓存数据失效后,可以保证数据的一致性
-
缓存的过期策略
- 定时过期
- 每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量
- 惰性过期
- 只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
- 定期过期
- 每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
- 定时过期
-
Redis中同时使用了惰性过期和定期过期两种过期策略。
- 定期删除,默认是每100ms检测一次,遇到过期的key则进行删除,这里的检测并不是顺序检测,而是随机检测,因此存在已过期但没有监测到的键
- 当我们去读/写一个已经过期的key时,会触发Redis的惰性删除策略,直接会干掉过期的key
-
缓存淘汰策略
Redis的内存淘汰策略是指在Redis的用于缓存的内存不足时,怎么处理需要新写入且需要申请额外空间的数据
- LRU(Least recently used)
- LRU算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”
- 基本思路
- 新数据插入到列表头部;
- 每当缓存命中(即缓存数据被访问),则将数据移到列表头部;
- 当列表满的时候,将列表尾部的数据丢弃
- LFU(Least Frequently Used)
- 它是基于“如果一个数据在最近一段时间内使用次数很少,那么在将来一段时间内被使用的可能性也很小”的思路。
- 因为某个key过去访问量过大,因此可能一直不会被删除,需要增加其他策略,定期衰减
- LRU(Least recently used)
缓存更新方式
-
Cache Aside 更新模式(同时更新数据库和缓存)
- 先更新数据库,在更新缓存
- 先删除缓存,在更新数据库
- 先更新数据库,在删除缓存
-
Read/Write-through 通读(先更新缓存,缓存负责同步更新数据库)
-
Write-behind caching(先更新缓存,缓存定时异步更新数据库)
-
三种缓存模式的优缺点:
Cache Aside 更新模式实现起来比较简单,但是需要维护两个数据存储,一个是缓存(Cache),一个是数据库(Repository)。
Read/Write Through 更新模式只需要维护一个数据存储(缓存),但是实现起来要复杂一些。
Write Behind Caching 更新模式和Read/Write Through 更新模式类似,区别是Write Behind Caching 更新模式的数据持久化操作是异步的,但是Read/Write Through 更新模式的数据持久化操作是同步的。优点是直接操作内存速度快,多次操作可以合并持久化到数据库。缺点是数据可能会丢失,例如系统断电等。
缓存高并发过程中容易出现的问题和解决方案
缓存穿透
-
缓存只是为了缓解数据库压力而添加的一层保护层,当从缓存中查询不到我们需要的数据就要去数据库中查询了。如果被黑客利用,频繁去访问缓存中没有的数据,那么缓存就失去了存在的意义,瞬间所有请求的压力都落在了数据库上,这样会导致数据库连接异常
-
解决方案:
1.对于返回为NULL的依然缓存,对于抛出异常的返回不进行缓存,注意不要把抛异常的也给缓存了。采用这种手段的会增加我们缓存的维护成本,需要在插入缓存的时候删除这个空缓存,当然我们可以通过设置较短的超时时间来解决这个问题
2.制定一些规则过滤一些不可能存在的数据,小数据用BitMap,大数据可以用布隆过滤器,比如你的订单ID 明显是在一个范围1-1000,如果不是1-1000之内的数据那其实可以直接给过滤掉
缓存雪崩
-
缓存雪崩是指缓存不可用或者大量缓存由于超时时间相同在同一时间段失效,大量请求直接访问数据库,数据库压力过大导致系统雪崩。
-
解决方案:
1、给缓存加上一定区间内的随机生效时间,不同的key设置不同的失效时间,避免同一时间集体失效。比如以前是设置10分钟的超时时间,那每个Key都可以随机8-13分钟过期,尽量让不同Key的过期时间不同。
2、采用多级缓存,不同级别缓存设置的超时时间不同,及时某个级别缓存都过期,也有其他级别缓存兜底。
3、利用加锁或者队列方式避免过多请求同时对服务器进行读写操作。