[关闭]
@javazjm 2017-12-27T10:01:31.000000Z 字数 4981 阅读 1497

在此处输入标题

面试知识


1. 数据库优化方面的事情

2. 查找慢查询并定位慢查询

项目部署之前,在启动MySQL数据库时开启慢查询,并把执行慢的语句写到日志中,在运行一段时间后,通过查看日志找到慢查询语句。

使用explain来详细分析语句的执行过程。

3. 数据库优化之数据库表设计遵循范式

数据库表设计时需要遵循的方式

表的范式,首先符合1NF,才能满足2NF,进一步满足3NF。

4. 选择合适的数据库引擎

研发中,经常使用的存储引擎 myisam/ innodb/ memory

MyISAM 和 INNODB 的区别

  1. 事务安全:myisam 不支持事务而 innodb支持
  2. 添加和查询速度:myisam 不支持事务就不用考虑同步锁,查找和添加的速度快
  3. 支持全文索引: myisam支持 innodb不支持
  4. 锁机制: myisam 支持表锁,innodb支持行锁(事务)
  5. 外键: myisam 不支持外键,innodb 支持外键(通常不设置外键,通常在程序中保证数据一致)
特点 Myisam InnoDB BDB Memory Archive
存储限制 没有 64TB 没有 没有
批量插入速度 非常高
事务安全 支持 支持
锁机制 表锁 行锁 页锁 表锁 行锁
B树索引 支持 支持 支持 支持
哈希索引 支持 支持
全文索引 支持
集群索引 支持
数据缓存 支持 支持
索引缓存 支持 支持 支持
数据可压缩 支持 支持
空间使用 N/A 非常低
内存使用 中等
支持外键 支持

5. 选择合适的索引

索引(Index)是帮助 DBMS 高效获取数据的数据结构。

分类:普通索引|唯一索引|主键索引|全文索引

6. 使用索引的一些技巧

索引弊端

  1. 占用磁盘空间
  2. 对dml(插入、修改、删除)操作有影响,变慢。

使用场景

  1. 肯定在 where 条件经常使用,如果不做查询就没有意义
  2. 该字段的内容不是唯一的几个值(sex)
  3. 字段内容不是频繁变化

具体技巧

  1. 对于创建的多列索引(复合索引),不是使用的第一部分就不会使用索引
  1. alter table dept ad index my_index(dname,loc); -- dname左边的列,loc右边的列
  2. explain select * from dept where dname='aaa'\G -- 会使用到索引
  3. explain select * from dept where loc='aaa'\G -- 不会使用到索引
  1. 对于使用 like 的查询,查询如果是 '%aaa'不会使用索引,而’aaa%‘ 会使用索引
  1. explain select * from dept where dname like '%aaa'\G -- 不会使用到索引
  2. explain select * from dept where dname like 'aaa%'\G -- 会使用到索引

所以在 like 查询时,’关键字‘ 最前面不能使用 % 或者 _ 这样的字符,如果一定要前面有变化的值,则考虑使用 全文索引 -> sphinx。
3. 如果条件中有or,有条件没有使用索引,即使其中有条件带索引也不会使用,换言之,就是要求使用的所有字段,都必须单独使用时能使用索引。可以考虑用in 或 union 替换or。
4. 如果列类型是字符串,一定要在条件中将数据使用引号引用起来,否则不使用索引

  1. explain select * from dept where dname='1111';
  2. explain select * from dept where dname=1111; -- 数值自动转字符串
  3. explain select * from dept where dname=qqq; -- 报错

也就是说,如果列是字符串类型,无论是不是字符串,数字一定要用 ’‘ 把它括起来。
5. 如果 MySQL 估计使用全表扫描比使用索引快,则不使用索引。
表里面只有一条记录。

7. 数据库优化之分表

分表分为水平(按行)分表 和 垂直(按列)分表。

根据经验,MySQL 表数据一般达到百万级别,查询效率会很低,容易造成表锁,甚至堆积很多连接,直接挂掉,水平分表能够很大程度减少这些压力。(按行数据进行分类)

如果一张表中某个字段值非常多(成文本,二进制等),而且只有在很少的情况下回查询,这时候就可以把字段单独放到一个表,通过外键关联起来。

