数据库优化

简介: 数据库优化可以从哪些维度入手?

343754ad2d63b8cea65bc4f7ec6dabbd.png正如上图所示,数据库优化可以从架构优化,硬件优化,DB优化,SQL优化四个维度入手,此上而下,位置越靠前优化越明显,对数据库的性能提升越高。我们常说的SQL优化反而是对性能提高最小的优化。


接下来我们再看看每种优化该如何实施。


架构优化


一般来说在高并发的场景下对架构层进行优化其效果最为明显,常见的优化手段有:分布式缓存,读写分离,分库分表等,每种优化手段又适用于不同的应用场景。


分布式缓存


有句老话说的好,性能不够,缓存来凑。当需要在架构层进行优化时我们第一时间就会想到缓存这个神器,在应用与数据库之间增加一个缓存服务,如Redis或Memcache。



eedabaf57d5e457e840a6b35136c05f9.png


当接收到查询请求后,我们先查询缓存,判断缓存中是否有数据,有数据就直接返回给应用,如若没有再查询数据库,并加载到缓存中,这样就大大减少了对数据库的访问次数,自然而然也提高了数据库性能。


不过需要注意的是,引入分布式缓存后系统需要考虑如何应对缓存穿透、缓存击穿和缓存雪崩的问题。


简单理解一下 缓存穿透、缓存击穿 和 缓存雪崩


缓存穿透:它是指当用户在查询一条数据的时候,而此时数据库和缓存都没有关于这条数据的任何记录。这条数据在缓存中没找到就会向数据库请求获取数据。它拿不到数据时,是会一直查询数据库,这样会对数据库的访问造成很大的压力。


缓存击穿:一个热点key刚好在某个时间点失效了,但是这时候突然来了大量对这个key的并发访问请求,导致大并发请求直接穿透缓存直达数据库,瞬间对数据库的访问压力增大。


缓存雪崩:某一个时间段内,缓存集中过期失效,如果这个时间段内有大量请求,而查询数据量巨大,所有的请求都会达到存储层,存储层的调用量会暴增,引起数据库压力过大甚至宕机。


读写分离


一主多从,读写分离,主动同步,是一种常见的数据库架构优化手段。


一般来说当你的应用是读多写少,数据库扛不住读压力的时候,采用读写分离,通过增加从库数量可以线性提升系统读性能。


eedabaf57d5e457e840a6b35136c05f9.png


主库,提供数据库写服务;从库,提供数据库读能力;主从之间,通过binlog同步数据。


当准备实施读写分离时,为了保证高可用,需要实现故障的自动转移,主从架构会有潜在主从不一致性问题。


水平切分


水平切分,也是一种常见的数据库架构优化手段。


当你的应用业务数据量很大,单库容量成为性能瓶颈后,采用水平切分,可以降低数据库单库容量,提升数据库写性能。


image.png


当准备实施水平切分时,需要结合实际业务选取合理的分片键(sharding-key),有时候为了解决非分片键查询问题还需要将数据写到单独的查询组件,如ElasticSearch。


架构优化小结


读写分离主要是用于解决 “数据库读性能问题”

水平切分主要是用于解决“数据库数据量大的问题”

分布式缓存架构可能比读写分离更适用于高并发、大数据量大场景。


硬件优化


我们使用数据库,不管是读操作还是写操作,最终都是要访问磁盘,所以说磁盘的性能决定了数据库的性能。一块PCIE固态硬盘的性能是普通机械硬盘的几十倍不止。这里我们可以从吞吐率、IOPS两个维度看一下机械硬盘、普通固态硬盘、PCIE固态硬盘之间的性能指标。


吞吐率:单位时间内读写的数据量


机械硬盘:约100MB/s ~ 200MB/s

普通固态硬盘:200MB/s ~ 500MB/s

PCIE固态硬盘:900MB/s ~ 3GB/s

IOPS:每秒IO操作的次数


机械硬盘:100 ~200

普通固态硬盘:30000 ~ 50000

PCIE固态硬盘:数十万

通过上面的数据可以很直观的看到不同规格的硬盘之间的性能差距非常大,当然性能更好的硬盘价格会更贵,在资金充足并且迫切需要提升数据库性能时,尝试更换一下数据库的硬盘不失为一个非常好的举措,你之前遇到SQL执行缓慢问题在你更换硬盘后很可能将不再是问题。


DB优化


SQL执行慢有时候不一定完全是SQL问题,手动安装一台数据库而不做任何参数调整,再怎么优化SQL都无法让其性能最大化。要让一台数据库实例完全发挥其性能,首先我们就得先优化数据库的实例参数。


数据库实例参数优化遵循三句口诀:日志不能小、缓存足够大、连接要够用。


数据库事务提交后需要将事务对数据页的修改刷( fsync)到磁盘上,才能保证数据的持久性。这个刷盘,是一个随机写,性能较低,如果每次事务提交都要刷盘,会极大影响数据库的性能。数据库在架构设计中都会采用如下两个优化手法:


先将事务写到日志文件RedoLog(WAL),将随机写优化成顺序写

加一层缓存结构Buffer,将单次写优化成顺序写

