首页 > 数据库 >SQL优化

SQL优化

时间:2024-08-31 22:25:59浏览次数:10  
标签:优化 查询 索引 user SQL 分表 where id

QL优化的核心在于降低查询的执行时间和资源消耗,系统的稳定。
SQL优化:
1、SQL语句的优化
2、SQL索引的优化(关键)
3、SQL数据表优化
4、SQL数据库优化

SQL语句的优化

1、不要用*,可能用到覆盖索引,减少回表,提高查询效率。
SELECT * FROM user;
SELECT id,username FROM user;

2、不要在where子句中使用or来连接条件,会使索引失效,从而全表扫描;要么使用UNION ALL或者分开写SQL。
SELECT * FROM user WHERE id=1 OR salary=5000;
SELECT * FROM user WHERE id=1
UNION ALL
SELECT*FROMuserWHEREsalary=5000;

3、尽量使用数值替代字符串类型
主键(id):primary key优先使用数值类型int,tinyint
性别(sex):0代表女,1代表男;数据库没有布尔类型,mysql推荐使用tinyint
因为引擎在处理查询和连接时会逐个比较字符串中每一个字符;
而对于数字型而言只需要比较一次就够了;
字符会降低查询和连接的性能,并会增加存储开销。

4、使用varchar代替char
addr char(100) DEFAULT NULL COMMENT'地址';
addr varchar(100) DEFAULT NULL COMMENT '地址';
varchar字段按数据内容实际长度存储,存储空间小,可以节省存储空间;
char按声明大小存储,不足补空格;
对于查询来说,在一个相对较小的字段内搜索,效率更高;

5、where中使用默认值代替null
SELECT * FROM user WHERE age IS NOT NULL;
SELECT * FROM user WHERE age>0;
SELECT * FROM user WHERE salary!=5000;
SELECT * FROM user WHERE salary<>5000;
并不是说使用了is null或者 is not null就会不走索引了,这个跟mysql版本以及查询成本都有关;
如果mysql优化器发现,走索引比不走索引成本还要高,就会放弃索引,这些条件 !=,<>,is null,is not null经常被认为让索引失效;
其实是因为一般情况下,查询的成本高,优化器自动放弃索引的;
如果把null值,换成默认值,很多时候让走索引成为可能,同时,表达意思也相对清晰一点;

6、inner join 、left join、right join,优先使用inner join
inner join 内连接,只保留两张表中完全匹配的结果集;返回的行数比较少,所以性能相对会好一点;
left join会返回左表所有的行,即使在右表中没有匹配的记录;
所以左边表数据结果尽量小,条件尽量放到左边处理,意味着返回的行数可能比较少;
这是mysql优化原则,就是小表驱动大表,小的数据集驱动大的数据集,从而让性能更优;
right join会返回右表所有的行,即使在左表中没有匹配的记录;优化同了left join

7、提高group by语句的效率
先分组,再过滤(不推荐)
select job,avg(salary)from employee group by job having job='develop' or job='test';
先过滤,后分组
select job,avg(salary)from employee where job='develop' or job='test' group by job;
可以在执行到该语句前,把不需要的记录过滤掉

8、清空表时优先使用truncate
truncate table在功能上与不带 where子句的 delete语句相同:二者均删除表中的全部行。但 truncate table比 delete速度快,且使用的系统和事务日志资源少。
delete语句每次删除一行,并在事务日志中为所删除的每行记录一项。truncate table通过释放存储表数据所用的数据页来删除数据,并且只在事务日志中记录页的释放。
truncate table删除表中的所有行,但表结构及其列、约束、索引等保持不变。新行标识所用的计数值重置为该列的种子。如果想保留标识计数值,请改用 DELETE。如果要删除表定义及其数据,请使用 drop table语句。
对于由 foreign key约束引用的表,不能使用 truncate table,而应使用不带 where子句的 DELETE 语句。由于 truncate table不记录在日志中,所以它不能激活触发器。
truncate table不能用于参与了索引视图的表。

