Redis是一个由 Salvatore Sanfilippo 写的 key_value 存储系统,是当前互联网世界最为流行的NoSQL数据库。NoSQL在互联网系统中的作用很大,因为他可以在很大程度上提高互联网系统的性能
Redis 开源免费,提供了 java,C/C++,C#,PHP 等客户端,使用方便。主要应用于内容缓存 和 处理大量数据的高访问负载
Redis 具备一定持久层的功能,也可以作为一种缓存工具。对于NoSQL数据库而言,作为持久层,它存储的数据是半结构化的,这就意味着计算机在读入内存中有更少的规则,读入速度更快
对于那些结构化、多范式规则的数据库系统而言,它更具性能优势。作为缓存,它可以支持大数据存入内存中,只要命中率高,它就能快速响应,以你为在内存中的数据读 / 写比数据库读/写磁盘的速度快几十到上百倍
NoSQL和传统数据库的区别
- 首先,NoSQL 的数据主要存储在内存中(部分可以持久化到磁盘),而数据库主要是磁盘。其次,NoSQL 数据结构比较简单,虽然能处理很多的问题,但是其功能毕竟是有限的,不如数据库的 SQL 语句强大,支持更为复杂的计算。
- 再者,NoSQL 并不完全安全稳定,由于它基于内存,一旦停电或者机器故障数据就很容易丢失数据,其持久化能力也是有限的,而基于磁盘的数据库则不会出现这样的问题。
- 最后,其数据完整性、事务能力、安全性、可靠性及可扩展性都远不及数据库
- 数据库系统有更好的规范性和数据完整性,功能更强大,作为持久层更为完善,安全性更高。而 NoSQL 结构松散、不完整,功能有限,目前尚不具备取代数据库的实力,但是作为缓存工具,它的高性能、高响应等功能,使它成为一个很重要的工具
Redis的优点
- 响应快速( 速度快 ) :Redis 响应非常快,每秒可以执行大约 110 000 个写入操作,或者 81 000 个读操作,其速度远超数据库。如果存入一些常用的数据,就能有效提高系统的性能
- 支持6 种数据类型( 数据类型丰富 ) :它们是字符串、哈希结构、列表、集合、可排序集合和基数。比如对于字符串可以存入一些 Java 基础数据类型,哈希可以存储对象,列表可以存储 List 对象等。这使得在应用中很容易根据自己的需要选择存储的数据类型,方便开发。对于 Redis 而言,虽然只有 6 种数据类型,但是有两大好处:
— 一方面可以满足存储各种数据结构体的需要;
— 另外一方面数据类型少,使得规则就少,需要的判断和逻辑就少,这样读/写的速度就更快 - 操作都是原子的(原子性): 所有Redis的操作都是原子的,从而确保当两个客户同时访问 Redis 服务器时,得到的是更新后的值(最新值)。在需要高并发的场合可以考虑使用 Redis 的事务,处理一些需要锁的业务
- MultiUtility 工具 :Redis 可以在如缓存、消息传递队列中使用(Redis 支持“发布+订阅”的消息模式),在应用程序如 Web 应用程序会话、网站页面点击数等任何短暂的数据中使用。正是因为 Redis 具备这些优点,使得它成为了目前主流的 NoSQL 技术,在 Java 互联网中得到了广泛使用。
---- 一方面,使用 NoSQL 从数据库中读取数据进行缓存,就可以从内存中读取数据了,而不像数据库一样读磁盘。现实是读操作远比写操作要多得多,所以缓存很多常用的数据,提高其命中率有助于整体性能的提高,并且能减缓数据库的压力,对互联网系统架构是十分有利的
---- 另一方面,它也能满足互联网高并发需要高速处理数据的场合,比如抢红包、商品秒杀等场景,这些场合需要高速处理,并保证并发数据安全和一致性
Redis在Java Web中的应用
一般而言 Redis 在 Java Web 应用中存在两个主要的场景,
----- 一个是缓存常用的数据,
----- 另一个是在需要高速读/写的场合使用它快速读/写,比如一些需要进行商品抢购和抢红包的场合
由于在高并发的情况下,需要对数据进行高速读/写的场景,一个最为核心的问题是数据一致性和访问控制
缓存
在对数据库的读/写操作中,现实的情况是读操作的次数远超写操作,一般是 1:9 到 3:7 的比例,所以需要读的可能性是比写的可能性多得多
当发送 SQL 去数据库进行读取时,数据库就会去磁盘把对应的数据索引回来,而索引磁盘是一个相对缓慢的过程。如果把数据直接放在运行在内存中的 Redis 服务器上,那么不需要去读/写磁盘了,而是直接读取内存,显然速度会快得多,并且会极大减轻数据库的压力
在使用 Redis 存储的时候,需要从 3 个方面进行考虑。
- 业务数据常用吗?命中率如何?如果命中率很低,就没有必要写入缓存。
- 该业务数据是读操作多,还是写操作多,如果写操作多,频繁需要写入数据库,也没有必要使用缓存。
- 业务数据大小如何?如果要存储几百兆字节的文件,会给缓存带来很大的压力,有没有必要?
读操作![Redis的缓存应用](https://i-blog.csdnimg.cn/blog_migrate/50ecf50ad68a63f7b29b08b6c221270b.png)
— 当第一次读取数据的时候,读取 Redis 的数据就会失败,此时会触发程序读取数据库,把数据读取出来,并且写入 Redis。
— 当第二次及以后读取数据时,就直接读取 Redis,读到数据后就结束了流程,这样速度就大大提高了。
从上面的分析可知,大部分的操作是读操作,使用 Redis 应对读操作,速度就会十分迅速,同时也降低了对数据库的依赖,大大降低了数据库的负担
写操作
从流程可以看出,更新或者写入的操作,需要多个 Redis 的操作。如果业务数据写次数远大于读次数没有必要使用 Redis。如果是读次数远大于写次数,则使用 Redis 就有其价值了,因为写入 Redis 虽然要消耗一定的代价,但是其性能良好,相对数据库而言,几乎可以忽略不计
高速读/写场合
在一个瞬间成千上万的请求就会达到服务器的情况下,如果使用的是数据库,一个瞬间数据库就需要执行成千上万的 SQL,很容易造成数据库的瓶颈,严重的会导致数据库瘫痪,造成 Java Web 系统服务崩溃
在这样的场合的应对办法往往是考虑异步写入数据库,而在高速读/写的场合中单单使用 Redis 去应对,把这些需要高速读/写的数据,缓存到 Redis 中,而在满足一定的条件下,触发这些缓存的数据写入数据库中
进一步论述这个过程:
----- 当一个请求达到服务器,只是把业务数据先在 Redis 读/写,而没有进行任何对数据库的操作,换句话说系统仅仅是操作 Redis 缓存,而没有操作数据库,这个速度就比操作数据库要快得多,从而达到需要高速响应的效果。
----- 但是一般缓存不能持久化,或者所持久化的数据不太规范,因此需要把这些数据存入数据库,所以在一个请求操作完 Redis 的读/写后,会去判断该高速读/写的业务是否结束。
----- 这个判断的条件往往就是秒杀商品剩余个数为 0,抢红包金额为 0,如果不成立,则不会操作数据库;如果成立,则触发事件将 Redis 缓存的数据以批量的形式一次性写入数据库,从而完成持久化的工作。
----- 假设面对的是一个商品秒杀的场景,从上面的流程看,一个用户抢购商品,绝大部分的场合都是在操作内存数据库 Redis,而不是磁盘数据库,所以其性能更为优越。只有在商品被抢购一空后才会触发系统把 Redis 缓存的数据写入数据库磁盘中,这样系统大部分的操作基于内存,就能够在秒杀的场合高速响应用户的请求,达到快速应答。
----- 而现实中这种需要高速响应的系统会比上面的分析更复杂,因为这里没有讨论高并发下的数据安全和一致性问题,没有讨论有效请求和无效请求、事务一致性等诸多问题