原创作品,出自 “晓风残月xj” 博客,欢迎转载,转载时请务必注明出处(http://blog.csdn.net/xiaofengcanyuexj)。

由于各种原因,可能存在诸多不足,欢迎斧正!

随着业务的快速发展,表不断增多,结构不断扩大,数据量也在慢慢积累,最近数据库DB压力较大,有些慢查询日志 。个人总结原因如下:表数量和结构日渐复杂,单表数量增多,联表查询等大sql ,应用层大事务等等。

1、表数量和结构日渐复杂
     随着业务的扩张,这种规模带来的问题最通用的解决方案应该是分库分表。分库有2种:水平拆分和垂直拆分。其中,垂直分库是将不同业务含义的表拆分到不同的数据库中;水平分库是将相同表中不同业务含义的数据行拆分到不同的数据库中。同理,分表也有垂直拆分和水平拆分的区别。其中,垂直分表是将表的不同字段拆分成不同的表;水平分表是将表的不同数据行拆分到不同表中,比如历史数据切分,冷热数据分离等等。

2、单表数量增多

可以通过分表分库解决,不过相对工作量大点。简单直接粗暴的解决方案是将过期或者无效或者相对不重要的数据分离或者清理掉,可以定期同步到相对隔绝的历史表中,也可以应用层定时任务直接删除数据(物理删除和逻辑删除都可以),不过最好是定时任务或者手工同步到历史表中备份,以防以后用到。

3、联表查询等大sql

拆分联表查询等大sql语句,可以减少大sql造成的读写锁竞争、利用DB本身的查询缓存、方便应用层预加载设置缓存,但拆分sql分多次查询潜在的问题是占用网络资源较多,可能应用机器会占用很大内存保存临时查询结果等。当然,一般内网网速和机器内存都不会是性能瓶颈,所以可以拆分大sql是不错的选择。将直接mapper访问抽象成dao层,对service透明。

4、应用层大事务

事务的特性是老生常谈的ACID,即原子性,一致性,隔离性,持久性。一般web框架对于事务的支持应该都是比较友好的,比如spring mvc只需要配置+注解@Transactional。事务容易加剧锁竞争,即便有些数据库,比如mysql已经采用MVCC(高版本并发控制),但是在某些关键共享变量上也会加锁,会比较影响性能。

上面都是快速发展的数据库DB容易遇到的问题和潜在的解决方案,当然比较肤浅,考虑到接触到的系统出现慢查询时从库slave并未完全使用,更多的是容灾备份,所以假期尝试将部分读请求打到slave上,做到读写分离,master写数据+少量读请求,slave只读数据。一般数据库主从同步都不可避免存在一定的时延,考虑到有些主流程对时效性要求比较强的读请求,应该读主库,这类问题的解决方案也有很多,主要有如下几种:

1、半同步复制,即等待写请求主库同步待从库后,写请求才返回,这样读从库就能读到最新的数据,mysql。

2、强制读主库,对于特定的读请求,直接强制读主库。

3、数据库中间件,所有读写请求都走中间件,写请求到主库,记录所有路由到写库的key,在经验主从同步时间窗口内,有读请求访问中间件,就把这个key上的读请求路由到主库。

4、缓存写key法,数据库中间件方案较重,较轻的是应用层使用缓存,当写请求发生的时候同时向缓存中插入一条带有过期失效时间的记录,当读请求到达时先读缓存key,如果没有值,则直接读从库;否则强制读主库。

。。。

各种方法 的优缺点及详细介绍,可以参看DB主从一致性架构优化4种方法

笔者实际的问题是尝试读写分离,下面记录一下读写分离方法,欢迎斧正。DB读写分离理论上方案比较多,如中间件转发、应用层分离,数据库驱动等等,各种方法的优缺点如下:

1、中间件转发

通过mysql中间件做主从集群,Mysql Proxy、Amoeba、Atlas等中间件貌似都能符合需求。

优点:对应用透明

缺点:需要代理,增加网络等性能开销

2、应用层分离

应用层路由数据源实现读写分离,通过AOP或者注解来动态选择数据源

优点:无需中间件,策略可选,可用来负载均衡

缺点:耦合度高

3、数据库驱动

Replication Driver或者使用Replication协议头,Replication Driver根据connection的readonly属性路由数据源,数据库驱动

优点:对应用透明,无需中间件

缺点:需要DB支持Replication协议

Java web可以分为两个层次:JDBC层的封装,ORM框架层的实现。

结合实际情况较轻的方案是应用层分离,比如配置不同mapper访问主从数据源、AOP切片路由读写请求等。

1、不同mapper访问主从数据源,一般来说,对于不同的数据源使用不同包的mapper访问,就可以读写分离,但是几乎相同代码要拷贝多份

2、AOP切片路由读写请求,利用反射,动态决定运行时的数据源,实现读写分离

编写动态数据源路由 DynamicDataSource类继承AbstractRoutingDataSource,并实现determineCurrentLookupKey()方法,determineCurrentLookupKey()是spring数据库连接池在建立连接时都要调用的。然后通过在mapper或者dao层切片,感觉最好在dao层切片,所有事务都定义在dao层;这样对于事务的处理也比较方便,因为要保持相同事务内的不同连接到同一个数据源(当然是master了),可以单独对事务注解@transactional进行切片。

import com.google.common.collect.ImmutableMap;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import javax.sql.DataSource;/*** 实现描述:切换动态数据源** @author jin.xu* @version v1.0.0* @see* @since 16-10-6 下午3:40*/
@Component
public class DynamicDataSource extends AbstractRoutingDataSource {private static final ThreadLocal<String> DAAL_HOLDER = new ThreadLocal<String>();@Resourceprivate DataSource masterDataSource;@Resourceprivate DataSource slaveDataSource;@Overridepublic void afterPropertiesSet() {setTargetDataSources(ImmutableMap.<Object, Object> of("master", masterDataSource, "slave", slaveDataSource));setDefaultTargetDataSource(spiderDataSource);super.afterPropertiesSet();}@Overrideprotected Object determineCurrentLookupKey() {return DAAL_HOLDER.get();}public static void switchToMaster() {DAAL_HOLDER.set("master");}public static void switchToSlave() {DAAL_HOLDER.set("slave");}public static void reset() {DAAL_HOLDER.remove();}}
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;import java.util.Arrays;/*** 实现描述:DB读写分离切片** @author jin.xu* @version v1.0.0* @see* @since 16-10-6 下午4:53*/
@Aspect
@Component
public class DBAspect {/*** 内部api监控** @param joinPoint* @return* @throws Throwable*/@Around("execution (* com.csdn.jinxu.dal.dao..*.*(..))")public Object dbLog(ProceedingJoinPoint joinPoint) throws Throwable {Long startTime = 0l;Long endTime = 0l;Object result = null;String method =null;try {startTime = System.currentTimeMillis();method = joinPoint.getSignature().getName();if(isSwitchToSlave(method)){DynamicDataSource.switchToSlave();}else{DynamicDataSource.switchToMaster();}result = joinPoint.proceed();endTime=System.currentTimeMillis();} catch (Exception e) {throw e;} finally {try {String request = Arrays.toString(joinPoint.getArgs());String response =result.toString();} catch (Exception e) {}DynamicDataSource.reset();}return result;}/***扩展配置粗略*/private boolean isSwitchToSlave(String method){boolean isBFlag=false;if(null!=method&&(method.startsWith("find")||method.startsWith("count")||method.startsWith("get"))){isBFlag=true;}return isBFlag;}}

路漫漫其修远兮,很多时候感觉想法比较幼稚。首先东西比较简单,其次工作也比较忙,还好周末可以抽时间处理这个。由于相关知识积累有限,欢迎大家提意见斧正,在此表示感谢!后续版本会持续更新…

数据库读写分离方法浅析相关推荐

  1. mysql读写分离java配置方法_springboot配置数据库读写分离

    为什么要做数据库读写分离 大多数互联网业务,往往读多写少,这时候,数据库的读会首先称为数据库的瓶颈,这时,如果我们希望能够线性的提升数据库的读性能,消除读写锁冲突从而提升数据库的写性能,那么就可以使用 ...

  2. 数据库读写分离,主从同步实现方法

    前言 众所周知,随着用户量的增多,数据库操作往往会成为一个系统的瓶颈所在,而且一般的系统"读"的压力远远大于"写",因此我们可以通过实现数据库的读写分离来提高系 ...

  3. 解决数据库读写分离(转)

    如何配置mysql数据库的主从? 单机配置mysql主从:http://my.oschina.net/god/blog/496 常见的解决数据库读写分离有两种方案 1.应用层 http://neore ...

  4. .net core发布 正在发现数据上下文_使用EF Core实现数据库读写分离

    以下文章来源于朝夕Net社区 ,作者Eleven 朝夕Net社区 朝气.丰富.活跃的.Net社区,朝夕教育携百万粉丝共同打造!有技术,有感悟,有新闻,有照片,有故事,还有梦想! [精选转载]| 作者/ ...

  5. 简单好用!利用Spring AOP技术10分钟实现一个数据库读写分离方案

    前言 最近我们的APP在线用户越来越多,接口的响应速度也是越来越慢,经过运维排查发现是由于并发查询太多导致的数据库压力比较大,架构师经过调研给出了数据库读写分离的解决方案,为了快速解决问题,我们最终采 ...

  6. PG SQL数据库读写分离的思路

    PGSQL可以设置一台主多个SLAVE,这样我们需要一个办法能在程序里做到读写分离,查询了互联网,找到了下面的解决思路,请周枫在后继开发中测试下面的思路方案: 在应用层通过spring特性解决数据库读 ...

  7. java spring mysql配置_java相关:mysql+spring+mybatis实现数据库读写分离的代码配置

    java相关:mysql+spring+mybatis实现数据库读写分离的代码配置 发布于 2020-4-4| 复制链接 分享一篇关于关于mysql+spring+mybatis实现数据库读写分离的代 ...

  8. mybatis获取mysql源数据类型_spring集成mybatis实现mysql数据库读写分离

    前言 在网站的用户达到一定规模后,数据库因为负载压力过高而成为网站的瓶颈.幸运的是目前大部分的主流数据库都提供主从热备功能,通过配置两台数据库主从关系,可以将一台数据库的数据更新同步到另一台服务器上. ...

  9. Spring 实现数据库读写分离

    Spring 实现数据库读写分离 现在大型的电子商务系统,在数据库层面大都采用读写分离技术,就是一个Master数据库,多个Slave数据库.Master库负责数据更新和实时数据查询,Slave库当然 ...

最新文章

  1. 第九课 特殊权限set_uid、stick_bit,软链接,硬链接
  2. 使用 Termux 查看连接到手机热点的ip地址
  3. python信息管理系统统计功能_用Python实现职工信息管理系统
  4. python rgb led控件_Raspberry Pi-用树莓派实现RGB LED的颜色控制——Python版本-电路城论坛 - 电子工程师学习交流园地...
  5. linux没有root密码xshell,LINUX终端免密登陆(以Xshell为例)
  6. eclipse中Lombok注解无效
  7. 【Druid IO】Druid的imply方式安装
  8. SQL Drop数据库– MySQL,PostgreSQL,SQL Server
  9. nginx+ffmpeg搭建rtmp转播rtsp流的flash服务器
  10. 配置gem5-gpu docker版
  11. 关于解决keil4和mdk共存后51不能使用go to definition Of 'XXXXXX'问题
  12. Java上传文件格式判断
  13. 再见也是永别——告别即将消逝的雅虎邮箱
  14. EXCEL批量删除行
  15. 简易图解N合1集成安装光盘制作教程
  16. 2021年饶州中学高考成绩查询,鄱阳饶州中学2019高考成绩喜报、一本二本上线人数情况...
  17. 如果在遨游浏览器里设置Bing(必应)搜索为默认搜索
  18. 押宝线下渠道 能否拯救陷入芯片困境的魅族?
  19. 计算机怎么通电启动,怎么让电脑一通电就立即开机?
  20. vue-chartjs

热门文章

  1. 【LeetCode844.比较含退格的字符串】——双指针法
  2. 大学生如何进行个人理财
  3. 【OQPSK】基于MATLAB/FPGA的OQPSK实现
  4. 失联8分钟后现神秘“第7次握手” 澳两次发现疑似黑匣子信号 搜救区域最深5000米
  5. Python实现炸金花小游戏
  6. 算法 64式 7、搜索算法整理_第2部分_16到30题
  7. yts1999 T2 容斥原理
  8. CKA考试习题:存储管理-普通卷、PV、PVC
  9. 转:解救西西弗斯- 模型驱动架构(MDA,Model Driven Architecture)浅述
  10. google autoaugment