9、批量插入性能提升
INSERT INTO user(id,username)VALUES(1,'张三');
INSERT INTO user(id,username)VALUES(2,'李四');
INSERT INTO user(id,username)VALUES(1,'张三'),(2,'李四');
默认新增SQL有事务控制,导致每条都需要事务开启和事务提交,而批量处理是一次事务开启和提交,效率提升明显,达到一定量级,效果显著,平时看不出来。

10、索引不宜太多,一般5个以内
索引并不是越多越好,虽其提高了查询的效率,但却会降低插入和更新的效率;
索引可以理解为一个就是一张表,其可以存储数据,其数据就要占空间;
索引表的数据是排序的,排序也是要花时间的;
insert或update时有可能会重建索引,如果数据量巨大,重建将进行记录的重新排序,所以建索引需要慎重考虑,视具体情况来定;
一个表的索引数最好不要超过5个,若太多需要考虑一些索引是否有存在的必要;

11、不要在索引列上使用内置函数
SELECT * FROM user WHERE DATE_ADD(birthday,INTERVAL7DAY)>=NOW;
SELECT * FROM user WHERE birthday>=DATE_ADD(NOW,INTERVAL7DAY);
不要在子句中的“=”左边进行函数、算术运算或其他表达式运算,否则索引失效。

12、复合索引最左特性
创建复合索引
ALTERTABLEemployeeADDINDEXidx_name_salary(name,salary)
满足复合索引的最左特性,哪怕只是部分,复合索引生效
SELECT*FROMemployeeWHERENAME='编程'
没有出现左边的字段,则不满足最左特性,索引失效
SELECT*FROMemployeeWHEREsalary=5000
复合索引全使用,按左侧顺序出现 name,salary,索引生效
SELECT*FROMemployeeWHERENAME='编程'ANDsalary=5000
虽然违背了最左特性,但MySQL执行SQL时会进行优化,底层进行颠倒优化
SELECT*FROMemployeeWHEREsalary=5000ANDNAME='编程'
复合索引也称为联合索引,当我们创建一个联合索引的时候,如(k1,k2,k3),相当于创建了(k1)、(k1,k2)和(k1,k2,k3)三个索引,这就是最左匹配原则。联合索引不满足最左原则,索引一般会失效。

13、优化like语句
模糊查询,程序员最喜欢的就是使用like,但是like很可能让你的索引失效。
select * from citys where name like'%大连'(不使用索引)
select * from citys where name like'%大连%'(不使用索引)
select * from citys where name like '大连%'(使用索引)。
首先尽量避免模糊查询,如果必须使用,不采用全模糊查询,也应尽量采用右模糊查询, 即like ‘…%’,是会使用索引的;
左模糊like ‘%...’无法直接使用索引,但可以利用reverse + function index的形式,变化成 like ‘…%’;
全模糊查询是无法优化的,一定要使用的话建议使用搜索引擎。

14、count(*)推荐使用
count(*) :它会获取所有行的数据,不做任何处理,行数加1。
count(1):它会获取所有行的数据,每行固定值1,也是行数加1。
count(id):id代表主键,它需要从所有行的数据中解析出id字段,其中id肯定都不为NULL,行数加1。
count(普通索引列):它需要从所有行的数据中解析出普通索引列,然后判断是否为NULL,如果不是NULL,则行数+1。
count(未加索引列):它会全表扫描获取所有数据,解析中未加索引列,然后判断是否为NULL,如果不是NULL,则行数+1。
由此,最后count的性能从高到低是:
count(*) ≈ count(1) > count(id) > count(普通索引列) > count(未加索引列)
可以为对应表key_len较小的列建立二级索引,以优化count(*)执行效率

