Redis简介

Redis简介

引言
在Web应用发展的初期,那时关系型数据库受到了较为广泛的关注和应用,原因是因为那时候Web站点基本上访问和并发不高、交互也较少。而在后来,随着访问量的提升,使用关系型数据库的Web站点多多少少都开始在性能上出现了一些瓶颈,而瓶颈的源头一般是在磁盘的I/O上。而随着互联网技术的进一步发展,各种类型的应用层出不穷,这导致在当今云计算、大数据盛行的时代,对性能有了更多的需求,主要体现在以下四个方面:

  1. 低延迟的读写速度:应用快速地反应能极大地提升用户的满意度
  2. 支撑海量的数据和流量:对于搜索这样大型应用而言,需要利用PB级别的数据和能应对百万级的流量
  3. 大规模集群的管理:系统管理员希望分布式应用能更简单的部署和管理
  4. 庞大运营成本的考量:IT部门希望在硬件成本、软件成本和人力成本能够有大幅度地降低
    为了克服这一问题,NoSQL应运而生,它同时具备了高性能、可扩展性强、高可用等优点,受到广泛开发人员和仓库管理人员的青睐。
    Redis是什么
    Redis是现在最受欢迎的NoSQL数据库之一,Redis是一个使用ANSI C编写的开源、包含多种数据结构、支持网络、基于内存、可选持久性的键值对存储数据库。它具备如下的特点:
    • 编写语言
    redis 是采用C语言编写的,好处就是底层代码执行效率高,依赖性低,没有太多运行时的依赖,而且系统的兼容性好,稳定性高
    • 存储
    redis是基于内存的数据里,可避免磁盘IO,因此也被称作缓存工具
    • 数据结构
    redis采用key-value的方式进行存储,也就是使用hash结构进行操作,数据的操作时间复杂度是O(1)
    • 设计模型
    redis采用的是单进程单线程的模型,可以避免上下文切换和线程之间引起的资源竞争。而且Redis还采用了IO多路复用技术,这里的多路复用是指多个socket网络连接,复用是指一个线程中处理多个IO请求,这样可以减少网络IO的消耗,大幅度提升效率
    Redis的应用场景有哪些
    Redis 的应用场景包括:缓存系统(“热点”数据:高频读、低频写)、计数器、消息队列系统、排行榜、社交网络和实时系统。
    综合来说,用缓存,主要有两个用途:高性能、高并发。
    高性能
    比如商品详情页,商详页是app 的基础,流量很大,为了给用户一个号的体验就需要在接口上做一些优化,将一些比较固定的属性,商品尺码、品牌故事、轮播图等信息,放在redis中中,每次直接读缓存能够大大提升接口响应速度。那这些信息是如何放在redis中的呢?后台更新了商品信息之后,redis中的数据就会被删除,在客户端再次请求接口的时候,把数据重新存入redis,这样能够一直保持redis中的数据是最新的。
    高并发
    所以要是你有个系统,高峰期一秒钟过来的请求有 1 万,那一个 mysql 单机绝对会死掉。你这个时候就只能上缓存,把很多数据放缓存,别放 mysql。缓存功能简单,说白了就是 key-value 式操作,单机支撑的并发量轻松一秒几万十几万,支撑高并发 so easy。单机承载并发量是 mysql 单机的几十倍。
    但是在实际业务中也应该合理使用缓存,如果出现了缓存与数据库双写不一致 、缓存雪崩、缓存穿透等问题,可就是致命的问题。
    Redis的数据类型及主要特性
    Redis提供的数据类型主要分为5种自有类型和一种自定义类型,这5种自有类型包括:String类型、哈希类型、列表类型、集合类型和顺序集合类型。
    String类型:
    String数据结构是简单的key-value类型,value其实不仅是String,也可以是数字。
    常用命令:set,get,decr,incr,mget等。
    应用场景:String 是最常用的一种数据类型,普通的key/value存储都可以归为此类。除了上面提到的操作,Redis还提供了下面的一些操作
    • 获取字符串长度
    • 设置和获取字符串的某一段内容
    • 设置及获取字符串的某一位(bit)
    • 设置及获取字符串的某一位
    • 批量设置一系列字符串的内容
    实现方式:String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr,decr等操作时会转成数值类型进行计算,此时redisObject的encoding字段为int。最大长度支持512M。
    哈希类型:
    该类型是由field和关联的value组成的map。其中,field和value都是字符串类型的。
    常用命令:hget,hset,hgetall等。
    应用场景:在Memcached中,我们经常将一些结构化的信息打包成HashMap,在客户端序列化后存储为一个字符串的值,比如用户的昵称、年龄、性别、积分等,这时候在需要修改其中某一项时,通常需要将所有值取出反序列化后,修改某一项的值,再序列化存储回去。这样不仅增大了开销,也不适用于一些可能并发操作的场合(比如两个并发的操作都需要修改积分)。而Redis的Hash结构可以使你像在数据库中Update一个属性一样只修改某一项属性值。
    简单的举个例子来描述下Hash的应用场景,比如我们要存储一个用户信息对象数据,包含以下信息:
    用户ID为查找的key,存储的value用户对象包含姓名,年龄,生日等信息,如果用普通的key/value结构来存储,主要有以下2种存储方式:
    第一种方式将用户ID作为查找key,把其他信息封装成一个对象以序列化的方式存储,这种方式的缺点是,增加了序列化/反序列化的开销,并且子啊需要修改其中一项信息时,需要把整个对象取回,并且修改操作需要对并发进行保护。
    第二种方法是这个用户信息对象有多少成员就存多少对key-value,用用户ID+对应属性名称作为唯一标识来取得对应属性的值,虽然省去了序列化开销和并发问题,但是用户ID为重复存储,如果存在大量这样的数据,内存浪费还是非常大的。
    那么Redis提供的Hash很好的解决了这个问题,Redis的Hash实际是内部存储的value为一个HashMap,并提供了直接存取这个Map成员的借口,如下图:
    也就是说,Key仍然是用户ID,value是一个Map,这个Map 的key是成员的属性名,value是属性值,这样对数据的修改和存取都介意直接通过内部Map的key(Redis里称内部Map的key为field),也就是通过key(用户ID)+field(属性标签)就可以操作对应属性数据了,既不需要重复存储数据,也不会带来序列化和并发修改控制的问题。
    这里同时需要注意,Redis提供了接口(hgstall)可以直接取到全部的属性数据,但是如果内部Map的成员很多,那么涉及到便利整个内部Map的操作,由于Redis单线程模型的缘故,这个遍历操作可能会比较耗时,而另其他客户端的请求完全不响应,这点需要格外注意。
    实现方式:Redis Hash对应Value内部实际就是一个HashMap,实际这里会有2种不同实现,这个Hash的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,对应的value redisObject的encoding为zipmap,当成员数量增大时会自动转成真正的HashMap,此时encoding为ht。
    列表类型:
    该类型是一个插入顺序排序的字符串元素集合, 基于双链表实现。
    常用命令:lpush,rpush,lpop,rpop,lrange等。
    应用场景:Redis list的应用场景非常多,也是Redis最重要的数据结构之一,比如twitter的关注列表,粉丝列表等都可以用Redis的list结构来实现。
    Lists就是链表,相信略有数据结构的人都应该能够理解其结构。使用Lists结构,我们可以轻松的实现最新消息排行等功能。Lists的另一个应用就是消息队列。可以利用Lists的PUSH操作,将任务存在Lists中,然后工作线程再用POP操作将任务取出进行执行。Redis还提供了操作Lists中某一段的api,你可以直接查询,删除Lists中某一段的元素。
    实现方式:Redis list的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用的这个数据结构。
    可以利用list的自然时间排序存储最新n个数据
    集合类型:
    Set类型是一种无顺序集合, 它和List类型最大的区别是:集合中的元素没有顺序, 且元素是唯一的。
    常用命令:sadd,spop,smembers,sunion 等。
    应用场景:Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。
    Sets 集合的概念就是一堆不重复值的组合。利用Redis提供的Sets数据结构,可以存储一些集合性的数据,比如在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis还为集合提供了求交集、并集、差集等操作,可以非常方便的实现如共同关注、共同喜好、二度好友等功能,对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存集到一个新的集合中。
    Set类型主要应用于:在某些场景,如社交场景中,通过交集、并集和差集运算,通过Set类型可以非常方便地查找共同好友、共同关注和共同偏好等社交关系。
    实现方式:set 的内部实现是一个 value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。
    顺序集合类型:
    ZSet是一种有序集合类型,每个元素都会关联一个double类型的分数权值,通过这个权值来为集合中的成员进行从小到大的排序。与Set类型一样,其底层也是通过哈希表实现的。
    常用命令:zadd,zpop, zmove, zrange,zrem,zcard,zcount等。
    使用场景:Redis sorted set的使用场景与set类似,区别是set不是自动有序的,而sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。当你需要一个有序的并且不重复的集合列表,那么可以选择sorted set数据结构,比如twitter 的public timeline可以以发表时间作为score来存储,这样获取时就是自动按时间排好序的。
    另外还可以用Sorted Sets来做带权重的队列,比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择按score的倒序来获取工作任务。让重要的任务优先执行。
    实现方式:Redis sorted set的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单。
    Redis的数据结构
    Redis的数据结构如下图所示:
    关于上表中的部分释义:
  5. 压缩列表是列表键和哈希键的底层实现之一。当一个列表键只包含少量列表项,并且每个列表项要么就是小整数,要么就是长度比较短的字符串,Redis就会使用压缩列表来做列表键的底层实现
  6. 整数集合是集合键的底层实现之一,当一个集合只包含整数值元素,并且这个集合的元素数量不多时,Redis就会使用整数集合作为集合键的底层实现
    数据持久化
    为什么数据持久化?
    由于redis的强大性能很大程度上是因为所有数据都是存储在内存中,然而当出现服务器宕机、redis重启等特殊场景,所有存储在内存中的数据将会丢失,这是无法容忍的事情,所以必须将内存数据持久化。例如:将redis作为数据库使用的;将redis作为缓存服务器使用等场景。
    持久化存在的方式?
    目前持久化存在两种方式:RDB方式和AOF方式。
    RDB方式:RDB持久化是把当前进程数据生成快照保存到硬盘的过程, 触发RDB持久化过程分为手动触发和自动触发。一般存在以下情况会对数据进行快照
    根据配置规则进行自动快照;
    用户执行SAVE, BGSAVE命令;
    执行FLUSHALL命令;
    执行复制(replication)时。
    优缺点:恢复数据较AOF更快;
    RDB方式数据没办法做到实时持久化/秒级持久化;存在老版本Redis服务无法兼容新版RDB格式的问题;非实时性。
    AOF方式:以独立日志的方式记录每次写命令(写入的内容直接是文本协议格式 ),重启时再重新执行AOF文件中的命令达到恢复数据的目的。
    AOF的工作流程操作: 命令写入(append) 、 文件同步(sync) 、 文件重写(rewrite) 、 重启加载(load)
    优点:实时性较好
    Redis常见问题:
    Redis常见问题解析:雪崩
    缓存雪崩是指Redis缓存层由于某种原因宕机后(有一种情况就是,缓存中大批量数据到过期时间,而查询数据量巨大,引起数据库压力过大甚至宕机),所有的请求会涌向存储层,短时间内的高并发请求可能会导致存储层挂机,称之为“Redis雪崩”。
    合理的规避方案:
    • 缓存数据的过期时间设置随机,方式同一时间大量数据过期现象发生。
    • 使用Redis集群,如果缓存数据库是分布式部署,将热点数据均匀分布在不同的缓存数据库中。
    • 设置热点数据永远不过期
    • 限流
    举个例子,对于系统 A,假设每天高峰期每秒 5000 个请求,本来缓存在高峰期可以扛住每秒 4000 个请求,但是缓存机器意外发生了全盘宕机。缓存挂了,此时 1 秒 5000 个请求全部落数据库,数据库必然扛不住,它会报一下警,然后就挂了。此时,如果没有采用什么特别的方案来处理这个故障,DBA 很着急,重启数据库,但是数据库立马又被新的流量给打死了。
    这就是缓存雪崩。
    缓存雪崩的事前事中事后的解决方案如下。
  • 事前:redis 高可用,主从+哨兵,redis cluster,避免全盘崩溃。 - 事中:本地 ehcache 缓存 + hystrix 限流&降级,避免 MySQL 被打死。 - 事后:redis持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。
    用户发送一个请求,系统 A 收到请求后,先查本地 ehcache 缓存,如果没查到再查 redis。如果 ehcache和 redis 都没有,再查数据库,将数据库中的结果,写入 ehcache 和 redis 中。
    限流组件,可以设置每秒的请求,有多少能通过组件,剩余的未通过的请求,怎么办?走降级!可以返回一些默认的值,或者友情提示,或者空白的值。
    Redis常见问题解析:击穿
    缓存击穿是指,在Redis获取某一key时, 由于key不存在在缓存中但数据库中有, 而必须向DB发起一次请求的行为, 这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力,称为“Redis击穿”。
    引发击穿的原因:
    • 第一次访问
    • 恶意访问不存在的key
    • Key过期
    合理的规避方案:
    • 服务器启动时, 提前写入对应的key,
    • 规范key的命名, 通过中间件拦截
    • 对某些高频访问的Key,设置合理的TTL或永不过期
    • 加互斥锁,代码如下
    说明:
    1)缓存中有数据,直接走上述代码49行后就返回结果了
    2)缓存中没有数据,第一个进入的线程,获取锁并从数据库去取数据,没释放锁之前,其他并行进入的线程会等待100ms,再重新去缓存取数据。这样就防止出现都去数据库重复取数据,重复往缓存中更新的数据的情况出现。
    3)当然这是简化处理,理论上如果能根据key值加锁就更好了,就是线程A从数据库取key1的数据并不妨碍线程B取key2的数据,上面代码明显做不到这一点。
    除了Redis,还有什么NoSQL型数据库
    市面上类似于Redis,同样是NoSQL型的数据库有很多,如下图所示,除了Redis,还有MemCache、Cassadra和Mongo。下面,我们就分别对这几个数据库做一下简要的介绍:
    Memcache:这是一个和Redis非常相似的数据库,但是它的数据类型没有Redis丰富。Memcache由LiveJournal的Brad Fitzpatrick开发,作为一套分布式的高速缓存系统,被许多网站使用以提升网站的访问速度,对于一些大型的、需要频繁访问数据库的网站访问速度的提升效果十分显著。
    Apache Cassandra:(社区内一般简称为C*)这是一套开源分布式NoSQL数据库系统。它最初由Facebook开发,用于储存收件箱等简单格式数据,集Google BigTable的数据模型与Amazon Dynamo的完全分布式架构于一身。Facebook于2008将 Cassandra 开源,由于其良好的可扩展性和性能,被 Apple、Comcast、Instagram、Spotify、eBay、Rackspace、Netflix等知名网站所采用,成为了一种流行的分布式结构化数据存储方案。
    MongoDB:是一个基于分布式文件存储、面向文档的NoSQL数据库,由C++编写,旨在为WEB应用提供可扩展的高性能数据存储解决方案。MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系型数据库的,它支持的数据结构非常松散,是一种类似json的BSON格式。
    测试思路
    1、缓存时间设置的合理性。缓存时间的设置,需要根据数据更新的频次合理设置;缓存时间太长会导致用户访问到的数据一直是老的,缓存时间太短对数据库访问会比较频繁。所以需要调研清楚实际数据更新的频次,再去设置缓存时间。
    2、缓存逻辑合理性。可以以这两个维度去测试:
    • 服务端或数据库返回数据的正确性。返回异常,不应该缓存;返回数据正常才缓存;
    • 存储缓存时查询条件。比如要缓存歌曲信息,可以查询歌名缓存也可以查询歌手缓存歌曲list;根据实际业务需求确认;
    3、缓存读取逻辑合理性。以下是比较合理的逻辑:
    • 有缓存,优先读取缓存;
    • 无缓存,请求借口或者查询数据库获取数据,并存储缓存
    4、缓存更新逻辑
    • 缓存失效后是否会更新缓存的内容
    5、缓存内容
    • redis缓存具体内容是否正确、格式(list、string)是否合理、实际每次缓存的数据数与需求是否一致
    6、缓存数据重复
    • 同样的数据触发缓存之后只有一条在redis缓存中可以查到,重复缓存会浪费资源。
    关于Redis的一些思考
