vlambda博客
学习文章列表

面试遇到数据库优化问你还不知道怎么答吗?全都在这里了

1、如何设计一个高并发的系统

  • 数据库的优化,包括合理的事务隔离级别、SQL语句优化、索引的优化

  • 使用缓存,尽量减少数据库 IO

  • 分布式数据库、分布式缓存

  • 服务器的负载均衡


2、实践中如何优化MySQL

四条从效果上第一条影响最大,后面越来越小。

  • SQL语句及索引的优化

  • 数据库表结构的优化

  • 系统配置的优化

  • 硬件的优化


3、锁的优化策略

  • 读写分离

  • 分段加锁

  • 减少锁持有的时间

  • 多个线程尽量以相同的顺序去获取资源

这些都不是绝对原则,都要根据情况,比如不能将锁的粒度过于细化,不然可能会出现线程的加锁和释放次数过多,反而效率不如一次加一把大锁。这部分跟面试官谈了很久


4、innodb的读写参数优化

(1)、读取参数

global buffer 以及 local buffer;

Global buffer:

Innodb_buffer_pool_size

innodb_log_buffer_size

innodb_additional_mem_pool_size

local buffer(下面的都是 server 层的 session 变量,不是 innodb 的):

Read_buffer_size

Join_buffer_size

Sort_buffer_size

Key_buffer_size

Binlog_cache_size

(2)、写入参数

innodb_flush_log_at_trx_commit innodb_buffer_pool_size

insert_buffer_size

innodb_double_write

innodb_write_io_thread

innodb_flush_method

(3)、与IO相关的参数

innodb_write_io_threads = 8 innodb_read_io_threads = 8 innodb_thread_concurrency = 0

Sync_binlog

Innodb_flush_log_at_trx_commit

Innodb_lru_scan_depth

Innodb_io_capacity

Innodb_io_capacity_max

innodb_log_buffer_size

innodb_max_dirty_pages_pct

(4)、缓存参数以及缓存的使用场景

query cache/query_cache_type 并不是所有表都适合使用query cache。造成query cache失效的原因主要是相应的table发生了变更 第一个:读操作多的话看看比例,简单来说,如果是用户清单表,或者说是数据比例比较固定,比如说商品列表,是可以打开的,前提是这些库比较集中,数据库中的实务比较小。第二个:我们“行骗”的时候,比如说我们竞标的时候压测,把query cache打开,还是能收到qps激增的效果,当然前提示前端的连接池什么的都配置一样。大部分情况下如果写入的居多,访问量并不多,那么就不要打开,例如社交网站的,10%的人产生内容,其余的90%都在消费,打开还是效果很好的,但是你如果是qq消息,或者聊天,那就很要命。第三个:小网站或者没有高并发的无所谓,高并发下,会看到 很多 qcache 锁 等待,所以一般高并发下,不建议打开query cache


5、你是如何监控你们的数据库的?你们的慢日志都是怎么查询的?

监控的工具有很多,例如zabbix,lepus,我这里用的是lepus


6、你是否做过主从一致性校验,如果有,怎么做的,如果没有,你打算怎么做?

主从一致性校验有多种工具 例如checksum、mysqldiff、pt-table-checksum等


7、表中有大字段X(例如:text类型),且字段X不会经常更新,以读为主,请问您是选择拆成子表,还是继续放一起?写出您这样选择的理由

拆带来的问题:连接消耗 + 存储拆分空间;不拆可能带来的问题:查询性能;如果能容忍拆分带来的空间问题,拆的话最好和经常要查询的表的主键在物理结构上放置在一起(分区) 顺序IO,减少连接消耗,最后这是一个文本列再加上一个全文索引来尽量抵消连接消耗 如果能容忍不拆分带来的查询性能损失的话:上面的方案在某个极致条件下肯定会出现问题,那么不拆就是最好的选择


8、优化数据库的方法

  • · 选取最适用的字段属性,尽可能减少定义字段宽度,尽量把字段设置NOTNULL,例如'省份'、'性别'最好适用ENUM

  • · 使用连接(JOIN)来代替子查询

  • · 适用联合(UNION)来代替手动创建的临时表

  • · 事务处理

  • · 锁定表、优化事务处理

  • · 适用外键,优化锁定表

  • · 建立索引

  • · 优化查询语句


