凯发app官方网站-凯发k8官网下载客户端中心 | | 凯发app官方网站-凯发k8官网下载客户端中心
  • 博客访问: 85475
  • 博文数量: 165
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1655
  • 用 户 组: 普通用户
  • 注册时间: 2022-09-26 14:37
文章分类

全部博文(165)

文章存档

2024年(2)

2023年(95)

2022年(68)

我的朋友
相关博文
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·

分类: mysql/postgresql

2023-01-18 10:31:42

mysql性能优化浅析及线上案例-凯发app官方网站

业务发展初期,数据库中量一般都不高,也不太容易出一些性能问题或者出的问题也不大,但是当数据库的量级达到一定规模之后,如果缺失有效的预警、监控、处理等手段则会对用户的使用体验造成影响,严重的则会直接导致订单、金额直接受损,因而就需要时刻关注数据库的性能问题。

数据库性能优化的常见手段有很多,比如添加索引、分库分表、优化连接池等,具体如下:

序号

类型

措施

说明

1

物理级别

提升硬件性能

将数据库安装到更高配置的服务器上会有立竿见影的效果,例如提高cpu配置、增加内存容量、采用固态硬盘等手段,在经费允许的范围可以尝试。

2

应用级别

连接池参数优化

我们大部分的应用都是使用连接池来托管数据库的连接,但是大部分都是默认的配置,因而配置好超时时长、连接池容量等参数就显得尤为重要。 1、 如果链接长时间被占用,新的请求无法获取到新的连接,就会影响到业务。 2、 如果连接数设置的过小,那么即使硬件资源没问题,也无法发挥其功效。之前公司做过一些压测,但就是死活不达标,{banned}最佳后发现是由于连接数太小。

3

单表级别

合理运用索引

如果数据量较大,但是又没有合适的索引,就会拖垮整个性能,但是索引是把双刃剑,并不是说索引越多越好,而是要根据业务的需要进行适当的添加和使用。 缺失索引、重复索引、冗余索引、失控索引这几类情况其实都是对系统很大的危害。

4

库表级别

分库分表

当数据量较大的时候,只使用索引就意义不大了,需要做好分库分表的操作,合理的利用好分区键,例如按照用户id、订单id、日期等维度进行分区,可以减少扫描范围。

5

监控级别

加强运维

针对线上的一些系统还需要进一步的加强监控,比如订阅一些慢sql日志,找到比较糟糕的一些sql,也可以利用业务内一些通用的工具,例如druid组件等。

首先了解一下数据的底层架构,也有助于我们做更好优化。



一次查询请求的执行过程

我们重点关注第二部分和第三部分,第二部分其实就是server层,这层主要就是负责查询优化,制定出一些执行计划,然后调用存储引擎给我们提供的各种底层基础api,{banned}最佳终将数据返回给客户端。

目前比较常用的是innodb存储引擎,本文讨论也是基于innodb引擎。我们一直说的加索引,那到底什么是索引、索引又是如何形成的呢、索引又如何应用呢?这个话题其实很大也很小,说大是因为他底层确实很复杂,说小是因为在大部分场景下程序员只需要添加索引就好,不太需要了解太底层原理,但是如果了解不透彻就会引发线上问题,因而本文平衡了大家的理解成本和知识深度,有一定底层原理介绍,但是又不会太过深入导致难以理解。

首先来做个实验:

创建一个表,目前是只有一个主键索引

create table `t1`(

a int not null,

b int default null,

c int default null,

d int default null,

e varchar(20) default null,

primarykey(a)

)engine=innodb

插入一些数据:

insert into test.t1 values(4,3,1,1,'d');

insert into test.t1 values(1,1,1,1,'a');

insert into test.t1 values(8,8,8,8,'h');

insert into test.t1 values(2,2,2,2,'b');

insert into test.t1 values(5,2,3,5,'e');

insert into test.t1 values(3,3,2,2,'c');

insert into test.t1 values(7,4,5,5,'g');

insert into test.t1 values(6,6,4,4,'f');

mysql从磁盘读取数据到内存是按照一页读取的,一页默认是16k,而一页的格式大概如下。


每一页都包括了这么几个内容,首先是页头、其次是页目录、还有用户数据区域。

1)刚才插入的几条数据就是放到这个用户数据区域的,这个是按照主键依次递增的单向链表。

2)页目录这个是用来指向具体的用户数据区域,因为当用户数据区域的数据变多的时候也就会形成分组,而页目录就会指向不同的分组,利用二分查找可以快速的定位数据。

当数据量变多的时候,那么这一页就装不下这么多数据,就要分裂页,而每页之间都会双向链接,{banned}最佳终形成一个双向链表。

页内的单向链表是为了查找快捷,而页间的双向链表是为了在做范围查询的时候提效,下图为示意图,其中其二页和第三页是复制的{banned}中国第一页,并不真实。


而如果数据还继续累加,光这几个页也不够了,那就逐步的形成了一棵树,也就是说索引b-tree是随着数据的积累逐步构建出来的。



{banned}最佳下边的一层叫做叶子节点,上边的叫做内节点,而叶子节点中存储的是全量数据,这样的树就是聚簇索引。一直有同学的理解是说索引是单独一份而数据是一份,其实mysql中有一个原则就是数据即索引、索引即数据,真实的数据本身就是存储在聚簇索引中的,所谓的回表就是回的聚簇索引

但是我们也不一定每次都按照主键来执行sql语句,大部分情况下都是按照一些业务字段来,那就会形成别的索引树,例如,如果按照b,c,d来创建的索引就会长这样。