**15、 慎用 IN 和 NOT IN**
IN 和 NOT IN 也要慎用,否则会导致全表扫描。对于连续的数值,能用 BETWEEN 就不要用 IN
select id from t where num between 1 and 3;
**注意:**
select id from t where num not in (1,2,null);
如果查询集合中有null,不适合用not in,容易出错(查询结果为空)。
为什么not in (1,2,null)会出错呢?
因为not in (1,2,null) 等价于x!=1 and x!=2 and x!=null ,**而在sql中,对于任意x,x!=null永远为false**,所以整体结果永远为false,所以查询结果永远为空。
**解决方法**
(1)仍使用not in ,但修改sql语句。
将in后的查询结果中的null过滤掉。
select name from a where name not in (
select name from b where name is not null
);
(2)使用not exists
共有的不要了,要独有的。
select * from a
where not exists(
select 1 from b where a.col = b.col
);


16、选择 exists 代替 in
**in**先查询子查询的表,然后将内表和外表做一个笛卡尔积,然后按照条件进行筛选。子查询为驱动表
当在两表查询的时候, 外表是大表(数据量多),内表是小表(数据量少),把外表(A)和内表(B)做hash连接,,先查询内表,再把内表结果与外表匹配,对外表使用索引(外表效率高),而内表都需要查询,不可避免,故外表大的使用in,可加快效率。
**exists**先执行主查询,再根据主查询的结果,执行子查询。主查询为驱动表
当在两表查询的时候, 外表是小表(数据量少),内表是大表(数据量多), 对外表做loop循环,每次loop循环再对内表进行查询,对内表的查询可使用索引(内表效率高),而外表大都需要遍历,不可避免(尽量用小表),故内表大的使用exists,可加快效率;
当子查询的结果较小,且主查询的表较大且有索引时,应用in。
当主查询的结果较小,而子查询的表较大且有索引时,应用exists。
SQL必须遵循“小表驱动大表“的原则
表A(小表),表B(大表)
select * from A where id in(select id from B)  -->效率低,用到了A表上id列的索引;
select * from A where exists(select id from B where id =A.id )  -->效率高,用到了B表上id列的索引。
select * from B where id in(select id from A)  -->效率高,用到了B表上id列的索引
select * from B where exists(select id from A where id=B.id)  -->效率低,用到了A表上id列的索引。

17、where后面的字段,留意其数据类型的隐式转换。
SELECT *FROM user WHERE NAME=110;
因为不加单引号时,是字符串跟数字的比较,它们类型不匹配;
MySQL会做隐式的类型转换,把它们转换为数值类型再做比较;

18、为 WHERE 及 ORDER BY 涉及的列上建立索引
对查询进行优化,应尽量避免全表扫描,首先应考虑在 WHERE 及 ORDER BY 涉及的列上建立索引

19、选择重复值较低的字段建索引
在创建索引时,一定要选择重复值较低的字段。离散型非常的差,优化器可能直接就选择不走索引了,因为优化器可能认为,走索引和全表扫描差不多。值分布很稀少的字段不适合建索引,例如"性别"这种只有两三个值的字段不适合做索引。

20、join使用问题
尽量不要使用子查询,表设计好很关键

21、asc和desc混用,导致索引失效
select * from_t where a=1 order by b desc, c asc;

22、在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log , 以提高速度;
如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。

SQL索引的优化-执行计划EXPLAIN
EXPLAIN可以分析SQL问题,写出更优质的SQL查询语句。使用方法,在select语句前加上EXPLAIN即可

