>  기사  >  데이터 베이스  >  인터뷰 질문: 일상 업무에서 MySQL을 어떻게 최적화합니까?

인터뷰 질문: 일상 업무에서 MySQL을 어떻게 최적화합니까?

Java后端技术全栈
Java后端技术全栈앞으로
2023-08-17 16:26:10983검색

머리말

MySQL의 일반적인 최적화 방법은 다음과 같은 측면으로 구분됩니다:

SQL 최적화, 디자인 최적화, 하드웨어 최적화 등, 각 측면에는 여러 개의 작은 최적화가 포함되어 있습니다.

인터뷰 질문: 일상 업무에서 MySQL을 어떻게 최적화합니까?

아래에서 자세히 살펴보겠습니다

SQL 최적화

이 최적화 계획은 SQL 문 및 인덱스를 최적화하여 MySQL 데이터베이스의 운영 효율성을 향상시키는 것을 의미합니다.

페이징 최적화

예:

select * from table where type = 2 and level = 9 order by id asc limit 190289,10;

최적화 계획:

  • 지연된 연결

    먼저 where 조건을 통해 기본 키를 추출한 다음 테이블을 원본 데이터 테이블과 연결하고, 기본 키 ID 대신 원래 보조 인덱스를 통해 데이터 행을 추출합니다.

    예:

select a.* from table a, (select id from table where type = 2 and level = 9 order by id asc limit 190289,10 ) b where a.id = b.id
  • 북마크 방법

    직접 말하면 북마크 방법은 해당 기본 키 값을 찾는 것입니다. Limit의 첫 번째 매개변수, 그리고 이 기본 키 값을 기준으로 필터 및 제한으로 이동

    예:

select * from table where id > (select * from table where type = 2 and level = 9 order by id asc limit 190289, 1) limit 10;

인덱스 최적화

인덱스를 올바르게 사용하세요

假如我们没有添加索引,那么在查询时就会触发全表扫描,因此查询的数据就会很多,并且查询效率会很低,为了提高查询的性能,我们就需要给最常使用的查询字段上,添加相应的索引,这样才能提高查询的性能

建立覆盖索引

InnoDB使用辅助索引查询数据时会回表,但是如果索引的叶节点中已经包含要查询的字段,那它没有必要再回表查询了,这就叫覆盖索引

例如对于如下查询:

select name from test where city='上海'

我们将被查询的字段建立到联合索引中,这样查询结果就可以直接从索引中获取

alter table test add index idx_city_name (city, name);

在 MySQL 5.0 之前的版本尽量避免使用or查询

在 MySQL 5.0 之前的版本要尽量避免使用 or 查询,可以使用 union 或者子查询来替代,因为早期的 MySQL 版本使用 or 查询可能会导致索引失效,在 MySQL 5.0 之后的版本中引入了索引合并

索引合并简单来说就是把多条件查询,比如or或and查询对多个索引分别进行条件扫描,然后将它们各自的结果进行合并,因此就不会导致索引失效的问题了

如果从Explain执行计划的type列的值是index_merge可以看出MySQL使用索引合并的方式来执行对表的查询

避免在 where 查询条件中使用 != 或者 a8093152e673feb7aba1828c43532094 操作符

SQL中,不等于操作符会导致查询引擎放弃索引索引,引起全表扫描,即使比较的字段上有索引

解决方法:通过把不等于操作符改成or,可以使用索引,避免全表扫描

例如,把columna8093152e673feb7aba1828c43532094’aaa’,改成column>’aaa’ or column<’aaa’,就可以使用索引了

适当使用前缀索引

MySQL 是支持前缀索引的,也就是说我们可以定义字符串的一部分来作为索引

我们知道索引越长占用的磁盘空间就越大,那么在相同数据页中能放下的索引值也就越少,这就意味着搜索索引需要的查询时间也就越长,进而查询的效率就会降低,所以我们可以适当的选择使用前缀索引,以减少空间的占用和提高查询效率