推荐1个网站,可以可视化的查看一些算法原型:

目录:

~galles/visualization/algorithms.html

b 树

~galles/visualization/bplustree.html

而在mysql凯发k8官网下载客户端中心官网上介绍的索引的叶子节点是双向链表。



关于索引结构的小结:

对于b-tree而言,叶子节点是没有链接的,而b tree索引是单向链表,但是mysql在b tree的基础之上加以改进,形成了双向链表,双向的好处是在处理> <,between and等'范围查询'语法时可以得心应手。

1、 只为用于搜索、排序或分组的列创建索引。

重点关注where语句后边的情况

2、 当列中不重复值的个数在总记录条数中的占比很大时,才为列建立索引。

例如手机号、用户id、班级等,但是比如一张全校学生表,每条记录是一名学生,where语句是查询所有’某学校‘的学生,那么其实也不会提高性能。

3、 索引列的类型尽量小。

无论是主键还是索引列都尽量选择小的,如果很大则会占据很大的索引空间。

4、 可以只为索引列前缀创建索引,减少索引占用的存储空间。

alter table single_table add index idx_key1(key1(10))

5、 尽量使用覆盖索引进行查询,以避免回表操作带来的性能损耗。

select key1 from single_table order by key1

6、 为了尽可能的少的让聚簇索引发生页面分裂的情况,建议让主键自增。

7、 定位并删除表中的冗余和重复索引。

冗余索引:

单列索引:(字段1)

联合索引:(字段1 字段2)

重复索引:

在一个字段上添加了普通索引、唯一索引、主键等多个索引




其中常用的是:

possible_keys: 可能用到的索引

key: 实际使用的索引

rows:预估的需要读取的记录条数

案例1:

在建设互联网医院系统中,问诊单表当时量级23万左右,其中有一个business_id字符串字段,这个字段用来记录外部订单的id,并且在该字段上也加了索引,但是'根据该id查询详情'的sql语句却总是时好时坏,性能不稳定,快则10ms,慢则2秒左右,sql大体如下:

select 字段1、字段2、字段3 from nethp_diag where business_id = ?

因为business_id是记录第三方系统的订单id,为了兼容不同的第三方系统,因而设计成了字符串类型,但如果传入的是一个数字类型是无法使用索引的,因为mysql只能将字符串转数字,而不能将数字转字符串,由于外部的id有的是数字有的是字符串,因而导致索引一会可以走到,一会走不到,{banned}最佳终导致了性能的不稳定。

案例2:

在某次大促的当天,突然接到dba运维的报警,说数据库突然流量激增,cpu也打到100%了,影响了部分线上功能和体验,遇到这种情况当时大部分人都比较紧张,下图为当时的数据库流量情况:



相关sql语句:

select count(1) from jdhe_medical_record where status = 1 and is_test = #{istest,jdbctype=integer} and electric_medical_record_status in (2,3) and patient_id = #{patientid,jdbctype=bigint} and doctor_pin = #{doctorpin,jdbctype=varchar} and created >#{datestart,jdbctype=timestamp};


当时的索引情况


??当时的执行计划


??
其实在patientid和doctor_pin两个字段上是有索引的,但是由于线上情况的改变,导致test判断没有进入,这样的通用查询导致这两个字段没有设置上,进而导致了数据库扫描的量激增,对数据库产生了很大压力。

案例3:

2020年某日上午收到数据库cpu异常报警,对线上有一定的影响,后续检查数据库cpu情况如下,从7点51分开始,cpu从8%瞬间达到99.92%,丝毫没有给程序员留任何情面。


??
当时的sql语句:

select rx_id, rx_create_time from nethp_rx_info where rx_status = 5 and status = 1 and rx_product_type = 0 and (parent_rx_id = 0 or parent_rx_id is null) and business_type != 7 and vender_id = 8888 order by rx_create_time asc limit 1;

当时的索引情况:

primary key (`id`), unique key `uniq_rx_id` (`rx_id`), key `idx_diag_id` (`diag_id`), key `idx_doctor_pin` (`doctor_pin`) using btree, key `idx_rx_storeid` (`store_id`), key `idx_parent_rx_id` (`parent_rx_id`) using btree, key `idx_rx_status` (`rx_status`) using btree, key `idx_doctor_status_type` (`doctor_pin`, `rx_status`, `rx_type`), key `idx_business_store` (`business_type`, `store_id`), key `idx_doctor_pin_patientid` (`patient_id`, `doctor_pin`) using btree, key `idx_rx_create_time` (`rx_create_time`)

当时这张表量级2000多万,而当这条慢sql执行较少的时候,数据库的cpu也就下来了,恢复到了49.91%,基本可以恢复线上业务,从而表象就是线上间歇性的一会可以开方一会不可以,这条sql当时总共执行了230次,当时的cpu情况也是忽高忽低,伴随这条sql语句的执行情况,从而{banned}最佳终证明cpu的飙升是由于这条慢sql。当线上业务逻辑复杂的时候,你很难{banned}中国第一时间知道到底是由于那条sql引起的,这个就需要对业务非常熟悉,对sql很熟悉,否则就会白白浪费大量的排查时间。

{banned}最佳后的排查结果:

在头天晚上的时候

阅读(1185) | 评论(0) | 转发(0) |
0

上一篇:

下一篇:

给主人留下些什么吧!~~
")); function link(t){ var href= $(t).attr('href'); href ="?url=" encodeuricomponent(location.href); $(t).attr('href',href); //setcookie("returnouturl", location.href, 60, "/"); }
网站地图