EXPLAIN返回参数(重点关注加粗):
(1)id:执行计划中每个操作的唯一标识,多表联查的时候会出现多条数据id是一样的
(2)select_type:查询类型,常见类型有:SIMPLE、PRIMARY、UNION、SUBQUERY
(3)table:涉及到的表
(4)partitions:涉及到的分区
(5)type:查询时使用到的索引类型:优>差:system>const>eq_ref>ref>range>index>ALL
system:系统表
const:常数索引,通常情况下,如果将一个主键放置到where后面作为条件查询,mysql优化器就能把这次查询优化转化为一个常量
eq_ref:唯一索引,使用primary key 或者unique key 索引的所有部分,只扫描索引树种的一个一个匹配行,返回唯一一条数据
ref:非唯一索引,而是使用普通索引或者唯一索引的部分前缀,只扫描索引树种一部分数据来查找匹配行,可能返回多条数据
range:范围索引,只扫描索引树种的一个范围来查找匹配的行
index:全索引扫描,使用某个二级索引,遍历索引树
ALL:全表扫描
(6)possible_keys:可能被查询优化器使用到的索引
(7)key:查询优化器选择的索引,会显示实际使用到的索引名称
(8)key_len:索引长度
(9)ref:索引的那一列被使用
(10)rows:估计要读取并检测的行数
(11)filtered:按表条件过滤的行百分比
(12)Extra:额外信息
Using where:表示使用了where过滤,过滤条件字段无索引 。
Using index:使用了覆盖索引,只需要扫描索引,无需回表检索(推荐)
using index condition:查询的列被索引覆盖,where筛选条件是索引列之一,确实命中了索引,但不是所有的列数据都在索引树上
Using temporary:创建了临时表用来存储结果,排序或者分组的情况下,需要创建索引(典型:group by和order by同时存在,且作用于不同的字段时,就会建立临时表)
Using filesort:表示(order by列)没有使用索引的排序,数据较小时从内存排序,否则从磁盘完成,需要创建索引
Using join buffer (Block Nested Loop):需要进行嵌套循环计算,这类SQL语句性能往往也较低,需要进行优化。典型的,两个关联表join,关联字段均未建立索引,就会出现这种情况。常见的优化方案是,在关联字段上添加索引,避免每次嵌套循环计算。

SQL数据库、表优化
分库分表是数据库设计中的一个重要策略,用于处理单个数据库实例无法处理的大量数据和高并发的情况。
1、分表

(1)纵向分表
就是把一张表中的字段,切分到多张表。
分表理由:表的字段太多,根据数据的活跃度进行分离(因为不同活跃的数据,处理方式是不同的)
案例:
对于一个博客系统,文章标题,作者,分类,创建时间等,是变化频率慢,查询次数多,而且最好有很好的实时性的数据,我们把它叫做冷数据。而博客的浏览量,回复数等,类似的统计信息,或者别的变化频率比较高的数据,我们把它叫做活跃数据。所以,在进行数据库结构设计的时候,就应该考虑分表,首先是 纵向分表的处理。
纵向分表后:
首先存储引擎的使用不同,冷数据使用MyIsam 可以有更好的查询数据。活跃数据,可以使用Innodb ,可以有更好的更新速度。
其次,对冷数据进行更多的从库配置,因为更多的操作是查询,这样来加快查询速度。对热数据,可以相对有更多的主库的横向分表处理。
其实,对于一些特殊的活跃数据,也可以考虑使用memcache ,redis之类的缓存,等累计到一定量再去更新数据库。或者mongodb一类的nosql 数据库。
(2)横向分表
就是是把大的单表结构,横向切割为同样结构的多表,如,用户信息表,user_1,user_2 等。表结构是完全一样,但是,根据某些特定的规则来划分的表,如根据用户ID来取模划分。
分表理由:根据数据量的规模来划分,保证单表的容量不会太大,从而来保证单表的查询等处理能力。
案例:
对于一个博客系统。当博客的量达到很大时候,就应该采取横向分割来降低每个单表的压力,来提升性能。例如博客的冷数据表,假如分为100个表,当同时有100万个用户在浏览时,如果是单表的话,会进行100万次请求,而现在分表后,就可能是每个表进行1万个数据的请求(因为,不可能绝对的平均,只是假设),这样压力就降低了很多很多。
2、分库
同分表策略

3、分库分表
分表+分库

4、分库分表采用的策略
(1) Range 范围
按某个字段的数据区间来进行切分。
比如:user表按照 user_id 的数据范围切分成多张表,每 1000万条数据存放一张表,切分后的表可以放到同一个数据库,也可以放 到不同的数据库,示例图如下