考试详情,一般我们只关注分数,不关注详情。

水平分表策略

  1. 按时间分表
    这种分表方式有一定的局限性,当数据有较强的时效性,如微博发送记录,微信消息记录等,这种数据很少有用户会查询几个月前的数据,如就可以按月分类。
  2. 按区间范围分表
    一般有严格的自增id需求上,如按照user_id水平分表。
    table_1 user_id 从 1~100w
    table_2 user_id 从 101w~200w
    table_3 user_id 从 201w~300w
  3. ☆hash分表☆
    一个原始目标的 ID 或者名称通过一定的 hash 算法计算出数据存储表的表名,然后访问相应的表。

8. 数据库的读写分离

一台数据库支持的最大并发数是有限的,如果用户并发访问太多,一台服务器满足不了要求,就可以集群处理,MySQL的集群处理技术最常用的就是读写分离。

主从同步

数据库最终会把数据持久化到磁盘,如果集群就必须保证每个数据库服务器的数据是一致的,能改变数据库数据的操作都往主数据库写,而其他数据库从主数据库上同步数据。

读写分离

使用负载均衡来实现写的操作都往主数据库去,而读的数据往从数据库。

9. 数据库优化之缓存

在持久层和数据库之前添加一个缓存层,如果用户访问的数据已经缓存起来时,在用户访问时直接从缓存中获取,不用访问数据库。而缓存是在内存级的,访问速度快。

作用

减少数据库服务器压力,减少访问时间,提高系统响应速度。

Java中常用的缓存有,hibernate 的二级缓存,但是该缓存不能完成分布式缓存。

可使用redis 作为中央缓存。

10. SQL语句优化小技巧

DDL优化

  1. 通过禁用索引来提高导入数据性能,该操作主要针对有数据的表,追加数据。
  1. -- 去除键
  2. alter table test DISABLE keys;
  3. -- 批量插入数据
  4. insert into test select * from test1;
  5. -- 恢复键
  6. alter table test ENABLE keys;
  1. 关闭唯一校验
  1. set unique_checks=0 -- 关闭
  2. set unique_checks=1 -- 开启
  1. 修改事务提交方式(导入)[变多次提交为一次]
  1. set autocommit=0 -- 关闭
  2. -- 批量插入
  3. set autocommit=1 -- 开启

DML 优化

变多次提交为一次

insert into test values(1,2);
insert into test values(1,3);
insert into test values(1,4);
-- 合并多条为一条
insert into test values(1,2),(1,3),(1,4);

DQL优化

  1. --
  2. explain
  3. select * from customer where customer_id not in (select DISTINCT customer_id from payment) ;
  4. -- 基于索引外链
  5. explain
  6. select * from customer c left join payment p on (c.customer_id=p.customer_id) where p.customer_id is null;
  1. select a.film_id,a.description from film a order by a.title limit 50,5;
  2. -- 优化
  3. select a.film_id,a.description from film a inner join (selet film_id from film order by title limit 50,5) b on a.film_id=b.film_id

11. JDBC批量插入几百万条数据

变多次提交为一次

  1. String JDBC_URL = "jdbc:mysql://localhost:3306/samp_db";
  2. String JDBC_USERNAME = "root";
  3. String JDBC_PASSWORD = "root";
  4. Connection connection = DriverManager.getConnection(JDBC_URL + "?useServerPrepStmts=false&rewriteBatchedStatements=true",JDBC_USERNAME,JDBC_PASSWORD);
  5. connection.setAutoCommit(false);
  6. PreparedStatement cmd = connection.prepareStatement("insert into test1 values(?,?)");
  7. for (int i = 0; i < 1000000; i++) {//100万条数据
  8. cmd.setInt(1, i);
  9. cmd.setString(2, "test");
  10. cmd.addBatch();
  11. if(i%1000==0){
  12. cmd.executeBatch();
  13. }
  14. }
  15. cmd.executeBatch();
  16. connection.commit();
  17. cmd.close();
  18. connection.close();

省出的时间相当可观,像这种操作能不使用代码就不要使用代码,可以使用存储过程代替。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注