<p> <span style="color:#E53333;">内容简介:</span> </p> <p> <span><span>Redis的的是完全开源免费的,遵守BSD协议,是一个高性能的键值数据库。是当前最热门的的的NoSql数据库之一,也被人们称为数据结构服务器。本</span><span></span>课程从Redis基本数据类型开始,了解不同数据类型的用法和底层实现 。进一步学习Redis的一些高级特性与工作原理。了解Redis在分布式环境中的工作方式,和实际项目的使用及问题解决。</span> </p> <p> <span><br /> </span> </p> <p> <span style="color:#E53333;">为什么学Redis?</span> </p> <p> <span>原因很简单,快!<br /> 这个问题在大并发,高负载的网站中必须考虑.redis数据库中的所有数据都存储在内存中。由于内存的读写速度远快于硬盘,因此Redis在性能上对比其他基于硬盘存储的数据库有非常明显的优势。<br /> 项目中使用Redis,主要是从两个角度去考虑:性能状语从句:并发。当然,Redis的的的还具备可以做分布式锁等其他功能,但是如果只是为了分布式锁这些其他功能,完全还有其他中间件代替,并不是非要使用Redis的的的。因此,这个问题主要从性能和并发两个角度去答。<br /> 性能:<br /> 我们在碰到需要执行耗时特别久,且结果不频繁变动的SQL,就特别适合将运行结果放入缓存,这样,后面的请求就去缓存中读取,请求使得能够迅速响应。</span> </p> <p> <span>并发:</span> </p> <p> <span> </span> </p> <p style="font-family:"font-size:16px;color:#4D4D4D;background-color:#FFFFFF;"> 在大并发的情况下,所有的请求直接访问数据库,数据库会出现连接异常。这个时候,就需要使用的的Redis的做一个缓冲操作,让请求先访问到的Redis的的,而不是直接访问数据库。 </p> <p style="font-family:"font-size:16px;color:#4D4D4D;background-color:#FFFFFF;"> redis优势: </p> <p style="font-family:"font-size:16px;color:#4D4D4D;background-color:#FFFFFF;"> 1.运行在内存,速度快官方号称支持并发11瓦特读操作,并发8瓦特写操作,可以说是相当彪悍了。<br /> 2.数据虽在内存,但是提供了持久化的支持,即可以将内存中的数据异步写入到硬盘中,同时不影响继续提供服务<br /> 3.支持数据结构丰富(string(字符串),list(链表),set(集合),zset(sorted set - 有序集合))和Hash(哈希类型,md5加密出来的那个串) </p> <p style="font-family:"font-size:16px;color:#4D4D4D;background-color:#FFFFFF;"> <br /> </p> <p style="font-family:"font-size:16px;color:#4D4D4D;background-color:#FFFFFF;"> <span style="color:#E53333;">课程大纲:</span> </p> <p style="font-family:"font-size:16px;color:#4D4D4D;background-color:#FFFFFF;"> 为了让大家快速系统了解Redis核心知识全貌,我为你总结了「Redis核心框架图」,帮你梳理学习重点,建议收藏!! </p> <p> <img src="https://img-bss.csdnimg.cn/202006240621088566.png" alt="" /> </p>
相关推荐
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页