优点:
方便扩容,每次数据量达到 range值就新加一张表,可以通过代码实现自动化扩容;
缺点:
存在写偏移,可能有热点问题;
用户注册场景:user表,因为新注册的用户数据都是写新表,通常来说新用户的活跃度高,所以读写流量全部集中在最新的 user表,因此,新表可能存在热点问题。
(2)hash切分
通过对分表键 key 进行一定的运算(通常有取余、取模运算,比如:key % m,key / m,hash(key)/m 等等),通过运算结果来决定路由的库和表。目前大多数互联网公司主要采用该方法。
比如:user表信息,根据 user_id 对 10 取余,这样就可以通过 user_id 尾号 hash 到 user_0 到 user_9 10张表中:

优点:
数据分片比较均匀,大大降低热点问题;
缺点:
hash 算法选择不合理,后期扩容可能需要迁移数据;
数据被切分到不同的库和表中,可能存在跨节点查询和分页等问题;
(3)映射表
映射表其实是 Range范围 和 hash切分的混合模式,将分表键和数据库的映射关系记录在一个单独的表(表的形式可以是 数据库表,文件或者配置中心)。
某社区电商下单场景,因为全国仓库的数量有限,所以分库直接使用了仓编编码-数据库映射表(后期新增加仓库,只要在表中增加映射关系),为了保证履约的时效性,用户下单时,商城端会选择最近的仓库,服务器在映射表中根据仓库编码查询并路由到对应的数据库,最后在库中进行 order表的操作,交互如下图:

优点:
可以灵活设置路由规则;
缺点:
方案比较复杂;
映射表可能也会随着业务量的增大,同样需要分库分表,带来更多的问题;
5、分库分表的问题
(1)调试和维护难度
单库单表,可以很直观在表中查看数据,分库分表后,需要先根据 key找到库和表,这样在一定意义上增加了开发人员定位问题的难度,再因为库和表的增多,维护难度自然也上去了(公司有DBA可以交给他们)。
(2)分布式ID
单库单表,可以直接使用表自增主键保证全局唯一性,分库分表后,需要自己维护全局唯一的ID,常用的算法有:UUID、号段模式(数据库生成全局ID)、雪花算法。
对于公司内部没有分布式ID相关实现的,可以使用或借鉴 美团开源的Leaf ,该框架提供了雪花算法和号段模式两种方案。
(3)分布式事务
业务划分的时候规避分布式事务;
使用专业的的分布式框架,比如阿里开源的 Seata、Redission redis分布式锁;
(4)跨库关联/分页/排序
单库单表可以直接使用 MySQL limit 特性实现分页,分库分表后,可能会出现分页问题,解决方案有三种:
选择合适的分表字段,规避绝大部分高频查询场景出现跨库;
使用专业的分布式框架,比如开源框架:ElasticSearch;
业务代码中分别查询,然后组装数据;
(5)分库分表工具
客户端模式
客户端模式是指在客户端实现直连数据库,客户端通常是通过一些封装好的 jar来实现,常见的开源中间件有:Apache的Sharding-JDBC、淘宝的TDDL、美图的Zebra。

代理模式
代理模式是指需要单独部署服务,客户端连接代理服务,由代理服务再和数据库交互。
常见的开源中间件有:Apache的 Sharding-Proxy、阿里的 cobar、国产的 MyCat、360的 Atlas、 google的 vitess,它是基于 zookeeper,通过 RPC方式进行数据管理。

总结
两种方案的核心思想都是类似的,都是将分库分表的逻辑进行抽象封装,业务无需关注分库分表的实现细节,只需按照规则进行简单的配置和开发,就能正常的使用分库分表。
客户端模式比较轻量,性能也会比较好;代理模式需要部署额外的服务器,所以对于该服务器的稳定性和性能等都需要保障。

标签:优化,查询,索引,user,SQL,分表,where,id
From: https://www.cnblogs.com/lhboke/p/18390866