9、说说对SQL语句优化有哪些方法?(选择几条)

(1)Where子句中:where表之间的连接必须写在其他Where条件之前,那些可以过滤掉最大数量记录的条件必须写在Where子句的末尾.HAVING最后。

(2)用EXISTS替代IN、用NOT EXISTS替代NOT IN。

(3) 避免在索引列上使用计算

(4)避免在索引列上使用IS NULL和IS NOT NULL

(5)对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。  

(6)应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描

(7)应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描


10、对于大流量的网站,您采用什么样的方法来解决各页面访问量统计问题?

a. 确认服务器是否能支撑当前访问量。

b. 优化数据库访问。

c. 禁止外部访问链接(盗链), 比如图片盗链。

d. 控制文件下载。

e. 使用不同主机分流。

f. 使用浏览统计软件,了解访问量,有针对性的进行优化。


11、MySQL如何优化DISTINCT?

DISTINCT在所有列上转换为GROUP BY,并与ORDER BY子句结合使用。

SELECT DISTINCT t1.a FROM t1,t2 where t1.a=t2.a;


12、如何进行SQL优化?(关于后边的解释同学们可以进行理解,到时根据自己的理解把大体意思说出来即可)

(1)选择正确的存储引擎

以 MySQL为例,包括有两个存储引擎 MyISAM 和 InnoDB,每个引擎都有利有弊。

MyISAM 适合于一些需要大量查询的应用,但其对于有大量的操作并不是很好。甚至你只是需要update一个字段,整个表都会被锁起来,而别的进程,就算是读进程都无法操作直到读操作完成。另外,MyISAM 对于 SELECT COUNT(*) 这类的计算是超快无比的。

InnoDB 的趋势会是一个非常复杂的存储引擎,对于一些小的应用,它会比 MyISAM 还慢。但是它支持“行锁” ,于是在写操作比较多的时候,会更优秀。并且,他还支持更多的高级应用,比如:事务。

(2)优化字段的数据类型

记住一个原则,越小的列会越快。如果一个表只会有几列罢了(比如说字典表,配置表),那么,我们就没有理由使用 INT 来做主键,使用 MEDIUMINT, SMALLINT 或是更小的 TINYINT 会更经济一些。如果你不需要记录时间,使用 DATE 要比 DATETIME 好得多。当然,你也需要留够足够的扩展空间。

(3)为搜索字段添加索引

索引并不一定就是给主键或是唯一的字段。如果在你的表中,有某个字段你总要会经常用来做搜索,那么最好是为其建立索引,除非你要搜索的字段是大的文本字段,那应该建立全文索引。

(4)避免使用Select *从数据库里读出越多的数据,那么查询就会变得越慢。并且,如果你的数据库服务器和WEB服务器是两台独立的服务器的话,这还会增加网络传输的负载。即使你要查询数据表的所有字段,也尽量不要用*通配符,善用内置提供的字段排除定义也许能给带来更多的便利。

(5)使用 ENUM 而不是 VARCHAR

ENUM 类型是非常快和紧凑的。在实际上,其保存的是 TINYINT,但其外表上显示为字符串。这样一来,用这个字段来做一些选项列表变得相当的完美。例如,性别、民族、部门和状态之类的这些字段的取值是有限而且固定的,那么,你应该使用 ENUM 而不是 VARCHAR。

(6)尽可能的使用 NOT NULL

除非你有一个很特别的原因去使用 NULL 值,你应该总是让你的字段保持 NOT NULL。NULL其实需要额外的空间,并且,在你进行比较的时候,你的程序会更复杂。当然,这里并不是说你就不能使用NULL了,现实情况是很复杂的,依然会有些情况下,你需要使用NULL值。

(7)固定长度的表会更快

如果表中的所有字段都是“固定长度”的,整个表会被认为是 “static” 或 “fixed-length”。例如,表中没有如下类型的字段:VARCHAR,TEXT,BLOB。只要你包括了其中一个这些字段,那么这个表就不是“固定长度静态表”了,这样,MySQL 引擎会用另一种方法来处理。