所以日志跟缓存对数据库实例尤其重要。而连接如果不够用,数据库会直接抛出异常,系统无法访问。


接下来我们以Oracle、MySQL(InnoDB)、POSTGRES、达梦为例,看看每种数据库的参数该如何配置。


Oracle


image.png


image.png

SQL优化


SQL优化很容易理解,就是通过给查询字段添加索引或者改写SQL提高其执行效率,一般而言,SQL编写有以下几个通用的技巧:


合理使用索引

索引少了查询慢;索引多了占用空间大,执行增删改语句的时候需要动态维护索引,影响性能

选择率高(重复值少)且被where频繁引用需要建立B树索引;一般join列需要建立索引;复杂文档类型查询采用全文索引效率更好;索引的建立要在查询和DML性能之间取得平衡;复合索引创建时要注意基于非前导列查询的情况


使用UNION ALL替代UNION

UNION ALL的执行效率比UNION高,UNION执行时需要排重;UNION需要对数据进行排序


避免select * 写法

执行SQL时优化器需要将 * 转成具体的列;每次查询都要回表,不能走覆盖索引。


JOIN字段建议建立索引

一般JOIN字段都提前加上索引


避免复杂SQL语句

提升可阅读性;避免慢查询的概率;可以转换成多个短查询,用业务端处理


避免where 1=1写法


避免order by rand()类似写法


RAND()导致数据列被多次扫描


执行计划


要想优化SQL必须要会看执行计划,执行计划会告诉你哪些地方效率低,哪里可以需要优化。我们以MYSQL为例,来认识一下执行计划。


通过explain sql 可以查看执行计划,如:


1b16be7c055047d574f99c81ad4163e5.png


image.png


SQL优化实战


这里为大家准备了一套SQL优化的综合实战,一步一步带你走一遍完整SQL优化的过程。

在执行优化之前我们需要先认识一下原始表及待优化的SQL。

  1. 原数据库表结构


