最近接触了Redis数据库,这里对Redis进行一个简单的介绍。
Redis是一个广泛被使用的高性能的存储系统,它具有一下特点:
1.存储在内存中,所以读写性能非常高
2.由C编写而成,所以跨平台支持的性能非常高
3.使用kv存储,同时支持多种数据类型操作,并且其中key是二进制安全的,也就是说所有的二进制序列都可以作为key从字符串甚至到图片内容都可以
4.redis是单线程的,所以所有的操作都是原子性的。
目前redis的应用主要有以下三类场景:
一、Redis作为缓存应用
Redis非常适合作为缓存层,因为他的性能好并且是单线程的。说到缓存就不得不说到缓存的命中、失败、驱逐和更新的问题。这里简单的介绍以下这些概念/
对于缓存的命中和失败很容易理解,我们都知道我们的计算机中的内存和硬盘,如果我们需要一个数据在内存中找到了那就是命中了,这时候就不需要再去硬盘里边读取了,因为硬盘读取速度十分的慢所以这样在某种程度上也就提高了性能。
缓存的驱逐:是指当缓存数据达到最大内存之后,当再有新的数据来的适合就需要淘汰掉一些旧数据。目前Redis提供了6中淘汰策略:
allkeys-lru:驱逐掉LRU数据
allkeys-random:随机驱逐数据
volatile-lru:从设置了过去时间的数据中选取lru数据
volatile-ttl:从设置了过期时间数据中选取ttl(生存时间)最少的
volatile-random:从设置了过期时间数据中随机淘汰
noebiction:直接返回错误
缓存更新是指当前缓存中的数据不是最新的了,很可能某个key对应的value值被更新了,这时候就需要进行缓存更新。目前的更新策略主要包括:
1.Cache Aside Pattern: 这种方式DB对于客户端来说是不透明的,客户端在读数据的适合先去cache读,如果命中了直接 返回数据,如果没有命中客户端再去DB中读取数据,然后将读取到的数据写入到cache中,客户端在写数据的适合直接向DB中写入然后将cache中对应的数据设置为失效。
2.Read/Write Through Pattern:这种方式下DB对于客户端是透明的,read through 客户端读数据的时候,如果cache中命中,cache直接返回数据,如果不命中cache自动从DB中读取数据并缓存,然后返回数据,write through客户端写数据的时候,如果cache不命中,直接写入DB,如果命中先更新缓存然后同步到DB。
3.Write Behind Caching Pattern:这种凡事在write through的基础上进行了修改,写数据的时候不再同时更新,而是变成了异步更新,在提高性能的同时降低了一致性,当写入数据的时候,cache直降数据保存在缓存快,并标记该缓存快为dirty,而不像数据库更新,只有当该块被驱逐的时候,才向DB中写入了更新了的数据,这样减少了写数据的阻塞时间,但是存在一个一致性的问题,如果dirty数据块还没有向DB中写入但是宕机了,那么数据就丢失了,并且如果cache负荷很大, 存在写入cache就立马被去驱逐的情况,这样的效率反而没有write高。
缓存穿透:是指当某个key值在数据库中不存在,但是还是在被不断的请求,这时候cache肯定不会被命中,于是所有的请求就不停的打到DB上。
解决方案:当key不存在的时候,也缓存一个默认值
缓存并发:是指某个key过期的时候,大量的请求在同一时间到达,将全部打到DB上,造成CPU和DB的性能大幅下降
解决方案:加锁,一个key值只允许一个线程查DB和写缓存,将并发量降到1
缓存雪崩:是指大量的key在同一时间失效,大量请求在同一时间打到DB上。
解决方案: 加锁,从而将雪崩问题降级到缓存并发,不同的key设置不同的过期时间,避免大量key在同一时间失效,设置二级缓存
二、Redis作为数据库
由于内存数据库因此性能非常搞笑,检索快,当它用作高并发的计数器,比如使用Redis作为剩余名额的计算,或者高并发队列的时候非常合适但于此同时内存数据库在数据量太大的时候,每次初始化都要load海量数据并且每次被封都需要dump海量数据到磁盘。但是Redis的kv存储结构,遇到关联查询就束手无策了。并且如果一致性较高的时候考虑Redis的时候较少。
三、Redis作为消息中间件
选择消息中间件一般主要考虑到以下几个因素:
消息路由,决定了消息中间件的灵活性,一般采用topic订阅或者pattern
消息可靠性,消息丢失的可能性以及恢复方法,一般依赖于持久化
消息堆积,高峰时暂存消息
ACK消息确认,保证至少消费一次
消息消费顺序,部分场景下要求消费顺序和产生一直
拓展和性能,能通过拓展来提高消费能力
不理解不过多介绍了/
四、Redis主要有的数据结构以及应用:
1.string 类型二进制安全 最长512mb 高并发下的计数器
2.hash 内部实现是hashmap 存储键值对 存储用户信息,比如用户姓名联系方式等
3.list 实际上是一个string链接起来的list因为是双向的,所以在头和尾插入是常量。中间插入是O(N) 消息队列,存储所有评论后获取前n条评论
4.set 无序不重复的string集合,内部实现是一个value永远为null 的hashmap 可以进行交/并/差运算 存储用户关注/粉丝 这样可以通过交操作获取共同关注
5.sorted set 和set不同点就是是有序的 比如排行榜
6.bitmap 基于string但是能够进行位操作 统计用户签到,只需要365就可以记录用户一年的签到情况,或者记录当前在线人员
五、Redis的 持久化
Redis的数据全部保存在内存中,一旦服务器挂了或者突然宕机了,那么里边的数据就会丢失,为了使服务器突然关机也能够保存数据,必须通过持久化的方式将数据保存到磁盘,主要有两种方式:RDB和AOF
1.RDB在制定时间间隔对数据进行全量的快照存储,把所有的数据全部保存到磁盘中。产生的备份文件格式紧凑,体积小,恢复起来快。
2.AOF是通过不断的写命令将数据追加到文件中,追加命令到缓冲区会阻塞主线程,而保存到磁盘的过程则是后台线程进行的。当AOF文件过大时,Redis会对文件进行重写。
虽然Redis是一个NoSql数据库但是支持一定的数据,不过Redis在开启事务之后,client的所有命令都会被Redis加入一个命令队列。Redis还提供了watch命令用于CAS实现了乐观锁。
Redis事务有一些特性:事务中如果某个命令失败了,那么其他命令继续执行,不支持回滚,不支持持久化。
Redis事务的ACID分析:
原子性:不满足,因为无法回滚,遇到错误命令会继续执行
一致性:能够满足,因为所有执行的写操作都是正确操作
隔离性:能够满足,因为Redis中所有的操作都是原子性,事务进行中不会执行其他命令
持久性:不能满足,AOF及时开启了每次写模式,依然有从缓冲区保存到磁盘这个短暂时间段是由后台线程完成的。
Redis 单条命令 通信读取4k per second 本地读取15k~20k per second
批量处理性能是单挑的10~20倍