固定长度的表会提高性能,因为MySQL搜寻得会更快一些,因为这些固定的长度是很容易计算下一个数据的偏移量的,所以读取的自然也会很快。而如果字段不是定长的,那么,每一次要找下一条的话,需要程序找到主键。

并且,固定长度的表也更容易被缓存和重建。不过,唯一的副作用是,固定长度的字段会浪费一些空间,因为定长的字段无论你用不用,他都是要分配那么多的空间。



4 、对于有丰富的 SQL 优化的经验

首先问 mysql 中 sql 优化的思路,如果能准备说出来, ok,那么我就开始问 explain的各种参数了,重点是 select_type, type, possible_key, ref,rows,extra 等参数的各种 值的含义,如果他都回答正确了,那么我再问 file sort 的含义以及什么时候会出现这个分析结果,如果这里他也回答对了,那么我就准备问 profile 分析了,如果这里他也答对了,那么我就会再问一个问 题,

那是曾经 tx 问我的让我郁闷不已的问题,一个 6 亿的表 a,一个 3 亿的表 b,通过外间 tid 关联,你如何最快的查询出满足条件的第 50000 到第 50200中的这 200 条数据记录。Explain 在上面的题目中有了,这里就不说了。如何最快的查询出满足条件的第 50000 到第 50200 中的这 200 条数据记录?这个我想不出来!


5、对于有丰富的数据库设计经验

这个对于数据库设计我真的没有太多的经验,我也就只能问问最基础的, mysql 中varchar(60) 60 是啥含义, int(30)中 30 是啥含义?如果他都回答对了,那么我就问 mysql中为什么要这么设计呢?

如果他还回答对了,我就继续问 int(20)存储的数字的上限和下限是多少?这个问题难倒了全部的 mysql dba 的应聘者,不得不佩服提出这个问题的金总的睿智啊,因为这个问题回答正确了,

那么他确实认认真真地研究了 mysql 的设计中关于字段类型的细节。至 于丰富的设计数据库的经验,不用着急,这不我上面还有更加厉害的 dba吗,他会搞明白的,那就跟我无关了。varchar(60)的 60 表示最多可以存储 60 个字符。int(30)的 30 表示客户端显示这个字段的宽度。为何这么设计?说不清楚,请大家补充 。int(20)的上限为 2147483647(signed)或者4294967295(unsigned)。


6 、关于 mysql 参数优化的经验

首先问他它们线上 mysql 数据库是怎么安装的,如果说是 rpm 安装的,那么我就直接问调优参数了,如果是源码安装的,那么我就要问编译中的一些参数了,比如 my.cnf 以及存储引擎以及字符类型等等。然后从以下几个方面问起:( 1) mysql 有哪些 global 内存参数,有哪些 local 内存参数。Global: innodb_buffer_pool_size/innodb_additional_mem_pool_size/innodb_log_buffer_size/key_buffer_size/query_cache_size/table_open_cache/table_definition_cache/thread_cache_size Local: read_buffer_size/read_rnd_buffer_size/sort_buffer_size/join_buffer_size/binlog_cache_size/tmp_table_size/thread_stack/bulk_insert_buffer_size

( 2) mysql 的写入参数需要调整哪些?重要的几个写参数的几个值得含义以及适用场景, 比如 innodb_flush_log_at_trx_commit 等。(求补充) sync_binlog 设置为 1,保证 binlog 的安全性。innodb_flush_log_at_trx_commit:0:事务提交时不将 redo log buffer 写入磁盘(仅每秒进行 master thread 刷新,安全 性能差,性能最好) 1:事务提交时将 redo log buffer 写入磁盘(安全性最好,性能最差, 推荐生产使用) 2:事务提交时仅将 redo log buffer 写入操作系统缓存(安全性和性能都居中,当 mysql宕机但是操作系统不宕机则不丢数据,如果操作系统宕机,最多丢一秒数据) innodb_io_capacity/innodb_io_capacity_max:看磁盘的性能来定。如果是 HDD 可以设置为 200-几百不等。如果是 SSD,推荐为 4000 左右。innodb_io_capacity_max 更大一些。innodb_flush_method 设置为 O_DIRECT。