CREATE TABLE `a`
(
    `id`          int(11) NOT NULL AUTO_INCREMENT,
    `seller_id`   bigint(20)                                       DEFAULT NULL,
    `seller_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
    `gmt_create`  varchar(30)                                      DEFAULT NULL,
    PRIMARY KEY (`id`)
);
CREATE TABLE `b`
(
    `id`          int(11) NOT NULL AUTO_INCREMENT,
    `seller_name` varchar(100) DEFAULT NULL,
    `user_id`     varchar(50)  DEFAULT NULL,
    `user_name`   varchar(100) DEFAULT NULL,
    `sales`       bigint(20)   DEFAULT NULL,
    `gmt_create`  varchar(30)  DEFAULT NULL,
    PRIMARY KEY (`id`)
);
CREATE TABLE `c`
(
    `id`         int(11) NOT NULL AUTO_INCREMENT,
    `user_id`    varchar(50)  DEFAULT NULL,
    `order_id`   varchar(100) DEFAULT NULL,
    `state`      bigint(20)   DEFAULT NULL,
    `gmt_create` varchar(30)  DEFAULT NULL,
    PRIMARY KEY (`id`)
);


  1. 待优化的SQL(查询当前用户在当前时间前后10个小时的订单情况,并根据订单创建时间升序排列)
select a.seller_id,
       a.seller_name,
       b.user_name,
       c.state
from a,
     b,
     c
where a.seller_name = b.seller_name
  and b.user_id = c.user_id
  and c.user_id = 17
  and a.gmt_create
    BETWEEN DATE_ADD(NOW(), INTERVAL – 600 MINUTE)
    AND DATE_ADD(NOW(), INTERVAL 600 MINUTE)
order by a.gmt_create;


  1. 原表数据量:

f65414df6b4745459be656ecd51e1966.png


  1. 原执行时间


d405b792af30e6ca327d6e24d1679abe.png


0.21s,执行速度还挺快

  1. 原执行计划


7dc661c9ecea309b44b7c2f1bb2f8210.png


真是糟糕的执行计划。(全表扫描,没有索引;临时表;排序)


初步优化思路:


SQL中 where条件字段类型要跟表结构一致,表中user_id 为varchar(50)类型,实际SQL用的int类型,存在隐式转换,也未添加索引。将b和c表user_id 字段改成int类型。

因存在b表和c表关联,将b和c表user_id创建索引

因存在a表和b表关联,将a和b表seller_name字段创建索引

利用复合索引消除临时表和排序

初步优化SQL



/

alter table b modify `user_id` int(10) DEFAULT NULL;
alter table c modify `user_id` int(10) DEFAULT NULL;
alter table c add index `idx_user_id`(`user_id`);
alter table b add index `idx_user_id_sell_name`(`user_id`,`seller_name`);
alter table a add index `idx_sellname_gmt_sellid`(`gmt_create`,`seller_name`,`seller_id`);


查看优化后的执行时间


109389e81d7dbd0954bae0a688405a44.png


通过执行计划可以看到,执行时间从0.21s优化成了0.01s,执行时间近乎缩短20倍。

查看优化后的执行计划


109389e81d7dbd0954bae0a688405a44.png


执行计划显示从全表扫描优化成了走索引,rows减少,但是此时出现了2个告警。

通过show warning语句 查看告警信息


cd08f95c86b7522903f742745c704bc2.png


提示gmt_crteate 的格式不对,mysql进行了隐式转换导致不能使用索引。

继续优化,修改gmtc-create的格式

alter table a modify "gmt_create" datetime DEFAULT NULL;
• 1

再次查看执行时间


392072d6e6aacd63748874f3cdaf0afe.png


再次查看执行计划


f8fb43cdcbac2372c1ae95301cafe4cc.png


至此,我们的优化过程结束,结果非常完美。

SQL优化小结


这里给大家总结一下SQL优化的套路:


查看执行计划 explain sql

如果有告警信息,查看告警信息 show warnings;

查看SQL涉及的表结构和索引信息

根据执行计划,思考可能的优化点

按照可能的优化点执行表结构变更、增加索引、SQL改写等操作

查看优化后的执行时间和执行计划

如果优化效果不明显,重复第四步操作


小结


我们今天分别从架构优化、硬件优化、DB优化、SQL优化四个角度探讨了如何实施优化,提升数据库性能。但是大家还是要记住一句话,数据库系统没有银弹, 要让适合的系统,做合适的事情。最后,我是飘渺Jam,一名写代码的架构师,做架构的程序员,期待您的转发与关注,当然也可以添加我的个人微信 jianzh5,咱们一起聊技术!



目录
相关文章
|
JavaScript 关系型数据库 MySQL
❤Nodejs 第六章(操作本地数据库前置知识优化)
【4月更文挑战第6天】本文介绍了Node.js操作本地数据库的前置配置和优化,包括处理接口跨域的CORS中间件,以及解析请求数据的body-parser、cookie-parser和multer。还讲解了与MySQL数据库交互的两种方式:`createPool`(适用于高并发,通过连接池管理连接)和`createConnection`(适用于低负载)。
22 0
|
1月前
|
存储 关系型数据库 MySQL
轻松入门MySQL:数据库设计之范式规范,优化企业管理系统效率(21)
轻松入门MySQL:数据库设计之范式规范,优化企业管理系统效率(21)
|
1月前
|
存储 关系型数据库 MySQL
轻松入门MySQL:优化复杂查询,使用临时表简化数据库查询流程(13)
轻松入门MySQL:优化复杂查询,使用临时表简化数据库查询流程(13)
|
1天前
|
数据库
编程日记02:个人站优化数据库和日志
编程日记02:个人站优化数据库和日志
5 0
|
2天前
|
缓存 关系型数据库 数据库
【Docker 专栏】Docker 与容器化数据库的集成与优化
【5月更文挑战第9天】本文探讨了Docker与容器化数据库集成的优势,如快速部署、环境一致性、资源隔离和可扩展性,并列举了常见容器化数据库(如MySQL、PostgreSQL和MongoDB)。讨论了集成方法、注意事项、优化策略,包括资源调整、缓存优化和监控告警。此外,强调了数据备份、恢复测试及性能评估的重要性。未来,随着技术发展,二者的集成将更紧密,为数据管理带来更多可能性。掌握此技术将应对数字化时代的机遇与挑战。
【Docker 专栏】Docker 与容器化数据库的集成与优化
|
3天前
|
存储 关系型数据库 分布式数据库
数据库索引回表困难?揭秘PolarDB存储引擎优化技术
PolarDB分布式版存储引擎采用CSM方案均衡资源开销与可用性。
数据库索引回表困难?揭秘PolarDB存储引擎优化技术
|
11天前
|
存储 SQL 缓存
构建高效的矢量数据库查询:查询语言与优化策略
【4月更文挑战第30天】本文探讨了构建高效矢量数据库查询的关键点,包括设计简洁、表达性强的查询语言,支持空间操作、函数及索引。查询优化策略涉及查询重写、索引优化、并行处理和缓存机制,以提升查询效率和准确性。这些方法对处理高维空间数据的应用至关重要,随着技术进步,矢量数据库查询系统将在更多领域得到应用。
|
11天前
|
存储 缓存 固态存储
优化矢量数据库性能:技巧与最佳实践
【4月更文挑战第30天】本文探讨了优化矢量数据库性能的技巧和最佳实践,包括硬件(如使用SSD、增加内存和利用多核处理器)、软件(索引优化、查询优化、数据分区和压缩)和架构(读写分离、分布式架构及缓存策略)方面的优化措施。通过这些方法,可以提升系统运行效率,应对大数据量和复杂查询的挑战。
|
13天前
|
关系型数据库 大数据 数据库
关系型数据库索引优化
关系型数据库索引优化是一个综合的过程,需要综合考虑数据的特点、查询的需求以及系统的性能要求。通过合理的索引策略和技术,可以显著提高数据库的查询性能和整体效率。
21 4
|
13天前
|
存储 缓存 关系型数据库
关系型数据库数据库表设计的优化
您可以优化关系型数据库的表设计,提高数据库的性能、可维护性和可扩展性。但请注意,每个数据库和应用程序都有其独特的需求和挑战,因此在实际应用中需要根据具体情况进行调整和优化。
14 4