比如,邮箱的后缀都是固定的“@xxx.com”,那么类似这种后面几位为固定值的字段就非常适合定义为前缀索引

alter table test add index index2(email(6));

使用前缀索引,定义好长度,就可以做到既节省空间,又不用额外增加太多的查询成本

需要注意的是,前缀索引也存在缺点,MySQL无法利用前缀索引做order by和group by 操作,也无法作为覆盖索引

查询具体的字段而非全部字段

要尽量避免使用select *,而是查询需要的字段,这样可以提升速度,以及减少网络传输的带宽压力

优化子查询

尽量使用 Join 语句来替代子查询,因为子查询是嵌套查询,而嵌套查询会新创建一张临时表,而临时表的创建与销毁会占用一定的系统资源以及花费一定的时间,同时对于返回结果集比较大的子查询,其对查询性能的影响更大

小表驱动大表

我们要尽量使用小表驱动大表的方式进行查询,也就是如果 B 表的数据小于 A 表的数据,那执行的顺序就是先查 B 表再查 A 表,具体查询语句如下:

select name from A where id in (select id from B);

不要在列上进行运算操作

不要在列字段上进行算术运算或其他表达式运算,否则可能会导致查询引擎无法正确使用索引,从而影响了查询的效率

select * from test where id + 1 = 50;
select * from test where month(updateTime) = 7;

一个很容易踩的坑:隐式类型转换:

select * from test where skuId=123456

skuId这个字段上有索引,但是explain的结果却显示这条语句会全表扫描

原因在于skuId的字符类型是varchar(32),比较值却是整型,故需要做类型转换

适当增加冗余字段

增加冗余字段可以减少大量的连表查询,因为多张表的连表查询性能很低,所有可以适当的增加冗余字段,以减少多张表的关联查询,这是以空间换时间的优化策略

正确使用联合索引

使用了 B+ 树的 MySQL 数据库引擎,比如 InnoDB 引擎,在每次查询复合字段时是从左往右匹配数据的,因此在创建联合索引的时候需要注意索引创建的顺序

例如,我们创建了一个联合索引是idx(name,age,sex),那么当我们使用,姓名+年龄+性别、姓名+年龄、姓名等这种最左前缀查询条件时,就会触发联合索引进行查询;然而如果非最左匹配的查询条件,例如,性别+姓名这种查询条件就不会触发联合索引

Join优化

MySQL的join语句连接表使用的是nested-loop join算法,这个过程类似于嵌套循环,简单来说,就是遍历驱动表(外层表),每读出一行数据,取出连接字段到被驱动表(内层表)里查找满足条件的行,组成结果行

要提升join语句的性能,就要尽可能减少嵌套循环的循环次数

一个显著优化方式是对被驱动表的join字段建立索引,利用索引能快速匹配到对应的行,避免与内层表每一行记录做比较,极大地减少总循环次数。另一个优化点,就是连接时用小结果集驱动大结果集,在索引优化的基础上能进一步减少嵌套循环的次数

如果难以判断哪个是大表,哪个是小表,可以用inner join连接,MySQL会自动选择小表去驱动大表

避免使用JOIN关联太多的表

对于 MySQL 来说,是存在关联缓存的,缓存的大小可以由join_buffer_size参数进行设置

在 MySQL 中,对于同一个 SQL 多关联(join)一个表,就会多分配一个关联缓存,如果在一个 SQL 中关联的表越多,所占用的内存也就越大

如果程序中大量的使用了多表关联的操作,同时join_buffer_size设置的也不合理的情况下,就容易造成服务器内存溢出的情况,就会影响到服务器数据库性能的稳定性

排序优化

利用索引扫描做排序

MySQL有两种方式生成有序结果:其一是对结果集进行排序的操作,其二是按照索引顺序扫描得出的结果自然是有序的

但是如果索引不能覆盖查询所需列,就不得不每扫描一条记录回表查询一次,这个读操作是随机IO,通常会比顺序全表扫描还慢

因此,在设计索引时,尽可能使用同一个索引既满足排序又用于查找行

例如:

--建立索引(date,staff_id,customer_id)
select staff_id, customer_id from test where date = &#39;2010-01-01&#39; order by staff_id,customer_id;

只有当索引的列顺序和ORDER BY子句的顺序完全一致,并且所有列的排序方向都一样时,才能够使用索引来对结果做排序

UNION优化

MySQL处理union的策略是先创建临时表,然后将各个查询结果填充到临时表中最后再来做查询,很多优化策略在union查询中都会失效,因为它无法利用索引

最好手工将where、limit等子句下推到union的各个子查询中,以便优化器可以充分利用这些条件进行优化

此外,除非确实需要服务器去重,一定要使用union all,如果不加all关键字,MySQL会给临时表加上distinct选项,这会导致对整个临时表做唯一性检查,代价很高

慢查询日志

出现慢查询通常的排查手段是先使用慢查询日志功能,查询出比较慢的 SQL 语句,然后再通过 Explain 来查询 SQL 语句的执行计划,最后分析并定位出问题的根源,再进行处理

느린 쿼리 로그는 long_query_time SQL 값이 로그에 기록됩니다long_query_time值的 SQL 将会被记录在日志中

我们可以通过设置“slow_query_log=1”

"slow_query_log=1" 느린 쿼리를 켜려면

느린 로그 기능을 켠 후에는 쿼리에 영향을 미친다는 점에 유의해야 합니다. MySQL의 성능에 영향을 미치므로 프로덕션 환경에서는 주의해서 사용해야 합니다. NULL을 사용하지 마세요. 연산자, NULL이 포함된 열은 쿼리를 최적화하기 어렵습니다.

열은 다음과 같이 지정해야 합니다. null이 아니며 null 값은 0, 빈 문자열 또는 int null 기본값이 아닌 0으로 정의된 것과 같은 기타 특수 값으로 대체되어야 합니다. 최소 데이터 길이

더 작은 데이터 유형 길이는 일반적으로 디스크, 메모리에서 더 적은 공간을 필요로 합니다.

가장 간단한 데이터 유형을 사용하세요

다음과 같은 간단한 데이터 유형 작업이 더 저렴합니다. int 유형을 사용할 수 있다면 varchar 유형을 사용하지 마세요. 쿼리 시 varchar 유형보다 더 효율적입니다. 텍스트 유형 쿼리 효율성은 매우 낮습니다. 이 필드를 쿼리해야 하는 경우 공동 쿼리를 사용하면 기본 테이블의 쿼리 효율성을 높일 수 있습니다

적절한 하위 테이블 및 하위 데이터베이스 전략

하위 테이블은 테이블에 더 많은 필드가 있는 경우를 의미합니다. 큰 테이블을 여러 하위 테이블로 분할하고, 더 자주 사용되는 기본 정보를 기본 테이블에 넣고, 나머지 정보는 하위 테이블에 넣을 수 있습니다. 이러한 방식으로 대부분의 쿼리는 기본 테이블만 쿼리하면 됩니다. 더 적은 수의 필드를 사용하여 쿼리 효율성을 효과적으로 향상시킵니다.

하위 데이터베이스는 데이터베이스를 여러 데이터베이스로 나누는 것을 의미합니다. 예를 들어, 데이터베이스를 여러 데이터베이스로 분할하면 하나의 기본 데이터베이스는 데이터를 작성하고 수정하는 데 사용되고, 다른 데이터베이스는 기본 데이터를 동기화하여 쿼리용으로 제공하는 데 사용됩니다. 하나의 데이터베이스의 압력을 여러 라이브러리가 공유하여 데이터베이스의 전반적인 운영 효율성을 향상시킵니다

공통 유형 선택

정수 유형 너비 설정

MySQL은 int(11)과 같은 정수 유형의 너비를 지정할 수 있는데 이는 실제로 의미가 없으며 값의 범위를 제한하지 않습니다. 저장 및 계산의 경우 int(1)과 int(20)은 동일

VARCHAR 및 CHAR 유형

