数据库SQL优化技巧

作为程序员,主要的工作任务就是curd,和数据库打交道是无可避免的。掌握一些数据库的优化技巧是非常有必要的

一、减少数据访问

1、使用索引

索引的原理是利用额外的空间建立了一个平衡的搜索树,大大缩短了查询的时间,使得查询的时间复杂度从O(n)降低到O(log(n)),但是在进行插入、修改、删除操作的时候,同时要对搜索树进行相对应的维护,需要带来额外的开销

例如上面的搜索树,需要查询6的时候,步骤如下

(1)6和10比较,10大,那么往左边下去,到4

(2)6和4比较,6大,那么往右边下去,到6

(3)6和6比较,相等,那么6就是想要查询的数据

如果没有索引,那么每个元素都要比较一下才可以查出目标数据,这点可以比喻二分查找元素的案例

上面的搜索树只是一个类比,mysql的索引使用的是b+树,思想和上面的例子是差不多的,只不过b+树是多叉平衡树,并且数据存在叶子节点,这样可以方便查找相邻的元素

使用索引需要注意的是,只有当索引符合一定条件的时候索引才会生效,因为索引并不能解决100%的问题,例如两个字段id、name,id列建立了一个索引,假如判断条件为where id = name,每一行的name是会变化的,那么就无法使用索引,必须每一行都进行判断才可以得到结果,并不是数据库设计者故意不让索引生效的,而是索引本身不能解决这个问题

查询条件 不能使用索引原因
INDEX_COLUMN <> ? 不等于操作不能使用索引
INDEX_COLUMN not in (?,?,...,?) 不等于操作不能使用索引
function(INDEX_COLUMN) = ? 经过普通运算或函数运算后的索引字段不能使用索引
INDEX_COLUMN || 'a' = ? 经过普通运算或函数运算后的索引字段不能使用索引
INDEX_COLUMN like '%'||? 含前导模糊查询的Like语法不能使用索引
INDEX_COLUMN like '%'||?||'%' 含前导模糊查询的Like语法不能使用索引
INDEX_COLUMN is null B-TREE索引里不保存字段为NULL值记录,因此IS NULL不能使用索引
CHAR_INDEX_COLUMN=12345 Oracle在做数值比较时需要将两边的数据转换成同一种数据类型,如果两边数据类型不同时会对字段值隐式转换,相当于加了一层函数处理,所以不能使用索引。
a.INDEX_COLUMN=a.COLUMN_1 给索引查询的值应是已知数据,不能是未知字段值。

在使用联合索引还有个最左匹配原则,例如有联合索引a、b、c,a没生效,那么b和c就无法生效,反过来,c如果要生效,那么a、b必须生效

2、分库分表

分库分表又有垂直划分和水平划分两种,先看一下定义:
(1)垂直分库

一般是为了解耦,将一个整体划分为多个部分,同时这样做也可以平衡数据库的压力,每个数据库管理一部分数据

(2)水平分库

一般用于解决单个数据库压力过大的问题,例如一百万个人同时访问学生数据库,会导致数据库瘫痪,那么可以考虑水平分库,下面举一个例子进行理解

例如下面将学生数据库分为了3个,那么可以根据学生的id去分配学生数据,例如常见的mod法,假如id为10,10%3=0,那么这个学生的数据库存在第一个数据库中,反过来看,数据库1存放学生id为0,3、6、9...的学生数据,数据2存放的是1、4、7、10...的学生数据,数据3存放的是2、5、8、11...的学生数据

(3)垂直分表

把存储数据多的或者不常用的字段分离出来,可以提高查询的速度,因为在查找的时候不用额外扫描不需要的数据,例如下面,简介可以储存1000个字,那么在查找的时候是相对耗时的,即使我们没有去查该字段的数据,分离之后,查找学生姓名的时候就不需要扫描简介字段了

(4)水平分表

水平分表和水平分库是类似的,单个表的数据量过大,那么查询的时间就会长,如果建立了索引,对插入、修改和删除操作就会有严重的影响

水平划分数据可以解决这个问题,可以按一定的逻辑去划分数据,不一定使用mod法,例如下面的例子:

由于业务需求,可能需要对各个年级的学生数据进行维护,例如划分课程等等,就可以按学生年级、专业这些相关字段去划分

(5)总结

水平分库、垂直分表、水平分表都使用了减少数据访问的原理对数据库操作做了优化,而垂直分库可解耦合,使得数据库变得更加灵活易维护

二、返回更少的数据

数据的传输是需要花费时间的,传输越多的数据,那么花费的时间就越多

(1)分页

一般来说,展示给用户的数据都会有分页的,那么查询的时候可以根据一些推荐算法对数据进行排序,用户需要更多数据的时候再查需要的数据,而不是将查到的数据一下子全部发送到客户端

(2)只返回需要的字段

通过去除不必要的返回字段可以提高性能,例:

调整前:select * from product where company_id=?;

调整后:select id,name from product where company_id=?;

优点:

1、减少数据在网络上传输开销

2、减少服务器数据处理开销

3、减少客户端内存占用

4、字段变更时提前发现问题,减少程序BUG

5、如果访问的所有字段刚好在一个索引里面,则可以使用纯索引访问提高性能。

缺点:增加编码工作量

三、减少交互次数
1、batch DML

如果插入1000条数据

通过for循环一条一条进行操作,那么需要和数据库建立1000次连

假如每次插入10条数据,那么需要建立100次连接接

假如每次插入100条数据,那么需要建立10次连接

那么是不是每次插入的数据量越多越好呢?不是的,批量插入的时候需要建立临时表,批量插入的数据越多,那么占用的空间越大,有内存溢出的风险

总的来说,适当地调节插入、修改、删除的批量操作的数量,可以降低数据库的连接次数,减少了网络通信使用的时间

mybatis plus也提供了批量操作的接口,默认单次处理的数据为1000条

2、In List

尽量不循环查询数据库,可以使用in条件批量查询,如果需要分离数据,可以使用程序分离,这样不仅减低数据库的连接次数,而且在没有索引的情况下降低了扫描数据库的次数

3、设置Fetch Size

数据查询出来需要从数据库传输到实体类中存储起来,这中间传输的时候需要建立连接通道,传输的速率有通道的大小决定,通道的大小可以通过调整Fetch Size调节,如果通道太小,传输数据就会很慢,如果过大,会浪费空间,有内存溢出的风险。所以调整Fetch Size优化性能的时候需要对数据量进行评估

mybatis可以使用fetchSize属性调节大小,网络上大家都说fetchSize设置在40-100比较合适,oracle默认是10,所以取10000条数据的时候就需要花费10秒钟,需要适当提高fetchSize来提高传输速度

4、使用存储过程

使用存储过程可以在一次连接做许多复杂的逻辑操作,查出的数据直接在数据库中处理,由此可以去掉连接和传输的时间花销,例如将一个表的数据处理后存到另一个表,传统的做法是取出数据到客户端,处理后再用insert语句插入到另一个表中,而存储过程却可以一步到位,直接取数据,处理,插入。最大的优点就是减少了数据在网络上传输开销

存储过程的收益并没有很大,又不容易维护,所以在实际的生产中很少使用,定时性的ETL任务或报表统计函数使用得比较多

相关推荐
qq_5298353532 分钟前
对计算机中缓存的理解和使用Redis作为缓存
数据库·redis·缓存
月光水岸New3 小时前
Ubuntu 中建的mysql数据库使用Navicat for MySQL连接不上
数据库·mysql·ubuntu
狄加山6753 小时前
数据库基础1
数据库
我爱松子鱼3 小时前
mysql之规则优化器RBO
数据库·mysql
chengooooooo3 小时前
苍穹外卖day8 地址上传 用户下单 订单支付
java·服务器·数据库
Rverdoser4 小时前
【SQL】多表查询案例
数据库·sql
Galeoto4 小时前
how to export a table in sqlite, and import into another
数据库·sqlite
人间打气筒(Ada)5 小时前
MySQL主从架构
服务器·数据库·mysql
leegong231115 小时前
学习PostgreSQL专家认证
数据库·学习·postgresql
喝醉酒的小白5 小时前
PostgreSQL:更新字段慢
数据库·postgresql