( 3) 读取的话,那几个全局的 pool 的值的设置,以及几个 local 的 buffer 的设置。Global: innodb_buffer_pool_size:设置为可用内存的 50%-60%左右,如果不够,再慢慢上调。innodb_additional_mem_pool_size:采用默认值 8M 即可。innodb_log_buffer_size:默认值 8M 即可。key_buffer_size:myisam 表需要的 buffer size,选择基本都用 innodb,所以采用默认的 8M 即可。Local: join_buffer_size:当 sql 有 BNL 和 BKA 的时候,需要用的 buffer_size(plain index scans, range index scans 的时候可能也会用到)。默认为 256k,建议设置为 16M-32M。read_rnd_buffer_size:当使用 mrr 时,用到的 buffer。默认为 256k,建议设置为16-32M。read_buffer_size:当顺序扫描一个 myisam 表,需要用到这个 buffer。或者用来决定memory table 的大小。或者所有的 engine 类型做如下操作:order by 的时候用 temporaryfile、 SELECT INTO … OUTFILE 'filename' 、 For caching results of nested queries。默认为 128K,建议为 16M。sort_buffer_size:sql 语句用来进行 sort 操作(order by,group by)的 buffer。如果 buffer 不够,则需要建立 temporary file。如果在 show global status 中发现有大量的 Sort_merge_passes 值,则需要考虑调大 sort_buffer_size。默认为 256k,建议设置为 16-32M。binlog_cache_size:表示每个 session 中存放 transaction 的 binlog 的 cache size。默认 32K。一般使用默认值即可。如果有大事务,可以考虑调大。thread_stack:每个进程都需要有,默认为 256K,使用默认值即可。

( 4) 还有就是著名的 query cache 了,以及 query cache 的适用场景了,这里有一个陷阱, 就是高并发的情况下,比如双十一的时候, query cache 开还是不开,开了怎么保证高并发,不开又有何别的考虑?建议关闭,上了性能反而更差。


2、100万 分区 一亿 分区 区别…

一亿数据量可以考虑的分区 分表

分区合理的话查询性能不差

3、800万数据索引

查询几条数据性能提升很大

查询大量数据 全表比索引要好 索引要回表


物理优化: 

1). Oracle的运行环境(网络,硬件等) 

2). 使用合适的优化器 

3). 合理配置oracle实例参数 

4). 建立合适的索引(减少IO) 

5). 将索引数据和表数据分开在不同的表空间上(降低IO冲突) 

6). 建立表分区,将数据分别存储在不同的分区上(以空间换取时间,减少IO) 


逻辑上优化: 

1). 可以对表进行逻辑分割,如中国移动用户表,可以根据手机尾数分成10个表,这样对性能会有一定的作用 

2). Sql语句使用占位符语句,并且开发时候必须按照规定编写sql语句(如全部大写,全部小写等)oracle解析语句后会放置到共享池中 

如:select * from Emp where name=?  这个语句只会在共享池中有一条,而如果是字符串的话,那就根据不同名字存在不同的语句,所以占位符效率较好 

3). 数据库不仅仅是一个存储数据的地方,同样是一个编程的地方,一些耗时的操作,可以通过存储过程等在用户较少的情况下执行,从而错开系统使用的高峰时间,提高数据库性能 

4). 尽量不使用*号,如select * from Emp,因为要转化为具体的列名是要查数据字典,比较耗时 

5). 选择有效的表名 

对于多表连接查询,可能oracle的优化器并不会优化到这个程度, oracle 中多表查询是根据FROM字句从右到左的数据进行的,那么最好右边的表(也就是基础表)选择数据较少的表,这样排序更快速,如果有link表(多对多中间表),那么将link表放最右边作为基础表,在默认情况下oracle会自动优化,但是如果配置了优化器的情况下,可能不会自动优化,所以平时最好能按照这个方式编写sql 

6). Where字句 规则 

Oracle 中Where字句是从右往左处理的,表之间的连接写在其他条件之前,能过滤掉非常多的数据的条件,放在where的末尾, 另外!=符号比较的列将不使用索引,列经过了计算(如变大写等)不会使用索引(需要建立起函数), is null、is not null等优化器不会使用索引 

