Spring Data Redis 让 NoSQL 快如闪电 (1)
【编者按】本文作者为 Xinyu Liu,详细介绍了 Redis 的特性,并辅之以丰富的用例。在本文的第一部分,将重点概述 Redis 的方方面面。文章系国内 ITOM 管理平台 OneAPM 编译呈现。
建立在 Java 企业版之上的多层体系结构是强大的服务器端编程解决方案。作为一名从业多年的 Java 企业版开发人员,我最满意的就是三层企业开发法:最下方是 JPA/Hibernate 持久层,中间是 Spring 或 EJB 应用层,最上方则是 web 层。对于较为复杂的用例,我用 BPM(业务流程管理)、一个类似于 Drools 的规则引擎和一个集成框架(例如 Camel)集成了一个工作流驱动的解决方案。
但是,笔者最近接到一个任务,要设计一个拥有亚秒级响应延迟并能支持成千上万名并发用户的系统。我立即发现了自己常用的 Java 企业版栈区的局限性。基于关系数据库管理系统的传统型 web 应用程序,包括在 Hibernate/JPA 之上构建的应用程序,都有二阶延迟,扩展效果不佳。传统的 Java 企业版持久性体系结构无法满足我当时设计的系统的性能和处理能力要求。然后我转而尝试 NoSQL,最后发现了 Redis。
作为一种内存键值数据库,Redis 打破了数据库的传统定义(将数据保存在硬盘上)。反之,使用 Redis 时可结合持久性的 NoSQL 数据库,比如 MongoDB、HBase、Cassandra 或 DynamoDB。Redis 以远程缓存服务器见长,对易挥发数据来说是极快型数据库。
在本文中,笔者会介绍一些有关 Redis 的简单用例和进阶用例以及性能调优情况。当然,我还会做个简单概述,但我相信各位基本都了解 NoSQL 及其各种解决方案。
Spring Data Redis
Redis 几乎拥有针对所有编程语言的各种客户端库,其中就包括 Java。Jedis 可能是最受欢迎的 Java 客户端库了。本文中的示例都基于 Spring Data Redis,我把它作为一个较高层次的包装程序 API。Spring Data Redis 不仅配置方便,而且拥有各种友好的 API 和实用插件。
Redis 概述
和大多数 NoSQL 数据库一样,Redis 舍弃了表格、行列的关系概念。而事实上,Redis 是一种键值数据库,利用独特的字符串键值来存储和检索每条记录。Redis 支持把以下内置数据结构作为所有记录的值:
STRING
保有单个字符串值。LIST
、SET
和HASH
从语义上来说与 Java 中的相同数据结构相一致。ZSET
是由浮点分数安排的字符串列表,类似于 Java 中的PriorityQueue
。
不同于关系数据库管理系统中的表,Redis 数据结构是即时实例化的。如果用户查询的内容不存在于 Redis 中,系统只会返回空值。虽然 Redis 不允许嵌套结构,但用户可以执行自定义的 Java 或 JSON 串行器/解串器,从而将 POJO 映射到字符串。通过这种方式,就可以把任意 Java bean 保存为 STRING
,或者将其放置在 LIST
、SET
中,等等。
性能和可扩展性
对于 Redis,人们注意到的第一个特点可能就是它的速度极快。根据记录的大小和连接的数量,性能基准会有所不同,但延迟通常为单数位毫秒。在大多数用例中,Redis 每秒最多可支持 50000 次请求。如果用户使用较高端的硬件,处理能力更可高达每秒 700000 次请求(但这一数值可能会被网卡带宽扼制)。
作为一种内存数据库,Redis 的存储容量有限; AWS EC2 中的最大实例为 r3.8xlarge,内存 244 GB。由于数据结构的索引和性能都经过优化,Redis 消耗的内存比所存储的数据量大得多。切分 Redis 有助于克服这一局限性。要把内存数据备份到硬盘上,可以在预定作业中进行时间点转储,也可以根据需要运行 dump
命令。
用 Spring 进行远程数据缓存
要想提升应用程序服务器的性能,数据缓存可能是性价比最高的办法了。利用 Spring 的缓存抽象注释(@Cacheable
、@CachePut
、@CacheEvict
、@Caching
和 @CacheConfig
)可以毫不费力地启用数据缓存。在 Spring 配置下,用户还可以把 Ehcache、Memcached 或 Redis 当作基本缓存服务器。
Encache 通常被配置成本地缓存层,具有嵌套结构,在应用的 JVM 上运行。 Memcached 和 Redis 都能作为独立的缓存服务器运行。要想把 Redis 缓存集成到基于 Spring 的应用中,需要使用 Spring Data Redis 的 RedisTemplate 和 RedisCacheManager。
在 Redis 中访问已缓存的对象,耗时通常不到数毫秒,和关系数据库查询相比,这大幅提升了应用程序的性能。
延迟和收益
亚马逊公司在很大程度上依赖缓存服务器来最大程度地减少其零售网站的延迟,该公司甚至曾经发布过一份案例分析,其中记录了延迟和收益之间的关系。
本地缓存与远程缓存
在没有网络开销的系统中,本地缓存快于远程缓存。本地缓存的缺点是,同一个对象的多个拷贝在服务器集群中的各个不同节点之中会同步得更快。正因如此,本地缓存仅适用于静态数据,例如可容忍短期滞后和不一致现象的系统级设置。如果为易挥发的业务数据(例如用户数据和交易数据)使用本地缓存,很有可能会以运行应用程序服务器的单个实例而告终。
远程缓存服务器就没有这一局限性。在同一个键的情况下,可保证缓存服务器上的对象只有一个拷贝。只要用户让缓存中的对象及其数据库值彼此保持同步,就无需处理过期数据。
列表 1 给出了一个 Spring 数据缓存的示例。
列表 1:在基于 Spring 的应用中启用缓存
@Cacheable(value="User_CACHE_REPOSITORY", key = "#id")
public User get(Long id) {
return em.find(User.class, id);
}
@Caching(put = {@CachePut(value="USER_CACHE_REPOSITORY", key = "#user.getId()")})
public User update(User user) {
em.merge(user);
return user;
}
@Caching(evict = {@CacheEvict(value="USER_CACHE_REPOSITORY", key = "#user.getId()")}) public void delete(User user) {
em.remove(user);
}
@Caching(evict = {@CacheEvict(value="USER_CACHE_REPOSITORY", key = "#user.getId()")}) public void evictCache(User user) {
}
这里的读取操作被 Spring 的 @Cacheable
注释围绕,作为 AOP 幕僚而执行。Spring 中的存活时间设置也规定了这些对象可在缓存中停留的时间。调用 get()
方法后,Spring 就会试着先从远程缓存读取和返回对象。如果未找到对象,Spring 会执行方法主体,然后将数据库结果放在远程缓存中,之后再返回结果。
但如果另一个过程(例如另一个服务器节点)甚至同一个 JVM 中的另一个线程在数据库中更新了同一个对象,又会怎样呢?如果只运用 @Cacheable
注释,你可能会从远程缓存服务器收到过期拷贝。
为了防止发生这种情况,可以给所有数据库更新操作添加一个 @CachePut
注释。每次调用这些方法时,返回值就会替换掉远程缓存中原先的对象。在数据库读取和写入上都更新缓存,可以让缓存服务器和后台数据之间的记录保持同步。
容错
听起来简直完美,对吧?事实当然不是这样。利用列表 1 中的配置,负载较低时可能不会遇到任何问题,但随着服务器集群上的负载逐渐增加,远程缓存上就会出现过期数据。要做好准备应对服务器节点争用甚至更糟的情况。即使成功写入数据库,最后也可能会因为网络故障而使得缓存服务器 PUT
以失败告终。另外,NoSQL 通常不支持在关系数据库中存在完整事务语义,因为这会导致部分提交。为了让代码容错,可以考虑给数据模型增加版本号,实现乐观锁。
在收到 OptimisticLockingFailureException
或 CurrentModificationException
(具体取决于持久性解决方案)时,可以调用带有 @CacheEvict
注释的方法,从缓存中清除过期拷贝,然后重试同一个操作:
列表 2:解决缓存中的过期对象
try{
User user = userDao.get(id); // user fetched in cache server
userDao.update(user, oldname, newname);
}catch(ConcurrentModificationException ex) { // cached user object may be stale
userDao.evictCache(user);
user = userDao.get(id); // refresh user object
userDao.update(user, oldname, newname); // retry the same operation. Note it may still throw legitimate ConcurrentModificationException.}
结合 Elasticache 使用 Redis
Amazon Elasticache 是一款内存缓存服务,可结合 Memcached 或 Redis 作为缓存服务器使用。虽然 Elasticache 不在本文介绍范围内,但笔者还是想给各位开发人员介绍一个结合 Redis 使用 Elasticache 的技巧。对于大多数 Redis 参数,使用其默认值并无大碍,但
tcp-keepalive
和timeout
的默认 Redis 设置并不会移除已无效的客户连接,最后还会耗尽缓存服务器上的套接口。结合 Elasticache 使用 Redis 时,务必每次都明确设置这两个值。
在本文的第二部分,将介绍 Redis 的6大用例,敬请期待。
本文系 OneAPM 工程师编译整理。OneAPM 能为您提供端到端的 Java 应用性能解决方案,我们支持所有常见的 Java 框架及应用服务器,助您快速发现系统瓶颈,定位异常根本原因。分钟级部署,即刻体验,Java 监控从来没有如此简单。想阅读更多技术文章,请访问 OneAPM 官方技术博客。
本文转自 OneAPM 官方博客
Spring Data Redis 让 NoSQL 快如闪电 (1)的更多相关文章
- Spring Data Redis 让 NoSQL 快如闪电(2)
[编者按]本文作者为 Xinyu Liu,文章的第一部分重点概述了 Redis 方方面面的特性.在第二部分,将介绍详细的用例.文章系国内 ITOM 管理平台 OneAPM 编译呈现. 把 Redis ...
- Spring Data Redis示例
说明 关于Redis:一个基于键值对存储的NoSQL内存数据库,可存储复杂的数据结构,如List, Set, Hashes. 关于Spring Data Redis:简称SDR, 能让Spring应用 ...
- Spring Data Redis学习
本文是从为知笔记上复制过来的,懒得调整格式了,为知笔记版本是带格式的,内容也比这里全.点这里 为知笔记版本 Spring Data Redis 学习 Version 1.8.4.Release 前言 ...
- Spring Data Redis整体介绍 (一)
为什么使用Spring Data Redis 首先Spring Data Redis 是Spring 框架提供的用于操作Redis的客户端. Spring框架是一个全栈Java程序框架,通过DI.AO ...
- spring data redis jackson 配置,工具类
spring data redis 序列化有jdk .jackson.string 等几种类型,自带的jackson不熟悉怎么使用,于是用string类型序列化,把对象先用工具类转成string,代码 ...
- spring data redis RedisTemplate操作redis相关用法
http://blog.mkfree.com/posts/515835d1975a30cc561dc35d spring-data-redis API:http://docs.spring.io/sp ...
- spring mvc Spring Data Redis RedisTemplate [转]
http://maven.springframework.org/release/org/springframework/data/spring-data-redis/(spring-data包下载) ...
- Spring Data Redis简介以及项目Demo,RedisTemplate和 Serializer详解
一.概念简介: Redis: Redis是一款开源的Key-Value数据库,运行在内存中,由ANSI C编写,详细的信息在Redis官网上面有,因为我自己通过google等各种渠道去学习Redis, ...
- Spring Data Redis—Pub/Sub(附Web项目源码)
一.发布和订阅机制 当一个客户端通过 PUBLISH 命令向订阅者发送信息的时候,我们称这个客户端为发布者(publisher). 而当一个客户端使用 SUBSCRIBE 或者 PSUBSCRIBE ...
随机推荐
- [译]ASP.NET Core中使用MediatR实现命令和中介者模式
作者:依乐祝 原文地址:https://www.cnblogs.com/yilezhu/p/9866068.html 在本文中,我将解释命令模式,以及如何利用基于命令模式的第三方库来实现它们,以及如何 ...
- vue 关于vue.set的学习笔记
vue新手小白,在看vue文档的时候 发现vue关于 数组,对象值改变的与 ng有那么点不同. 官方表示 由于 JavaScript 的限制,Vue 不能检测以下变动的数组: 当你利用索引直接设置一个 ...
- Storm是什么
Why use Storm? Apache Storm是一个免费的开源的分布式实时计算系统.Storm使得可靠的实时处理无边界的数据量变得很容易,就如同Hadoop做批处理那样.Storm很简单,可以 ...
- mysql 开发基础系列19 触发器
触发器是与表有关的数据库对象,触发器只能是针对创建的永久表,而不能是临时表. 1.1 创建触发器 -- 语法: CREATE TRIGGER trigger_name trigger_time tri ...
- vue-06-过度和动画
1, css过度与动画 需要使用 v-if, v-show 来进行 1), 过度类名 v-enter: 进入时触发 v-enter-active: 执行过程中 v-enter-to: 停止时进行 v- ...
- vue-router 基本使用
参考原文:http://www.cnblogs.com/SamWeb/p/6610733.html 此链接讲了大部分常用的路由配置及使用,下星期总结
- Python中编码和字符串
编码和字符串 编码 在学习回顾中总结一下ASCII编码.Unicode编码和utf-8编码. 计算机中只能处理数字,我们若要处理文本的话就要将文件转换为数字.所以,这就涉及该怎样转换的问题,也就是编码 ...
- C# 获取当前屏幕的宽高和位置
上一篇博客<C# 获取当前屏幕DPI>,介绍了如何获取当前屏幕的DPI设置 本章主要介绍如何获取当前窗口所在屏幕的信息 当前屏幕信息 如果当前是单屏幕,可以直接获取主屏幕 var prim ...
- Android Studio 管理所有程序退出
import android.app.Activity; import java.util.ArrayList; import java.util.List; public class fa { pu ...
- Oracle+mybatis实现对数据的简单增删改查
第一步:--创建一个表空间:名字叫 mybatis,建在D盘下的date文件夹下: 第二步:创建用户,名字叫 lisi ,密码为 :123456 第三步:给用户授权: 第四步:我们在 li ...