文章目录
一、理解Redis
1、什么是Redis?
Redis是一个开源的内存中的数据结构存储系统,它可以用作:数据库、缓存和消息中间件
redis是什么
redis是一个高性能的key-value数据库,它是完全开源免费的,而且redis是一个NOSQL类型数据库,是为了解决高并发、高扩展,大数据存储等一系列的问题而产生的数据库解决方案,是一个非关系型的数据库。但是,它也是不能替代关系型数据库,只能作为特定环境下的扩充。
redis是一个以key-value存储的数据库结构型服务器,它支持的数据结构类型包括:字符串(String)、链表(lists)、哈希表(hash)、集合(set)、有序集合(Zset)等。为了保证读取的效率,redis把数据对象都存储在内存当中,它可以支持周期性的把更新的数据写入磁盘文件中。而且它还提供了交集和并集,以及一些不同方式排序的操作。
2、为什么要使用Redis?
从性能和并发两方面考虑:
在我们日常的Java Web开发中,无不都是使用数据库来进行数据的存储,由于一般的系统任务中通常不会存在高并发的情况,
所以这样看起来并没有什么问题,可是一旦涉及大数据量的需求,比如一些商品抢购的情景,或者是主页访问量瞬间较大的时候,
单一使用数据库来保存数据的系统会因为面向磁盘,磁盘读/写速度比较慢的问题而存在严重的性能弊端,一瞬间成千上万的请求到来,
需要系统在极短的时间内完成成千上万次的读/写操作,这个时候往往不是数据库能够承受的,极其容易造成数据库系统瘫痪,
最终导致服务宕机的严重生产问题。
所以,在大并发的情况下,所有的请求直接访问数据库,数据库会出现连接异常。这个时候,就需要使用 Redis 做一个缓冲操作,让请求先访问到 Redis,而不是直接访问数据库。
特点:
- 纯内存操作
- 核心是基于非阻塞的 IO 多路复用机制
- 单线程反而避免了多线程的频繁上下文切换问题
3、redis的应用场景:
(1)redis由于数据的读取和操作都在内存当中操作,读写的效率较高,所以经常被用来做数据的缓存。把一些需要频繁访问的数据,而且在短时间之内不会发生变化的,放入redis中进行操作。从而提高用户的请求速度和降低网站的负载,降低数据库的读写次数,就把这些数据放到缓存中。
(2)一些常用的实时计算的功能。需要实时变化和展示的功能,就可以把相关数据放在redis中进行操作。大大提高效率。
(3)消息队列,经常用来构建类似实时聊天系统的功能,大大提高应用的可用性。
4、Redis事件机制
Redis 采用事件驱动机制来处理大量的网络IO。
Redis中的事件驱动库只关注网络IO,以及定时器。该事件库处理下面两类事件:
- 文件事件(file event):用于处理 Redis 服务器和客户端之间的网络IO。
- 时间事件(time eveat):Redis
服务器中的一些操作(比如serverCron函数)需要在给定的时间点执行,而时间事件就是处理这类定时操作的。
aeEventLoop(事件管理器)是整个事件驱动的核心,它管理着文件事件表和时间事件列表,
不断地循环处理着就绪的文件事件和到期的时间事件。
(1)文件事件处理器
Redis基于Reactor模式开发了自己的网络事件处理器,也就是文件事件处理器。文件事件处理器使用IO多路复用技术,同时监听多个套接字,并为套接字关联不同的事件处理函数。当套接字的可读或者可写事件触发时,就会调用相应的事件处理函数。
Redis 使用的IO多路复用技术主要有:select、epoll、evport和kqueue等。
如下图所示,文件事件处理器有四个组成部分,它们分别是套接字、I/O多路复用程序、文件事件分派器以及事件处理器。
①redis是基于reactor模式开发了网络事件处理器,这个处理器叫做 文件事件处理器(file event Handler)。这个文件事件处理器是单线程的,所以redis才叫做单线程模式,采用IO多路复用机制去同时监听多个socket,根据socket上的时间来选择对应的事件处理器来处理这个事件。
②如果被监听的socket准备好执行accept、read、write、close等操作的时候,跟操作对应的文件事件就会产生,这个时候文件处理器就会调用之前关联好的的事件处理器来处理这个事件。
③文件事件处理器是单线程模式运行的,但是通过IO多路复用机制监听多个socket,可以实现高性能的网络通信模型,又可以跟内部其他单线程的模块进行对接,保证了redis内部的线程模型的简单性。
④文件事件处理器的结构包含四个部分:多个socket、IO多路复用程序、文件事件分派器、事件处理器(命令请求处理器、命令回复处理器、连接应答处理器,等等)。
⑤多个socket可能并发的产生不同的操作,每个操作对应不同的文件 事件,但是IO多路复用程序会监听多个socket,但是会将socket放到一个队列中去处理,每次从队列中取出一个socket给事件分派器,事件分派器把socket给对应的事件处理器。
⑥然后一个socket的事件处理完了之后,IO多路复用程序才会将队列中的下一个socket给事件分派器。事件分派器会根据每个socket当前产生的事件,来选择对应的事件处理器来处理。
(2)文件事件
所以,一次 Redis 客户端与服务器进行连接并且发送命令的过程如上图所示。
- 客户端向服务端发起建立 socket 连接的请求,那么监听套接字将产生 AE_READABLE
事件,触发连接应答处理器执行。处理器会对客户端的连接请求进行应答,然后创建客户端套接字,以及客户端状态,并将客户端套接字的
AE_READABLE 事件与命令请求处理器关联。 - 客户端建立连接后,向服务器发送命令,那么客户端套接字将产生 AE_READABLE
事件,触发命令请求处理器执行,处理器读取客户端命令,然后传递给相关程序去执行。 - 执行命令获得相应的命令回复,为了将命令回复传递给客户端,服务器将客户端套接字的 AE_WRITEABLE 事件与命令回复处理器关联。当客户端试图读取命令回复时,客户端套接字产生 AE_WRITEABLE 事件,触发命令回复处理器将命令回复全部写入到套接字中。
(3)时间事件
Redis 的时间事件分为以下两类:
- 定时事件:让一段程序在指定的时间之后执行一次。
- 周期性事件:让一段程序每隔指定时间就执行一次。
5、redis单线程+多路I/O复用模型
redis 内部使用文件事件处理器 file event handler,这个文件事件处理器是单线程的,所以 redis 才叫做单线程的模型。
它采用 IO 多路复用机制同时监听多个 socket,根据 socket 上的事件来选择对应的事件处理器进行处理。
单线程的模式解决了数据存储的顽疾:数据并发安全,任何运行多线程同时访问数据库都会存在这个问题,所以才有了mysql的mvcc和锁, Memcached 的cas 乐观锁,来保证数据不会出现并发导致的数据问题,但是redis 使用单线程就不存在这个问题:
1,单线程足够简单,无论在redis的实现还是作为调用方,都不需要为数据并发提心吊胆,不需要加锁。
2.不会出现不必要的线程调度,你知道多线程,频繁切换上下文,也会带来很多性能消耗
什么是切换上下文?
多任务竞争CPU,cpu变换任务的时候进行CPU上下文切换(context switch)。CPU执行任务有4种方式:进程、线程、或者硬件通过触发信号导致中断的调用。
当切换任务的时候,需要记录任务当前的状态和获取下一任务的信息和地址(指针),这就是上下文的内容。因此,上下文是指某一时间点CPU寄存器(CPU register)和程序计数器(PC)的内容, 广义上还包括内存中进程的虚拟地址映射信息。
上下文切换的过程:
(1)记录当前任务的上下文(即寄存器和计数器等所有的状态);
(2)找到新任务的上下文并加载;
(3)切换到新任务的程序计算器位置,恢复其任务。
根据任务的执行形式,相应的下上文切换,有进程上下文切换、线程上下文切换、以及中断上下文切换三类。
进程和线程的区别:
进程是资源分配和执行的基本单位;线程是任务调度和运行的基本单位。线程没有资源,进程给指针提供虚拟内存、栈、变量等共享资源,而线程可以共享进程的资源。
进程的上下文切换过程:
(a)接收到切换信号,挂起进程,记录当前进程的虚拟内存、栈等资源存储;
(b)将这个进程在 CPU 中的上下文状态存储于起来;
©然后在内存中检索下一个进程的上下文;
(d)并将其加载到 CPU的寄存器中恢复;
(e)还需要刷新进程的虚拟内存和用户栈;
(f)最后跳转到程序计数器所指向的位置,以恢复该进程。
线程上下文切换:
(1)不同于进程之间的线程上下文切换,其过程和进程上下文切换大致相同。
(2)进程内部的线程上下文切换。不需要切换进程的用户资源,只需要切换线程私有的数据和寄存器等。这会比进程上下文进程切换消耗的资源少,所以多线程相比多进程的优势。
中断上下文切换:
快速响应硬件的事件,中断处理会打断进程的正常调度和执行。同一CPU内,硬件中断优先级高于进程。切换过程类似于系统调用的时候,不涉及到用户运行态资源。但大量的中断上下文切换同样可能引发性能问题。
多路 I/O 复用模型,这个也是java 的NIO体系使用的IO模型,也是linux诸多IO模型中的一种,说白了就是当一个请求来访问redis后,redis去组织数据要返回给请求,这个时间段,redis的请求入口不是阻塞的,其他请求可以继续向redis发送请求,等到redis io流完成后,再向调用者返回数据,这样一来,单线程也不怕会影响速度了
- 这里“多路”指的是多个网络连接
- “复用”指的是复用同一个线程
我们使用单线程的方式是无法发挥多核CPU 性能,不过我们可以通过在单机开多个Redis 实例来完善
6、如何应对缓存穿透和缓存雪崩问题
这两个问题,一般中小型传统软件企业很难碰到。如果有大并发的项目,流量有几百万左右,这两个问题一定要深刻考虑。缓存穿透,即黑客故意去请求缓存中不存在的数据,导致所有的请求都怼到数据库上,从而数据库连接异常。
缓存穿透解决方案:利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试。采用异步更新策略,无论 Key 是否取到值,都直接返回,所有的查询以缓存中为准。Value 值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。提供一个能迅速判断请求是否有效的拦截机制,比如,利用布隆过滤器,内部维护一系列合法有效的 Key。迅速判断出,请求所携带的 Key 是否合法有效。如果不合法,则直接返回。
缓存雪崩,即缓存同一时间大面积的失效,这个时候又来了一波请求,结果请求都怼到数据库上,从而导致数据库连接异常。
缓存雪崩解决方案:给缓存的失效时间,加上一个随机值,避免集体失效。使用互斥锁,但是该方案吞吐量明显下降了。双缓存。我们有两个缓存,缓存 A 和缓存 B。缓存 A 的失效时间为 20 分钟,缓存 B 不设失效时间。自己做缓存预热操作。然后细分以下几个小点:从缓存 A 读数据库,有则直接返回;A 没有数据,直接从 B 读数据,直接返回,并且异步启动一个更新线程,更新线程同时更新缓存 A 和缓存 B。
7、如何解决 Redis 的并发竞争 Key 问题
这个问题大致就是,同时有多个子系统去 Set 一个 Key。这个时候要注意什么呢?大家基本都是推荐用 Redis 事务机制。
但是并不推荐使用 Redis 的事务机制。因为我们的生产环境,基本都是 Redis 集群环境,做了数据分片操作。你一个事务中有涉及到多个 Key 操作的时候,这多个 Key 不一定都存储在同一个 redis-server 上。
如果对这个 Key 操作,不要求顺序
这种情况下,准备一个分布式锁,大家去抢锁,抢到锁就做 set 操作即可,比较简单。
如果对这个 Key 操作,要求顺序
假设有一个 key1,系统 A 需要将 key1 设置为 valueA,系统 B 需要将 key1 设置为 valueB,系统 C 需要将 key1 设置为 valueC。
期望按照 key1 的 value 值按照 valueA > valueB > valueC 的顺序变化。这种时候我们在数据写入数据库的时候,需要保存一个时间戳。
假设时间戳如下:
系统 A key 1 {valueA 3:00}
系统 B key 1 {valueB 3:05}
系统 C key 1 {valueC 3:10}
那么,假设系统 B 先抢到锁,将 key1 设置为{valueB 3:05}。接下来系统 A 抢到锁,发现自己的 valueA 的时间戳早于缓存中的时间戳,那就不做 set 操作了,以此类推。其他方法,比如利用队列,将 set 方法变成串行访问也可以。
8、Redis 的过期策略和内存淘汰机制
Redis 是否用到家,从这就能看出来。比如你 Redis 只能存 5G 数据,可是你写了 10G,那会删 5G 的数据。怎么删的,这个问题思考过么?
正解:Redis 采用的是定期删除+惰性删除策略
定期删除+惰性删除如何工作
定期删除,Redis 默认每个 100ms 检查,有过期 Key 则删除。需要说明的是,Redis 不是每个 100ms 将所有的 Key 检查一次,而是随机抽取进行检查。如果只采用定期删除策略,会导致很多 Key 到时间没有删除。于是,惰性删除派上用场。
(1)采用定期删除+惰性删除就没其他问题了么?
不是的,如果定期删除没删除掉 Key。并且你也没及时去请求 Key,也就是说惰性删除也没生效。这样,Redis 的内存会越来越高
(2)如果一个键过期了,那么它什么时候会被删除呢?
定时删除:在设置键的过期时间的同时,创建一个定时器( timer ). 让定时器在键的过期时间来临时,立即执行对键的删除操作。
惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键;如果没有过期,就返回该键。
定期删除: 每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键。至于要删除多少过期键,以及要检查多少个数据库, 则由算法决定。
在这三种策略中,第一种和第三种为主动删除策略, 而第二种则为被动删除策略
(3)定时删除
定时删除策略对内存是最友好的:通过使用定时器,定时删除策略可以保证过期键会尽可能快地被删除,并释放过期键所占用的内存。另一方面,定时删除策略的缺点是,它对CPU 时间是最不友好的:在过期键比较多的情况下,删除过期键这一行为可能会占用相当一部分CPU 时间,在内存不紧张但是CPU 时间非常紧张的情况下.将CPU 时间用在删除和当前任务无关的过期键上,无疑会对服务器的响应时间和吞吐量造成影响。
例如,如果正有大量的命令请求在等待服务器处理,并且服务器当前不缺少内存,那么服务器应该优先将CPU 时间用在处理客户端的命令请求上面,而不是用在删除过期键上面。除此之外,创建一个定时器需要用到Redis 服务器中的时间事件,而当前时间事件的实现方式一一无序链表,查找一个事件的时间复杂度为O(N)一并不能高效地处理大量时间事件。
因此,要让服务器创建大量的定时器,从而实现定时删除策略,在现阶段来说并不现实。
(4)惰性删除
惰性删除策略对CPU 时间来说是最友好的:程序只会在取出键时才对键进行过期检查,这可以保证删除过期键的操作只会在非做不可的情况下进行, 并且删除的目标仅限于当前处理的键,这个策略不会在删除其他无关的过期键上花费任何CPU时间。
惰性删除策略的缺点是,它对内存是最不友好的: 如果一个键已经过期,而这个键又仍然保留在数据库中,那么只要这个过期键不被删除,它所占用的内存就不会释放。在使用惰性删除策略时,如果数据库中有非常多的过期键,而这些过期键又恰好没有被访问到的话,那么它们也许永远也不会被删除(除非用户手动执行FLUSHDB),我们甚至可以将这种情况看作是一种内存泄漏一一无用的垃圾数据占用了大量的内存,而服务器却不会自己去释放它们,这对于运行状态非常依赖于内存的Redis服务器来说,肯定不是一个好消息。
举个例子,对于一些和时间有关的数据,比如日志(log) ,在某个时间点之后,对它们的访问就会大大减少,甚至不再访问,如果这类过期数据大量地积压在数据库中,用户以为服务器已经自动将它们删除了,但实际上这些键仍然存在, 而且键所占用的内存也没有释放,那么造成的后果肯定是非常严重的。
(5)定期删除
从上面对定时删除和惰性删除的讨论来看,这两种删除方式在单一使用时都有明显的缺陷:
定时删除占用太多CPU 时间,影响服务器的响应时间和吞吐量。惰性删除浪费太多内存,有内存泄漏的危险。
定期删除策略是前两种策略的一种整合和折中:
定期删除策略每隔一段时间执行一次删除过期键操作,并通过限制删除操作执行的时长和频率来减少删除操作对CPU 时间的影响。除此之外,通过定期删除过期键,定期删除策略有效地减少了因为过期键而带来的内存浪费。定期删除策略的难点是确定删除操作执行的时长和频率:
如果删除操作执行得太频繁,或者执行的时间太长,定期删除策略就会退化成定时删除策略,以至于将C P U 时间过多地消耗在删除过期键上面。
如果删除操作执行得太少,或者执行的时间太短,定期删除策略又会和惰性删除策略一样,出现浪费内存的情况。
因此,如果采用定期删除策略的话,服务器必须根据情况,合理地设置删除操作的执行时长和执行频率。
9、Redis的事务功能
Redis事务的概念:
Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
注意:如果对数据有强一致性要求,不能放缓存
(1)Redis会将一个事务中的所有命令序列化,然后按顺序执行。
Redis不可能在一个Redis事务的执行过程中插入执行另一个客户端发出的请求。这样便能保证Redis将这些命令作为一个单独的隔离操作执行。 在一个Redis事务中,Redis要么执行其中的所有命令,要么什么都不执行。因此,Redis事务能够保证原子性。EXEC命令会触发执行事务中的所有命令。因此,当某个客户端正在执行一次事务时,如果它在调用MULTI命令之前就从Redis服务端断开连接,那么就不会执行事务中的任何操作;相反,如果它在调用EXEC命令之后才从Redis服务端断开连接,那么就会执行事务中的所有操作
(2)为什么Redis不支持回滚?
回滚(Rollback)指的是程序或数据处理错误,将程序或数据恢复到上一次正确状态的行为。删除由一个或多个部分完成的事务执行的更新。
如果你具备关系型数据库的知识背景,你就会发现一个事实:在事务运行期间,虽然Redis命令可能会执行失败,但是Redis仍然会执行事务中余下的其他命令,而不会执行回滚操作,你可能会觉得这种行为很奇怪然而,这种行为也有其合理之处:
只有当被调用的Redis命令有语法错误时,这条命令才会执行失败(在将这个命令放入事务队列期间,Redis能够发现此类问题),或者对某个键执行不符合其数据类型的操作:实际上,这就意味着只有程序错误才会导致Redis命令执行失败,这种错误很有可能在程序开发期间发现,一般很少在生产环境发现。
Redis已经在系统内部进行功能简化,这样可以确保更快的运行速度,因为Redis不需要事务回滚的能力。
对于Redis事务的这种行为,有一个普遍的反对观点,那就是程序有可能会有缺陷(bug)。但是,你应当注意到:事务回滚并不能解决任何程序错误。例如,如果某个查询会将一个键的值递增2,而不是1,或者递增错误的键,那么事务回滚机制是没有办法解决这些程序问题的。请注意,没有人能解决程序员自己的错误,这种错误可能会导致Redis命令执行失败。正因为这些程序错误不大可能会进入生产环境,所以我们在开发Redis时选用更加简单和快速的方法,没有实现错误回滚的功能
10、redis的持久化
什么是持久化:
持久化就是把内存中的数据写到磁盘中去,防止服务器宕机了内存数据丢失。
两种持久化方式:
- RDB
RDB是Redis Database缩写
功能核心函数rdbSave(生成RDB文件)和rdbLoad(从文件加载内存)两个函数
内存中的数据对象 —rdbSave—> 磁盘中的RDB文件
内存中的数据对象 <—rdbLoad— 磁盘中的RDB文件
-
AOF
AOF是Append-only file缩写
服务器—flushAppendOnlyFile—>磁盘中的AOF文件
每当执行服务器(定时)任务或函数时flushAppendOnlyFile函数都会被调用,这个函数执行以下两个工作
aof写入保存:
WRITE:根据条件,将aof_buf中的缓存写入到AOF文件。
SAVE:根据条件,调用fsync或fdatasync函数,将AOF文件保存到磁盘中。
存储结构:
内容是redis通讯协议(RESP)格式的命令文本存储。
比较:
1、aof文件比rdb更新频率高,优先使用aof还原数据。
2、aof比rdb安全性高。
3、rdb比aof性能好。
4、如果两个都配了优先加载AOF。
RESP协议:
RESP是redis客户端和服务端之间使用的一种通讯协议。
RESP特点:实现简单、快速解析、可读性好。
Redis的应用:
存储 缓存 用的数据;
需要高速读/写的场合使用它快速读/写;
11、redis实现的技术
- 持久化:持久化是最简单的高可用方法(有时甚至不被归为高可用的手段),主要作用是数据备份,即将数据存储在硬盘,保证数据不会因进程退出而丢失。
- 复制:复制是高可用Redis的基础,哨兵和集群都是在复制基础上实现高可用的。复制主要实现了数据的多机备份,以及对于读操作的负载均衡和简单的故障恢复。缺陷是故障恢复无法自动化;写操作无法负载均衡;存储能力受到单机的限制。
- 哨兵:在复制的基础上,哨兵实现了自动化的故障恢复。缺陷是写操作无法负载均衡;存储能力受到单机的限制。
- 集群:通过集群,Redis解决了写操作无法负载均衡,以及存储能力受到单机限制的问题,实现了较为完善的高可用方案。
二、Mysql和Redis数据库的区别
(1)mysql和redis的数据库类型
- mysql是关系型数据库,主要用于存放持久化数据,将数据存储在硬盘中,读取速度较慢。
- redis是NOSQL,即非关系型数据库,也是缓存数据库,即将数据存储在缓存中,缓存的读取速度快,能够大大的提高运行效率,但是保存时间有限
(2)mysql的运行机制
mysql作为持久化存储的关系型数据库,相对薄弱的地方在于每次请求访问数据库时,都存在着I/O操作,如果反复频繁的访问数据库。第一:会在反复链接数据库上花费大量时间,从而导致运行效率过慢;第二:反复的访问数据库也会导致数据库的负载过高,那么此时缓存的概念就衍生了出来。
(3)缓存
缓存就是数据交换的缓冲区(cache),当浏览器执行请求时,首先会对在缓存中进行查找,如果存在,就获取;否则就访问数据库。
缓存的好处就是读取速度快
(4)redis数据库
redis数据库就是一款缓存数据库,用于存储使用频繁的数据,这样减少访问数据库的次数,提高运行效率。
(5)redis和mysql的区别总结
类型上:
从类型上来说,mysql是关系型数据库,redis是缓存数据库
作用上:
mysql用于持久化的存储数据到硬盘,功能强大,但是速度较慢
redis用于存储使用较为频繁的数据到缓存中,读取速度快
速度上:
redis的速度:单机读可达10000次/s 写可达5000/s
mysql 经过了这么多年优化 才1000次/S,500次/S
需求上:
mysql和redis因为需求的不同,一般都是配合使用。
注意:
MySQL是关系型数据库,是持久化存储的,查询检索的话,会涉及到磁盘IO操作,为了提高性能,可以使用缓存技术,而memcached就是内存数据库,数据存储在内存中(当然也可以进行持久化存储),可以用作缓存数据库。用户首先去memcached查询数据,如果未查询到(即缓存未命中),才去MySQL中查询数据,查询到的数据会更新到缓存数据库中,提供给下次可能进行的查询。提高了数据查询方面的性能。
Redis和memcached都是缓存数据库,可以大大提升高数据量的web访问速度。
但是memcached只是提供了简单的数据结构string,而Redis的value可以是string、list、set、hash、sorted set这些,功能更加强大。
web应用中一般采用MySQL+Redis的方式,web应用每次先访问Redis,如果没有找到数据,才去访问MySQL。
Redis是内存数据库,数据保存在内存中,访问速度快。MySQL是关系型数据库,功能强大,存储在磁盘中,数据访问速度慢。像memcached,MongoDB,Redis等,都属于No sql系列。
三、Memcached和Redis对比
(1)Redis支持服务器端的数据操作
redis和memcached相比,redis拥有更多的 数据结构并且支持更丰富的数据操作 ,通常在memcached里面,你需要将数据拿到客户端来进行类型的修改然后在set回去,这样就严重增加了网络IO的次数和数据体积。在redis里面,这些操作可以在服务端完成,所以这些复杂的操作就和一般的GET/SET一样高效。所以,如果需要缓存能支持更复杂的结构和操作,那么redis是不错的选择 。
(2)内存使用率
如果使用简单的 key-value 存储的话,Memcached的内存利用率会更高,而如果Redis采用 hash 结构来做 key-value 存储,由于其组合式的压缩,其内存利用率会高于Memcached。
(3)性能
由于redis只使用单核,而Memcached可以使用多核,所以平均每一个核上redis在存储小数据时比Memcached性能更好。而在100K以上的数据中,Memcached性能要高于redis。
(4)集群模式
memcached没有原生的集群模式,需要依靠客户端来实现集群中分片写入数据;redis原生支持cluster模式,官方支持redis cluster集群模式。
对比点 | memcached | redis |
---|---|---|
是否支持服务端操作 | 不支持 | 支持 |
数据结构类型 | 简单 | 复杂多样 |
内存使用率 | 简单 key-value 存储,利用率高 | 采用hash结构存储,内存利用率高 |
性能 | 存储大数据性能高 | 存储小数据性能高 |
集群模式 | 没有原生支持 | 原生支持cluster模式 |
四、sql和nosql的区别
(1)概念
SQL (Structured Query Language) 数据库,指关系型数据库。主要代表:SQL Server,Oracle,MySQL(开源),PostgreSQL(开源)
NoSQL(Not Only SQL)泛指非关系型数据库。主要代表:MongoDB,Redis,CouchDB
(2)区别
存储方式
SQL数据存在特定结构的表中,通常以数据库表形式存储数据;而NoSQL则更加灵活和可扩展,存储方式可以是JSON文档、哈希表或者其他方式。
表/数据集合的数据的关系
在SQL中,必须定义好表和字段结构后才能添加数据,例如定义表的主键(primary key),索引(index),触发器(trigger),存储过程(stored procedure)等。表结构可以在被定义之后更新,但是如果有比较大的结构变更的话就会变得比较复杂。
在NoSQL中,数据可以在任何时候任何地方添加,不需要先定义表。
NoSQL也可以在数据集中建立索引。以MongoDB为例,会自动在数据集合创建后创建唯一值_id字段,
这样的话就可以在数据集创建后增加索引。
从这点来看,NoSQL可能更加适合初始化数据还不明确或者未定的项目中。
外部数据存储
SQL中如果需要增加外部关联数据的话,规范化做法是在原表中增加一个外键,关联外部数据表。
而在NoSQL中除了这种规范化的外部数据表做法以外,我们还能用非规范化方式把外部数据直接放到原数据集中,以提高查询效率。
SQL中的JOIN查询
SQL中可以使用JOIN表链接方式将多个关系数据表中的数据用一条简单的查询语句查询出来。
NoSQL暂未提供类似JOIN的查询方式对多个数据集中的数据做查询。所以大部分NoSQL使用非规范化的数据存储方式存储数据。
数据耦合性
SQL中不允许删除已经被使用的外部数据,以保证数据完整性。而NoSQL中则没有这种强耦合的概念,可以随时删除任何数据。
事务
SQL中如果多张表数据需要同批次被更新,即如果其中一张表更新失败的话其他表也不能更新成功。
这种场景可以通过事务来控制,可以在所有命令完成后再统一提交事务。
而NoSQL中没有事务这个概念,每一个数据集的操作都是原子级的。
查询性能
在相同水平的系统设计的前提下,因为NoSQL中省略了JOIN查询的消耗,故理论上性能上是优于SQL的。
目前许多大型互联网项目都会选用MySQL(或任何关系型数据库) + NoSQL的组合方案。
关系型数据库适合存储结构化数据,如用户的帐号、地址:
1)这些数据通常需要做结构化查询,比如join,这时候,关系型数据库就要胜出一筹
2)这些数据的规模、增长的速度通常是可以预期的
3)事务性、一致性
NoSQL适合存储非结构化数据,如文章、评论:
1)这些数据通常用于模糊处理,如全文搜索、机器学习
2)这些数据是海量的,而且增长的速度是难以预期的,
3)根据数据的特点,NoSQL数据库通常具有无限(至少接近)伸缩性
4)按key获取数据效率很高,但是对join或其他结构化查询的支持就比较差
基于它们的适用范围不同,目前主流架构才会采用组合方案,一个也不能少。
目前为止,还没有出现一个能够通吃各种场景的数据库,而且根据CAP理论,这样的数据库是不存在的。
NoSQL 技术
为了克服上述的问题,Java Web项目通常会引入NoSQL技术,这是一种基于内存的数据库,并且提供一定的持久化功能。
Redis和MongoDB是当前使用最广泛的NoSQL,而就Redis技术而言,它的性能十分优越,可以支持每秒十几万此的读/写操作,
其性能远超数据库,并且还支持集群、分布式、主从同步等配置,原则上可以无限扩展,让更多的数据存储在内存中,
更让人欣慰的是它还支持一定的事务能力,这保证了高并发的场景下数据的安全和一致性。
redis是Nosql数据库,是一个key-value存储系统
虽然redis是key-value的存储系统,但是redis支持的value存储类型是非常的多,比如字符串、链表、集合、有序集合和哈希
那么为什么要使用类似redis这样的Nosql数据库呢?
(1)当数据量的总大小一个机器放不下时;
(2)数据索引一个机器的内存放不下时;
(3)访问量(读写混合)一个实例放不下时。
单机时代,存储只用一台机器装mysql,如果每次存储成千上万条数据,这样很会导致mysql的性能很差,存储以及读取速度很慢,然后就演变成缓存+mysql+垂直拆分的方式。
Cache作为中间缓存时代,将所有的数据先保存到缓存中,然后再存入mysql中,减小数据库压力,提高效率。
但是当数据再次增加到又一个量级,上面的方式也不能满足需求,由于数据库的写入压力增加,缓存只能缓解数据库的读取压力。读写集中在一个数据库上让数据库不堪重负,大部分网站开始使用主从复制技术来达到读写分离,以提高读写性能和读库的可扩展性。Mysql的master-slave模式成为这个时候的网站标配了。
主从分离模式时代,在redis的高速缓存,MySQL的主从复制,读写分离的基础之上,这时MySQL主库的写压力开始出现瓶颈,而数据量的持续猛增,由于MyISAM使用表锁,在高并发下会出现严重的锁问题,大量的高并发MySQL应用开始使用InnoDB引擎代替MyISAM。