一、基于Durid分析Mysql执行性能
配置Durid打开SQL执行监控
package com.xdclass.mysql.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class DruidConfig {
@Value("${spring.datasource.druid.username}")
private String userName;
@Value("${spring.datasource.druid.password}")
private String password;
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource(){
return new DruidDataSource();
}
@Bean
public ServletRegistrationBean druidServlet() {
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
servletRegistrationBean.setServlet(new StatViewServlet());
servletRegistrationBean.addUrlMappings("/druid/*");
Map<String, String> initParameters = new HashMap<>(3);
// 用户名
initParameters.put("loginUsername", userName);
// 密码
initParameters.put("loginPassword", password);
// 禁用HTML页面上的“Reset All”功能
initParameters.put("resetEnable", "false");
servletRegistrationBean.setInitParameters(initParameters);
return servletRegistrationBean;
}
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new WebStatFilter());
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return filterRegistrationBean;
}
}
打开网站,就可以来到Druid界面
http://localhost:8080/druid/index.html

多的不谈,直接安排代码
CustomUserMapper.java
void batchInsert(@Param("userList") List<User> userList); CustomUserMapper.xml
<select id="batchInsert" parameterType="java.util.List">
insert into user ( `name`, company
)
values
<foreach collection="userList" item="item" separator=",">
( #{item.name,jdbcType=VARCHAR}, #{item.company,jdbcType=VARCHAR})
</foreach>
</select> UserController
@RequestMapping(value = "/batchInsert", method = RequestMethod.GET)
public RestItemResult<Integer> batchInsert() {
int num = 1000;
List<com.xdclass.mysql.model.User> userList = new ArrayList<>();
for (int i = 0; i < num; i++) {
com.xdclass.mysql.model.User u = new com.xdclass.mysql.model.User();
u.setName("daniel"+i);
u.setCompany("xdclass"+i);
userList.add(u);
}
List<List<com.xdclass.mysql.model.User>> partition = Lists.partition(userList, 200);
partition.forEach(users -> customUserMapper.batchInsert(users));
RestItemResult<Integer> integerRestItemResult = new RestItemResult<>();
integerRestItemResult.setItem(100);
return integerRestItemResult;
} Java之Lists.Partition使用与坑 将list集合按指定长度进行切分,返回新的List<List List<List> lists=Lists.partition(numList,3); Lists.Partition 在项目中遇到的坑总结: 项目中使用 Lists.Partition 批量处理数据,但是最近内存一直 OutOffMemory,GC无法回收。 后来我们对使用过的集合手动 clear,没有测试直接就上线了。尴尬的是内存回收了,但是跑出来的数据出问题了。 最后自己单元测试发现是 List<List> resultPartition = Lists.partition(list, 500) 之后再对 list 进行 clear 操作,resultPartition也会被清空。 回来谷歌才发现它最终会调用 list.subList。subList 执行结果是获取 ArrayList 的一部分,返回的是 ArrayList 的部分视图。 对子集合的操作会反映到原集合, 对原集合的操作也会影响子集合。
import com.google.common.collect.Lists;
import org.junit.Test;
import java.util.List;
public class testList {
@Test
public void test(){
List<Integer> numList = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8);
List<List<Integer>> lists=Lists.partition(numList,3);
System.out.println(lists); //[[1, 2, 3], [4, 5, 6], [7, 8]]
}
}
二、Mysql数据库索引性能分析实战
在数据量1000w的表里面对比是否采用索引数据对照组,查询使用索引前面性能对比








- 不适合用索引的场景
- 不适合键值较少的列(重复数据较多的列)
- 索引无法存储null值
- 索引失效的经典场景
- 如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因),要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引
- 对于多列索引,不是使用的第一部分,则不会使用索引
- like查询以%开头
- 类型强转情况下不走索引,如varchar类型用long传参来查
- hash索引不支持范围检索
三、高级面试题之谈谈你对MYSQL优化的理解
谈谈你对SQL优化的理解
- SQL优化可分为两个部分,一个是设计阶段,另一个是查询阶段
- 设计阶段运用到的优化
- 使用适当的数据库列类型和大小
- 尽量从设计上采用单表查询解决业务问题
- 在适当字段加入索引,能用唯一索引用唯一索引
- 查询阶段涉及的优化
- 尽可能不用
select *:让优化器无法完成索引覆盖扫描这类优化,而且还会增加额外的I/O、内存和CPU的消耗 - 慎用
join操作:单张表查询可以减少锁的竞争,更容易应对业务的发展,方便对数据库进行拆分 - 慎用子查询和临时表:未带索引的字段上的
group by操作,UNION查询,部分order by操作,例如distinct函数和order by一起使用且distinct和order by同一个字段 - 尽量不适用limit,部分场景可改用bewteen and
- 尽可能不用
四、高级面试题之Mysql千万级别数据如何做分页?
- 后端开发中为了防止一次性加载太多数据导致内存、磁盘IO都开销过大,经常需要分页展示,这个时候就需要用到MySQL的LIMIT关键字。但你以为LIMIT分页就万事大吉了么,LIMIT在数据量大的时候极可能造成深度分页问题




- 通过explain分析深度分页查询问题 explain select * from user where age>10 and age<90000000 order by age desc limit 8000000,10000;
-
执行计划Extra列可能出现的值及含义:
- Using where:表示优化器需要通过索引回表查询数据。
- Using index:即覆盖索引,表示直接访问索引就足够获取到所需要的数据,不需要通过索引回表,通常是通过将待查询字段建立联合索引实现。
- Using index condition:在5.6版本后加入的新特性,即大名鼎鼎的索引下推,是MySQL关于
减少回表次数的重大优化。 - Using filesort:文件排序,这个一般在ORDER BY时候,数据量过大,MySQL会将所有数据召回内存中排序,比较消耗资源。
- 解决方案
- 通过主键索引优化
- 在查询条件中带上主键索引 explain select * from user where id>{maxId} age>10 and age<90000000 order by age desc limit 8000000,10000;
- Elastic Search搜索引擎优化(倒排索引)
- 实际上类似于淘宝这样的电商基本上都是把所有商品放进ES搜索引擎里的(那么海量的数据,放进MySQL是不可能的,放进Redis也不现实)。但即使用了ES搜索引擎,也还是有可能发生深度分页的问题的,这时怎么办呢?答案是通过游标scroll
- 通过主键索引优化
本文作者为DBC,转载请注明。