7). 使用Exits Not Exits 替代 In  Not in 

8). 合理使用事务,合理设置事务隔离性 

数据库的数据操作比较消耗数据库资源的,尽量使用批量处理,以降低事务操作次数


20. MySQL数据库作发布系统的存储,一天五万条以上的增量,预计运维三年,怎么优化?

a. 设计良好的数据库结构,允许部分数据冗余,尽量避免join查询,提高效率。

b. 选择合适的表字段数据类型和存储引擎,适当的添加索引。

c. mysql库主从读写分离。

d. 找规律分表,减少单表中的数据量提高查询速度。

e。添加缓存机制,比如memcached,apc等。

f. 不经常改动的页面,生成静态页面。

g. 书写高效率的SQL。比如 SELECT * FROM TABEL 改为 SELECT field_1, field_2, field_3 FROM TABLE.


21、ACID 是什么。

A,atomic,原子性,要么都提交,要么都失败,不能一部分成功,一部分失败。 

C,consistent,一致性,事物开始及结束后,数据的一致性约束没有被破坏 

I,isolation,隔离性,并发事物间相互不影响,互不干扰。 

D,durability,持久性,已经提交的事物对数据库所做的更新必须永久保存。即便发生崩溃,也不能被回滚或数据丢失。


22、Mysql 怎么优化 table scan(全表扫描) 的。

避免在where子句中对字段进行is null判断 

应尽量避免在where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。 

避免在where 子句中使用or 来连接条件 

in 和not in 也要慎用 

Like查询(非左开头) 

使用NUM=@num参数这种 

where 子句中对字段进行表达式操作num/2=XX 

在where子句中对字段进行函数操作


23、一条sql执行过长的时间,你如何优化,从哪些方面?

1、查看sql是否涉及多表的联表或者子查询,如果有,看是否能进行业务拆分,相关字段冗余或者合并成临时表(业务和算法的优化)

2、涉及链表的查询,是否能进行分表查询,单表查询之后的结果进行字段整合

3、如果以上两种都不能操作,非要链表查询,那么考虑对相对应的查询条件做索引。加快查询速度

4、针对数量大的表进行历史表分离(如交易流水表)

5、数据库主从分离,读写分离,降低读写针对同一表同时的压力,至于主从同步,mysql有自带的binlog实现 主从同步

6、explain分析sql语句,查看执行计划,分析索引是否用上,分析扫描行数等等

7、查看mysql执行日志,看看是否有其他方面的问题

个人理解:从根本上来说,查询慢是占用mysql内存比较多,那么可以从这方面去酌手考虑


24、高并发下,如何做到安全的修改同一行数据。

使用悲观锁 悲观锁本质是当前只有一个线程执行操作,结束了唤醒其他线程进行处理。 

也可以缓存队列中锁定主键。


25、乐观锁和悲观锁是什么,INNODB 的行级锁有哪 2 种,解释其含义。

乐观锁是设定每次修改都不会冲突,只在提交的时候去检查,悲观锁设定每次修改都会冲突,持有排他锁。 

行级锁分为共享锁和排他锁两种 共享锁又称读锁 排他锁又称写锁 

26、SQL 优化的一般步骤是什么,怎么看执行计划,如何理解其中各个字段的含义。

查看慢日志(show [session|gobal] status ),定位慢查询,查看慢查询执行计划 根据执行计划确认优化方案 

Explain sql 

select_type:表示select类型。常见的取值有SIMPLE(简单表,即不使用连接或者子查询)、PRIMARY(主查询,即外层的查询)、UNION(union中的第二个或者后面的查询语句)、SUBQUERY(子查询中的第一个SELECT)等。 

talbe:输出结果集的表。 

type:表的连接类型。性能由高到底:system(表中仅有一行)、const(表中最多有一个匹配行)、eq_ref、ref、ref_null、index_merge、unique_subquery、index_subquery、range、idnex等 

possible_keys:查询时,可能使用的索引 

key:实际使用的索引 

key_len:索引字段的长度 

rows:扫描行的数量 

Extra:执行情况的说明和描述 

27、SQL性能优化

关注我

只分享干货

喜欢请点个赞