数据库缓存mybatis,redis
简介
处理并发问题的重点不在于你的设计是怎样的,而在于你要评估你的并发,并在并发范围内处理。
你预估你的并发是多少,然后测试r+m是否支持。缓存的目的是为了应对普通对象数据库的读写限制,依托与nosql的优势进行高速读写。redis本身也有并发瓶颈。所以你要把读写和并发区分开来处理。只读业务是不是可以用mysql分布做只读库和只读表,进行读写分离+库分布,
拆库拆表不能搞定再考虑上多级缓存
任何设计,你外面套一层,就多一倍的维护成本,缓存不是万金油。这里多级缓存主要指的是二级缓存技术,也就是依托nosql的高速读取优势。
mybatis
如果底层ORM框架用的是mybatis框架,就可以用mybatis自带的缓存机制,mybatis自带一级缓存和二级缓存。
一级缓存指的是sqlsession缓存,是session级别缓存
二级缓存指的是mapper级别的缓存,同一个namespace公用这一个缓存,所以对SqlSession是共享的,这里的namespace指的是xml里的命名空间
mybatis默认开启一级缓存,二级缓存默认不开启,需要手动开启。一级缓存
mybatis的一级缓存是SqlSession级别的缓存,在操作数据库的时候需要先创建SqlSession会话对象,在对象中有一个HashMap用于存储缓存数据,
此HashMap是当前会话对象私有的,别的SqlSession会话对象无法访问。
具体流程:1.第一次执行select完毕会将查到的数据写入SqlSession内的HashMap中缓存起来
2.第二次执行select会从缓存中查数据,如果select相同切传参数一样,那么就能从缓存中返回数据,不用去数据库了,从而提高了效率
注意事项:
1.如果SqlSession执行了DML操作(insert、update、delete),并commit了,那么mybatis就会清空当前SqlSession缓存中的所有缓存数据,
这样可以保证缓存中的存的数据永远和数据库中一致,避免出现脏读2.当一个SqlSession结束后那么他里面的一级缓存也就不存在了,mybatis默认是开启一级缓存,不需要配置
3.mybatis的缓存是基于[namespace:sql语句:参数]来进行缓存的,意思就是,SqlSession的HashMap存储缓存数据时,
是使用[namespace:sql:参数]作为key,查询返回的语句作为value保存的。
例如:-1242243203:1146242777:winclpt.bean.userMapper.getUser:0:2147483647:select * from user where id=?:19二级缓存
二级缓存是mapper级别的缓存,也就是同一个namespace的mappe.xml,当多个SqlSession使用同一个Mapper操作数据库的时候,得到的数据会缓存在同一个二级缓存区域二级缓存默认是没有开启的,需要在xml配置setting全局参数中配置开启二级缓存
<settings>
<setting name="cacheEnabled" value="true"/><!--开启mybatis缓存 默认是false:关闭二级缓存--><settings>然后再具体的xml里配置
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>当前mapper下所有语句开启二级缓存
这里配置了一个LRU缓存,并每隔60秒刷新,最大存储512个对象,而却返回的对象是只读的
若想禁用当前select语句的二级缓存,添加useCache="false"修改如下:
<select id="getCountByName" parameterType="java.util.Map" resultType="INTEGER" statementType="CALLABLE" useCache="false">
具体流程:
1.当一个sqlseesion执行了一次select后,在关闭此session的时候,会将查询结果缓存到二级缓存
2.当另一个sqlsession执行select时,首先会在他自己的一级缓存中找,如果没找到,就回去二级缓存中找,
找到了就返回,就不用去数据库了,从而减少了数据库压力提高了性能注意事项:
1.如果SqlSession执行了DML操作(insert、update、delete),并commit了,那么mybatis就会清空当前mapper缓存中的所有缓存数据,这样可以保证缓存中的存的数据永远和数据库中一致,避免出现脏读
2.mybatis的缓存是基于[namespace:sql语句:参数]来进行缓存的,意思就是,SqlSession的HashMap存储缓存数据时,是使用[namespace:sql:参数]作为key,查询返回的语句作为value保存的。例如:-1242243203:1146242777:winclpt.bean.userMapper.getUser:0:2147483647:select * from user where id=?:19
redis
这里我实现了spring集成Jedis,并通过解析key实现自定义缓存失效时间。官网可以下载linux下的redis,没有windows下的,但micro开发小组在github上维护了windows环境下的redis,
https://github.com/MSOpenTech/redis/releasespom.xml引入依赖
<!--redis start-->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.6.0.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.7.3</version>
</dependency>
<!--redis end-->在xml配置文件里配置各种bean
<!--JedisPool线程池-->
<!--redis是单线程的,为了满足java多线程需求,jedis引入线程池的概念-->
<bean id="jedisPool" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="100"/>
<property name="maxTotal" value="700"/>
<property name="maxWaitMillis" value="1000"/>
<property name="testOnBorrow" value="false"/>
</bean> <!--创建redis连接工厂-->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.hostname}"/>
<property name="port" value="${redis.port}"/>
<property name="password" value="${redis.password}"/>
<property name="database" value="${redis.database}"/>
<property name="poolConfig" ref="jedisPool"/>
</bean> <!--配置redistempLate -->
<bean id="redistempLate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory"/>
<!--<property name="defaultSerializer">-->
<!--<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>-->
<!--</property>-->
</bean>
<!--配置缓存配置信息-->
<!--这是用的spring的cacheManager,不支持缓存有效期动态配置-->
<!--<bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">-->
<!--<!–配置redis模板–>-->
<!--<constructor-arg name="redisOperations" ref="redistempLate"/>-->
<!--<!–默认缓存失效时间–>-->
<!--<property name="defaultExpiration" value="120"/>-->
<!--<!–是否使用前缀–>-->
<!--<property name="usePrefix" value="false"/>--> <!--</bean>--> <!--手动实现的redisCacheManager,支持缓存有效期动态配置,可在@Cacheable中的value属性添加有效时间-->
<bean id="myRedisCacheManager" class="com.djkj.demo.common.MyRedisCacheManager">
<!--配置redis模板-->
<constructor-arg name="redisOperations" ref="redistempLate"/>
<!--默认缓存失效时间-->
<property name="defaultExpiration" value="120"/>
<!--是否使用前缀-->
<property name="usePrefix" value="false"/>
<!--缓存名字和有效期的分隔符-->
<property name="separator" value="#"/> <!-- 多个缓存有效期,一般的单个工程可以省略此项 -->
<!--<property name="expires">-->
<!--<map>-->
<!--<entry key="caiya_a" value="1800"/>-->
<!--</map>-->
<!--</property>--> </bean> <!--配置RedisCacheConfig,redis缓存启动类-->
<bean id="redisCacheConfig" class="com.djkj.demo.common.RedisCacheConfig">
<constructor-arg ref="jedisConnectionFactory"/>
<constructor-arg ref="redistempLate"/>
<constructor-arg ref="myRedisCacheManager"/>
</bean>这里jedisPool的数据源地址根据实际情况设置,reis默认port为6379,如果你的redis设置了校验密码则这里需要否则不用,database设置的0,redis默认生成15个数据库,0表示采用的是第一个。
最主要的是redisCacheConfig缓存启动类和myRedisCacheManager缓存管理类,启动类里设置了缓存的key的生成策略,管理类主要实现了自定义有效期
redisCacheConfigpackage com.djkj.demo.common; import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate; import java.lang.reflect.Method; @Configuration
@EnableCaching
public class RedisCacheConfig extends CachingConfigurerSupport { private volatile JedisConnectionFactory jedisConnectionFactory;
private volatile RedisTemplate<String,String> redisTemplate;
private volatile RedisCacheManager redisCacheManager; public RedisCacheConfig(){
super();
} public RedisCacheConfig(JedisConnectionFactory jedisConnectionFactory
,RedisTemplate<String,String> redisTemplate, RedisCacheManager redisCacheManager){
this.jedisConnectionFactory = jedisConnectionFactory;
this.redisTemplate = redisTemplate;
this.redisCacheManager = redisCacheManager;
} public JedisConnectionFactory getJedisConnectionFactory() {
return jedisConnectionFactory;
} public RedisTemplate<String, String> getRedisTemplate() {
return redisTemplate;
} public RedisCacheManager getRedisCacheManager() {
return redisCacheManager;
}
//主键生成策略
@Bean
public KeyGenerator customKeyGenerator(){
return new KeyGenerator() {
@Override
public Object generate(Object o, Method method, Object... objects) {
StringBuilder stringBuilder=new StringBuilder();
stringBuilder.append(o.getClass().getName());
stringBuilder.append(method.getName());
for (Object obj : objects) {
stringBuilder.append(obj.toString().hashCode());
}
System.out.println(stringBuilder.toString());
return stringBuilder.toString();
}
};
}
}myRedisCacheManager
package com.djkj.demo.common; import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.cache.Cache;
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCachePrefix;
import org.springframework.data.redis.core.RedisOperations; import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.util.Collection;
import java.util.Collections;
import java.util.regex.Pattern; public class MyRedisCacheManager extends RedisCacheManager { private static final Logger logger = Logger.getLogger(MyRedisCacheManager.class); private static final ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName("JavaScript"); private static final Pattern pattern = Pattern.compile("[+\\-*/%]"); private String defaultCacheName; private String separator = "#"; public MyRedisCacheManager(RedisOperations redisOperations) {
this(redisOperations, Collections.<String>emptyList());
} public MyRedisCacheManager(RedisOperations redisOperations, Collection<String> cacheNames) {
super(redisOperations, cacheNames);
} @Override
public Cache getCache(String name) {
String cacheName="";
String expirationStr="";
Long expiration=0L;
String[] params = name.split(getSeparator());
if(params.length>=1){
cacheName = params[0];
}
if(params.length>=2){
expirationStr = params[1];
} if(StringUtils.isBlank(cacheName)){
cacheName = defaultCacheName;
}
Cache cache = (RedisCache) super.getCache(cacheName);
if (cache == null) {
return null;
} if(StringUtils.isNotEmpty(expirationStr)){
try {
expiration = Double.valueOf(expirationStr).longValue();
}catch (Exception e){
logger.error("expiration exchange failed!");
}
} if (expiration==null || expiration == 0L) {
logger.warn("Default expiration time will be used for cache '{}' because cannot parse '{}', cacheName : " + cacheName + ", name : " + name);
return cache;
} return new RedisCache(cacheName, (isUsePrefix() ? getCachePrefix().prefix(cacheName) : null), getRedisOperations(), expiration);
} public String getSeparator() {
return separator;
} public void setSeparator(String separator) {
this.separator = separator;
} private Long getExpiration(final String name, final int separatorIndex) {
Long expiration = null;
String expirationAsString = name.substring(separatorIndex + 1);
try {
// calculate expiration, support arithmetic expressions.
if(pattern.matcher(expirationAsString).find()){
expiration = (long) Double.parseDouble(scriptEngine.eval(expirationAsString).toString());
}else{
expiration = Long.parseLong(expirationAsString);
}
} catch (NumberFormatException ex) {
logger.error(String.format("Cannnot separate expiration time from cache: '%s'", name), ex);
} catch (ScriptException e) {
logger.error(String.format("Cannnot separate expiration time from cache: '%s'", name), e);
} return expiration;
} @Override
public void setUsePrefix(boolean usePrefix) {
super.setUsePrefix(usePrefix);
} @Override
public void setCachePrefix(RedisCachePrefix cachePrefix) {
super.setCachePrefix(cachePrefix);
} public void setDefaultCacheName(String defaultCacheName) {
this.defaultCacheName = defaultCacheName;
}
}根据符号 # 将缓存名切割,前面的作为缓存名,后面的作为有效期
具体应用方法
在service层通过@Cacheable注解使用缓存
//设置缓存有效时间和刷新时间
@Cacheable(value="testPojoCache" ,keyGenerator = "customKeyGenerator")
@Override
public List<TestPojo> query(TestPojo bean) {
List<TestPojo> testList = testPojoMapper.query(bean);
if(testList.size()>0){
for(TestPojo pojo:testList){
pojo.setTime(sdf.format(new Date()));
pojo.setAttr1(bean.getAttr1());
}
}
return testList;
}testPojoCache表示缓存name,30表示有效期,keyGenerator表示key的生成策略,用的是在配置类里通过@Bean配置的bean。
进阶说明
spring通过集成jedis的方式有别于直接通过获取Jedis对象的set方法,通过jedis.set(key,value)只会在redis服务器产生一条数据,而用注解会产生两条数据,以上面的例子为例,首先会生成一条key为testPojoCache的缓存数据,value内容是一个集合,放的是通过生存策略生成的值,再以这生成的值为key生成一条缓存数据,value为这个service方法返回的对象数据。当你根据不同的条件参数调用service的方法,都会在testPojoCache里存放键值,然后再创建缓存。那怎么调用缓存呢?在调用service方法前,redis会根据生成策略生成的值到testPojoCache里去找,看有没有。若果有,根据键值获取缓存;如果没有就查询数据库。
数据库缓存mybatis,redis的更多相关文章
- 构建高性能数据库缓存之redis主从复制
一.什么是redis主从复制? 主从复制,当用户往Master端写入数据时,通过Redis Sync机制将数据文件发送至Slave,Slave也会执行相同的操作确保数据一致:且实现Redis的主从复制 ...
- 构建高性能数据库缓存之redis(二)
一.概述 在构建高性能数据库缓存之redis(一)这篇文档中,阐述了Redis数据库(key/value)的特点.功能以及简单的配置过程,相信阅读过这篇文档的朋友,对Redis数据库会有一点的了解,此 ...
- 构建高性能数据库缓存之Redis(一)
一.Redis概述 1.1 什么是redis Redis是一个开源的用ANSI C编写.支持网络.基于内存.亦可持久化的日志型.Key-Value数据库,根据DB-Engines.com站点月度排行的 ...
- springboot+mybatis+redis实现分布式缓存
大家都知道springboot项目都是微服务部署,A服务和B服务分开部署,那么它们如何更新或者获取共有模块的缓存数据,或者给A服务做分布式集群负载,如何确保A服务的所有集群都能同步公共模块的缓存数据, ...
- Spring Boot + Mybatis + Redis二级缓存开发指南
Spring Boot + Mybatis + Redis二级缓存开发指南 背景 Spring-Boot因其提供了各种开箱即用的插件,使得它成为了当今最为主流的Java Web开发框架之一.Mybat ...
- 用Redis作Mysql数据库缓存
使用redis作mysql数据库缓存时,需要考虑两个问题: 1.确定用何种数据结构存储来自Mysql的数据; 2.在确定数据结构之后,用什么标识作为该数据结构的键. 直观上看,Mysql中的数据都是按 ...
- redis(二)--用Redis作MySQL数据库缓存
用Redis作MySQL数据库缓存,必须解决2个问题.首先,应该确定用何种数据结构存储来自mysql的数据:在确定数据结构之后,还要考虑用什么标识作为该数据结构的键. 直观上看,Mysql中的数据都是 ...
- MyBatis功能点一应用:二级缓存整合redis
Mybatis提供了默认的cache实现PerpetualCache,那为什么还要整合第三方的框架redis?因为Mybatis提供的cache实现为单机版,无法实现分布式存储(即本机存储的数据,其他 ...
- 如何玩转最新的项目的搭配springmvc+mybatis+Redis+Nginx+tomcat+mysql
上一次完成nginx+tomcat组合搭配,今天我们就说说,这几个软件在项目中充当的角色: 要想完成这几个软件的组合,我们必须知道和熟悉应用这个框架, 一: Nginx:在项目中大多数作为反向代理服务 ...
随机推荐
- Netty实现高性能IOT服务器(Groza)之手撕MQTT协议篇上
前言 诞生及优势 MQTT由Andy Stanford-Clark(IBM)和Arlen Nipper(Eurotech,现为Cirrus Link)于1999年开发,用于监测穿越沙漠的石油管道.目标 ...
- r.js合并实践 --项目中用到require.js做生产时模块开发 r.js build.js配置详解
本文所用源代码已上传,需要的朋友自行下载:点我下载 第一步: 全局安装 npm install -g requirejs 第二步: 1.以下例子主要实现功能, 1)引用jq库获取dom中元素文本, ...
- 在ASP.NET Core中使用EPPlus导入出Excel文件
这篇文章说明了如何使用EPPlus在ASP.NET Core中导入和导出.xls/.xlsx文件(Excel).在考虑使用.NET处理excel时,我们总是寻找第三方库或组件.使用Open Offic ...
- 【转】asp.net基础-HttpModule
HttpModule是向实现类提供模块初始化和处置事件.当一个HTTP请求到达HttpModule时,整个ASP.NET Framework系统还并没有对这个HTTP请求做任何处理,也就是说此时对于H ...
- 神经网络——反向传播BP算法公式推导
在神经网络中,当我们的网络层数越来越多时,网络的参数也越来越多,如何对网络进行训练呢?我们需要一种强大的算法,无论网络多复杂,都能够有效的进行训练.在众多的训练算法中,其中最杰出的代表就是BP算法,它 ...
- 『OGG 02』Win7 配置 Oracle GoldenGate Adapter Java 踩坑指南
上一文章 <__Win7 配置OGG(Oracle GoldenGate).docx>定下了 两个目标: 目标1: 给安装的Oracle_11g 创建 两个用户 admin 和 root ...
- 衡量GDP,哪种夜间灯光数据更靠谱?
<新科学家>杂志报道,随着经济发展,一些国家通常会新修道路,扩展居民区,这两项措施都会使从太空中看到的灯光强度增加.不少学者利用夜间灯光数据与国内生产总值统计数据进行比较,发现从太空中看到 ...
- 一起学Android之Storage
概述 在Android开发中,存储(Storage)的方式根据具体的需求不同而不同,例如数据对应用程序是私有的还是其他应用程序(和用户)可以访问的,以及保存数据需要多大的空间. 存储分类 主要的存储方 ...
- 第三周LINUX学习笔记
周期性任务丶find 文件查找:find命令 locate :在数据库中查找,非实时查找,精确度不高,查找速度快,模糊查找 /tmp/passwad/a.textfind:实时查找:速度慢 ,精确 ...
- 安装windows 10到固态硬盘实践记录
1.前提 由于之前一直用的机械硬盘,电脑用了几年是越来越慢,所以打算买个SSD,装个新系统,其他的机械硬盘都当从盘用 2.准备工作 SSD :256G 3星的 WIN10正版光盘一张 外置光驱一个 3 ...