系统性能优化(二)

 性能优化 - 索引

https://i-blog.csdnimg.cn/blog_migrate/62cb57987f032d8ed94d77a3175f9b66.png ​

当根据操作人进行查询时, 查询的效率很低,耗时比较长。原因就是因为在创建数据库表结构时,并没有针对于 操作人 字段建立索引。

CREATE INDEX idx_user_method_return_cost ON operation_log(operate_user,operate_method,return_class,cost_time);

 

同上 , 为了查询效率高,我们也需要对 操作方法、返回值类型、操作耗时 等字段进行创建索引,以提高查询效 率。

CREATE INDEX idx_optlog_method_return_cost ON

operation_log(operate_method,return_class,cost_time);

CREATE INDEX idx_optlog_return_cost ON operation_log(return_class,cost_time);

CREATE INDEX idx_optlog_cost ON operation_log(cost_time);

 

性能优化 - 排序

    在查询数据时,如果业务需求中需要我们对结果内容进行了排序处理 , 这个时候,我们还需要对排序的字段建立适当 的索引, 来提高排序的效率 。

性能优化 - 读写分离

    在Mysql主从复制的基础上,可以使用读写分离来降低单台Mysql节点的压力,从而来提高访问效率,读写分离的 架构如下:

https://i-blog.csdnimg.cn/blog_migrate/eca45bf7e486b0c7fe9d5985648f6c5b.png ​

对于读写分离的实现,可以通过Spring AOP 来进行动态的切换数据源,进行操作 :

实现方式

db.properties

jdbc.write.driver=com.mysql.jdbc.Driver

jdbc.write.url=jdbc:mysql://192.168.142.128:3306/mysql_demo

jdbc.write.username=root

jdbc.write.password=itcast

jdbc.read.driver=com.mysql.jdbc.Driver

jdbc.read.url=jdbc:mysql://192.168.142.129:3306/mysql_demo

jdbc.read.username=root

jdbc.read.password=itcast

 

applicationContext-datasource.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:aop="http://www.springframework.org/schema/aop"

xmlns:tx="http://www.springframework.org/schema/tx"

xmlns:context="http://www.springframework.org/schema/context"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/tx

http://www.springframework.org/schema/tx/spring-tx.xsd

http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context.xsd">

<!-- 配置数据源 - Read -->

<bean id="readDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"

destroy-method="close" lazy-init="true">

<property name="driverClass" value="${jdbc.read.driver}"></property>

<property name="jdbcUrl" value="${jdbc.read.url}"></property>

<property name="user" value="${jdbc.read.username}"></property>

<property name="password" value="${jdbc.read.password}"></property>

</bean>

<!-- 配置数据源 - Write -->

<bean id="writeDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"

destroy-method="close" lazy-init="true">

<property name="driverClass" value="${jdbc.write.driver}"></property>

<property name="jdbcUrl" value="${jdbc.write.url}"></property>

<property name="user" value="${jdbc.write.username}"></property>

<property name="password" value="${jdbc.write.password}"></property>

</bean>

<!-- 配置动态分配的读写 数据源 -->

<bean id="dataSource" class="cn.itcast.aop.datasource.ChooseDataSource" lazy[1]init="true">

<property name="targetDataSources">

<map key-type="java.lang.String" value-type="javax.sql.DataSource">

<entry key="write" value-ref="writeDataSource"/>

<entry key="read" value-ref="readDataSource"/>

</map>

</property>

<property name="defaultTargetDataSource" ref="writeDataSource"/>

<property name="methodType">

<map key-type="java.lang.String">

<entry key="read" value=",get,select,count,list,query,find"/>

<entry key="write"

value=",add,create,update,delete,remove,insert"/>

</map>

</property>

</bean>

</beans>

 

ChooseDataSource

public class ChooseDataSource extends AbstractRoutingDataSource {

public static Map<String, List<String>> METHOD_TYPE_MAP = new HashMap<String,

List<String>>();

/**

* 实现父类中的抽象方法,获取数据源名称

* @return

*/

protected Object determineCurrentLookupKey() {

return DataSourceHandler.getDataSource();

}

// 设置方法名前缀对应的数据源

public void setMethodType(Map<String, String> map) {

for (String key : map.keySet()) {

List<String> v = new ArrayList<String>();

String[] types = map.get(key).split(",");

for (String type : types) {

if (!StringUtils.isEmpty(type)) {

v.add(type);

}

}

METHOD_TYPE_MAP.put(key, v);

}

System.out.println("METHOD_TYPE_MAP : "+METHOD_TYPE_MAP);

}

}

 

DataSourceHandler

public class DataSourceHandler {

// 数据源名称

public static final ThreadLocal<String> holder = new ThreadLocal<String>();

/**

* 在项目启动的时候将配置的读、写数据源加到holder中

*/

public static void putDataSource(String datasource) {

holder.set(datasource);

}

/**

* 从holer中获取数据源字符串

*/

public static String getDataSource() {

return holder.get();

}

}

 

DataSourceAspect

@Aspect

@Component

@Order(-9999)

@EnableAspectJAutoProxy(proxyTargetClass = true)

public class DataSourceAspect {

protected Logger logger = LoggerFactory.getLogger(this.getClass());

/**

* 配置前置通知,使用在方法aspect()上注册的切入点

*/

@Before("execution(* cn.itcast.service.*.*(..))")

@Order(-9999)

public void before(JoinPoint point) {

String className = point.getTarget().getClass().getName();

String method = point.getSignature().getName();

logger.info(className + "." + method + "(" +

Arrays.asList(point.getArgs())+ ")");

try {

for (String key : ChooseDataSource.METHOD_TYPE_MAP.keySet()) {

for (String type : ChooseDataSource.METHOD_TYPE_MAP.get(key)) {

if (method.startsWith(type)) {

System.out.println("key : " + key);

DataSourceHandler.putDataSource(key);

break;

}

}

}

} catch (Exception e) {

e.printStackTrace();

}

}

}

 

通过 @Order(-9999) 注解来控制事务管理器, 与该通知类的加载顺序 , 需要让通知类 , 先加载 , 来判定使用哪个数据 源 .

验证

在主库和从库中,执行如下SQL语句,来查看是否读的时候, 从从库中读取 ; 写入操作的时候,是否写入到主 库。

show status like 'Innodb_rows_%' ;

 

https://i-blog.csdnimg.cn/blog_migrate/697eb11c504468eff3be8c89fdfb99f2.png ​

原理

https://i-blog.csdnimg.cn/blog_migrate/968d0f22484bf4a04e29ec5ef19fcf64.png ​

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值