char 유형은 고정 길이이고 varchar는 가변 문자열을 저장하므로 고정 길이보다 더 많은 공간을 절약하지만 varchar는 문자열 길이를 기록하려면 추가로 1~2바이트가 필요하며 업데이트 시 조각화되기 쉬우므로 사용 시나리오에 따라 선택해야 합니다. 문자열 열의 최대 길이가 평균 길이보다 훨씬 큰 경우 또는 열이 거의 업데이트되지 않는 경우 varchar를 선택하는 것이 더 편리합니다. 매우 짧은 문자열을 저장하려는 경우, 문자열 값의 길이가 MD5 값과 같은 경우 또는 열 데이터가 자주 변경되는 경우에 적합합니다. , char 유형

DATETIME 및 TIMESTAMP 유형

을 사용하도록 선택하세요.datetime은 더 넓은 범위를 가지며 1001년부터 9999년까지의 연도를 나타낼 수 있고 타임스탬프는 1970년부터 2038년까지의 연도만 나타낼 수 있습니다. datetime은 시간대와 관련이 없으며 타임스탬프 표시 값은 시간대에 따라 다릅니다. 대부분의 시나리오에서는 두 유형 모두 잘 작동하지만 날짜/시간은 8바이트를 차지하고 타임스탬프는 4바이트만 차지하며 타임스탬프 공간이 더 효율적이므로 타임스탬프를 사용하는 것이 좋습니다.

BLOB 및 TEXT 유형

blob과 텍스트는 둘 다입니다. 많은 양의 데이터를 저장하도록 설계된 문자열 데이터 유형은 각각 바이너리 및 문자 모드로 저장됩니다.

실제 사용 시 이 두 유형은 쿼리 효율성이 매우 낮으므로 주의해서 사용해야 합니다. 유형에 따라 이 필드를 하위 테이블로 분리할 수 있습니다. 이 필드를 쿼리해야 하는 경우 공동 쿼리를 사용하면 기본 테이블의 쿼리 효율성이 향상됩니다

Normalization 더 나은 패러다임 정규화할 때 더 적은 데이터가 수정되며 정규화된 테이블은 일반적으로 더 작고 더 많은 데이터를 메모리에 캐시할 수 있으므로 실행 작업이 더 빨라집니다

단점은 쿼리 중에 더 많은 연관이 필요하다는 것입니다

Chapter 첫 번째 정규형: 필드를 분리할 수 없으며 데이터베이스에서 기본적으로 지원합니다. 두 번째 정규형: 기본 키에 대한 부분 종속성을 제거합니다. 자동 증가 ID를 사용하는 것과 같습니다. 세 번째 정규형: 기본 키에 대한 전이 종속성을 제거하고 테이블을 분할하여 데이터 중복성을 줄입니다

하드웨어 최적화

MySQL의 하드웨어 요구 사항은 주로 디스크, 네트워크, 메모리의 세 가지 측면에 반영됩니다.

Disk

디스크는 고성능 읽기 및 쓰기 기능을 갖춘 디스크를 사용해야 합니다. 이러한 방식으로 I/O 실행 시간을 줄일 수 있으므로 MySQL의 전반적인 운영 효율성이 향상됩니다. 디스크는 고정되어 있습니다. 이는 여러 디스크를 병렬로 실행하는 것과 같습니다

Network

원활한 네트워크 대역폭(낮은 대기 시간)과 충분한 네트워크 대역폭을 보장하는 것은 MySQL의 정상적인 작동을 위한 기본 조건입니다. 또한 여러 개의 네트워크 카드를 설정하여 네트워크 피크 기간 동안 MySQL 서버의 운영 효율성을 향상시킵니다.

Memory

MySQL 서버의 메모리가 클수록 더 많은 정보가 저장되고 캐시되며 성능이 향상됩니다. 메모리가 매우 높으므로 전체 MySQL 운영 효율성이 향상됩니다

위 내용은 인터뷰 질문: 일상 업무에서 MySQL을 어떻게 최적화합니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 Java后端技术全栈에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제