相关文章

  • MySql数据库卸载与安装
    MySql数据库卸载与安装数据库卸载卸载mysql服务打开控制面板-->卸载MySql删除mysql文件夹打开我的电脑-->C盘-->打开隐藏的文件夹ProgramData-->删除MySql文件夹删除注册表windows+r打开运行命令-->regedit(打开注册表)-->HKTY_LOCAL_MACHINE-->SYSTEM只要发现带有ControlSet的文......
  • 如何优化 Apache Tomcat 上的接口性能
    为了更好地说明如何优化ApacheTomcat上的接口性能,我们将结合代码示例和一张简化的架构图来阐述优化方案。请注意,由于文本环境的限制,无法直接插入图片,但我将描述一张可能的架构图,并提供相应的代码示例。架构图描述假设我们有一个典型的三层架构应用,包括前端(Web层)、中间层(业务逻......
  • 面试SQL题的水到底有多深?一文带你揭晓
    不谋万世者,不足谋一时;不谋全局者,不足谋一域目录0面试现状1面试SQL题目的难度及特点1.1题目场景化1.2题目算法化1.3方法多元化2破局之道3总结数字化建设通关指南主要内容:(1)SQL进阶实战技巧(2)数仓建模实战技巧和个人心得0面试现状  最近有不少参加面......
  • 多线程篇(基本认识 - 锁优化)(持续更新迭代)
    目录一、前言二、阿里开发手册三、synchronized锁优化的背景四、Synchronized的性能变化1.Java5之前:用户态和内核态之间的切换2.java6开始:优化Synchronized五、锁升级1.无锁2.偏向锁2.1.前言2.2.什么是偏向锁2.3.偏向锁的工作过程2.4.为什么要引入偏向锁......
  • SQL注入总结
    一、万能密码:什么是万能密码?用户进行用户名和密码验证时,网站需要查询数据库。查询数据库就是执行SQL语句。用户登录时,后台执行的数据库查询操作(SQL语句)是:【Selectuser_id,user_type,emailFromusersWhereuser_id=’用户名’Andpassword=’密码’】这里我们......
  • Sql Server 2005 获取表结构信息
    SELECT表名 =CASEa.colorderWHEN1THENc.nameELSE''END,    序  =a.colorder,    字段名=a.name,    标识 =CASECOLUMNPROPERTY(a.id,a.name,'IsIdentity')WHEN1THEN'√'ELSE''END,    主键 =CASE ......
  • 9 张图总结 MySQL 架构
    原文:9张图总结一下MySQL架构前言目前大部分的后端开发人员对MySQL的理解可能停留在一个黑盒子阶段。对MySQL基本使用没什么问题,比如建库、建表、建索引,执行各种增删改查。所有很多后端开发人员眼中的MySQL如下图所示:导致在实际工作中碰到MySQL中死锁异常、SQL性能太差......
  • 【SQL注入】代码安全审计经验分享
    一、MyBatis框架中的注入漏洞Mybatis框架支持的CURD功能可以直接搜索XML文件中的${和${}拼接的SQL语句,如果SQL的参数可控,就可能造成注入风险。另外,有的SQL语句使用的是注解开发,把SQL语句可以直接写在了代理接口方法上方,审计的时候可以将两种情况都注意一下,或许有不同的发现。......
  • MySQL教程:从基础到实战
    引言MySQL是一个流行的开源关系型数据库管理系统(RDBMS),广泛应用于各种Web应用程序和服务器中。本教程将详细介绍MySQL的基础知识,并通过实战作业帮助你巩固所学内容。MySQL的作用?/为什么要学MySQL作为一款流行的开源关系型数据库管理系统(RDBMS),具有高效、可靠、易用和灵活的特......
  • 【基于python tkinter的本地音乐播放器优化版】
    系列文章目录本地音乐播放器初版本地小说阅读器初版本地小说阅读器优化版文章目录系列文章目录前言一、功能介绍二、使用注意项三、界面展示1)菜单分类:2)关键字搜索3)拓展项4)临时列表四、附件前言前面写了一文,学习开发本地音乐播放器,小试牛刀,界面小气、粗糙且羞涩......