戳蓝字“CSDN云计算”关注我们哦!
技术头条:干货、简洁、多维全面。更多云计算精华知识尽在眼前,get要点、solve难题,统统不在话下!
作者:ITFLY8
转自:架构师技术联盟
面对大量用户访问、高并发请求,海量数据,可以使用高性能的服务器、大型数据库,存储设备,高性能Web服务器,采用高效率的编程语言比如(Go,Scala)等,当单机容量达到极限时,我们需要考虑业务拆分和分布式部署,来解决大型网站访问量大,并发量高,海量数据的问题。
从单机网站到分布式网站,很重要的区别是业务拆分和分布式部署,将应用拆分后,部署到不同的机器上,实现大规模分布式系统。分布式和业务拆分解决了,从集中到分布的问题,但是每个部署的独立业务还存在单点的问题和访问统一入口问题,为解决单点故障,我们可以采取冗余的方式。将相同的应用部署到多台机器上。解决访问统一入口问题,我们可以在集群前面增加负载均衡设备,实现流量分发。
负载均衡(Load Balance),意思是将负载(工作任务,访问请求)进行平衡、分摊到多个操作单元(服务器,组件)上进行执行。是解决高性能,单点故障(高可用),扩展性(水平伸缩)的终极解决方案。
本文是负载均衡详解的第一篇文章,介绍负载均衡的原理,负载均衡分类(DNS负载均衡,HTTP负载均衡,IP负载均衡,链路层负载均衡,混合型P负载均衡)。部分内容摘自读书笔记。
一、负载均衡原理
系统的扩展可分为纵向(垂直)扩展和横向(水平)扩展。纵向扩展,是从单机的角度通过增加硬件处理能力,比如CPU处理能力,内存容量,磁盘等方面,实现服务器处理能力的提升,不能满足大型分布式系统(网站),大流量,高并发,海量数据的问题。因此需要采用横向扩展的方式,通过添加机器来满足大型网站服务的处理能力。比如:一台机器不能满足,则增加两台或者多台机器,共同承担访问压力。这就是典型的集群和负载均衡架构:如下图:
应用集群:将同一应用部署到多台机器上,组成处理集群,接收负载均衡设备分发的请求,进行处理,并返回相应数据。
负载均衡设备:将用户访问的请求,根据负载均衡算法,分发到集群中的一台处理服务器。(一种把网络请求分散到一个服务器集群中的可用服务器上去的设备)
负载均衡的作用(解决的问题):
1.解决并发压力,提高应用处理性能(增加吞吐量,加强网络处理能力);
2.提供故障转移,实现高可用;
3.通过添加或减少服务器数量,提供网站伸缩性(扩展性);
4.安全防护;(负载均衡设备上做一些过滤,黑白名单等处理)
二、负载均衡分类
根据实现技术不同,可分为DNS负载均衡,HTTP负载均衡,IP负载均衡,链路层负载均衡等。
2.1 DNS负载均衡
最早的负载均衡技术,利用域名解析实现负载均衡,在DNS服务器,配置多个A记录,这些A记录对应的服务器构成集群。大型网站总是部分使用DNS解析,作为第一级负载均衡。如下图:
优点
使用简单:负载均衡工作,交给DNS服务器处理,省掉了负载均衡服务器维护的麻烦
提高性能:可以支持基于地址的域名解析,解析成距离用户最近的服务器地址,可以加快访问速度,改善性能;
缺点
可用性差:DNS解析是多级解析,新增/修改DNS后,解析时间较长;解析过程中,用户访问网站将失败;
扩展性低:DNS负载均衡的控制权在域名商那里,无法对其做更多的改善和扩展;
维护性差:也不能反映服务器的当前运行状态;支持的算法少;不能区分服务器的差异(不能根据系统与服务的状态来判断负载)
实践建议
将DNS作为第一级负载均衡,A记录对应着内部负载均衡的IP地址,通过内部负载均衡将请求分发到真实的Web服务器上。一般用于互联网公司,复杂的业务系统不合适使用。如下图:
2.2 IP负载均衡
在网络层通过修改请求目标地址进行负载均衡。
用户请求数据包,到达负载均衡服务器后,负载均衡服务器在操作系统内核进程获取网络数据包,根据负载均衡算法得到一台真实服务器地址,然后将请求目的地址修改为,获得的真实ip地址,不需要经过用户进程处理。
真实服务器处理完成后,响应数据包回到负载均衡服务器,负载均衡服务器,再将数据包源地址修改为自身的ip地址,发送给用户浏览器。如下图:
IP负载均衡,真实物理服务器返回给负载均衡服务器,存在两种方式:(1)负载均衡服务器在修改目的ip地址的同时修改源地址。将数据包源地址设为自身盘,即源地址转换(snat)。(2)将负载均衡服务器同时作为真实物理服务器集群的网关服务器。
优点:
(1)在内核进程完成数据分发,比在应用层分发性能更好;
缺点:
(2)所有请求响应都需要经过负载均衡服务器,集群最大吞吐量受限于负载均衡服务器网卡带宽;
2.3 链路层负载均衡
在通信协议的数据链路层修改mac地址,进行负载均衡。
数据分发时,不修改ip地址,指修改目标mac地址,配置真实物理服务器集群所有机器虚拟ip和负载均衡服务器ip地址一致,达到不修改数据包的源地址和目标地址,进行数据分发的目的。
实际处理服务器ip和数据请求目的ip一致,不需要经过负载均衡服务器进行地址转换,可将响应数据包直接返回给用户浏览器,避免负载均衡服务器网卡带宽成为瓶颈。也称为直接路由模式(DR模式)。如下图:
优点:性能好;
缺点:配置复杂;
实践建议:DR模式是目前使用最广泛的一种负载均衡方式。
2.4 混合型负载均衡
由于多个服务器群内硬件设备、各自的规模、提供的服务等的差异,可以考虑给每个服务器群采用最合适的负载均衡方式,然后又在这多个服务器群间再一次负载均衡或群集起来以一个整体向外界提供服务(即把这多个服务器群当做一个新的服务器群),从而达到最佳的性能。将这种方式称之为混合型负载均衡。
此种方式有时也用于单台均衡设备的性能不能满足大量连接请求的情况下。是目前大型互联网公司,普遍使用的方式。
方式一,如下图:
以上模式适合有动静分离的场景,反向代理服务器(集群)可以起到缓存和动态请求分发的作用,当时静态资源缓存在代理服务器时,则直接返回到浏览器。如果动态页面则请求后面的应用负载均衡(应用集群)。
方式二,如下图:
以上模式,适合动态请求场景。
因混合模式,可以根据具体场景,灵活搭配各种方式,以上两种方式仅供参考。
三、负载均衡算法
常用的负载均衡算法有,轮询,随机,最少链接,源地址散列,加权等方式;
3.1 轮询
将所有请求,依次分发到每台服务器上,适合服务器硬件同相同的场景。
优点:服务器请求数目相同;
缺点:服务器压力不一样,不适合服务器配置不同的情况;
3.2 随机
请求随机分配到各个服务器。
优点:使用简单;
缺点:不适合机器配置不同的场景;
3.3 最少链接
将请求分配到连接数最少的服务器(目前处理请求最少的服务器)。
优点:根据服务器当前的请求处理情况,动态分配;
缺点:算法实现相对复杂,需要监控服务器请求连接数;
3.4 Hash(源地址散列)
根据IP地址进行Hash计算,得到IP地址。
优点:将来自同一IP地址的请求,同一会话期内,转发到相同的服务器;实现会话粘滞。
缺点:目标服务器宕机后,会话会丢失;
3.5 加权
在轮询,随机,最少链接,Hash’等算法的基础上,通过加权的方式,进行负载服务器分配。
优点:根据权重,调节转发服务器的请求数目;
缺点:使用相对复杂;
四、硬件负载均衡
采用硬件的方式实现负载均衡,一般是单独的负载均衡服务器,价格昂贵,一般土豪级公司可以考虑,业界领先的有两款,F5和A10。
使用硬件负载均衡,主要考虑一下几个方面:
(1)功能考虑:功能全面支持各层级的负载均衡,支持全面的负载均衡算法,支持全局负载均衡;
(2)性能考虑:一般软件负载均衡支持到5万级并发已经很困难了,硬件负载均衡可以支持
(3)稳定性:商用硬件负载均衡,经过了良好的严格的测试,从经过大规模使用,在稳定性方面高;
(4)安全防护:硬件均衡设备除具备负载均衡功能外,还具备防火墙,防DDOS攻击等安全功能;
(5)维护角度:提供良好的维护管理界面,售后服务和技术支持;
(6)土豪公司:F5 Big Ip 价格:15w~55w不等;A10 价格:55w-100w不等;
缺点
(1)价格昂贵;
(2)扩展能力差;
4.4 小结
(1)一般硬件的负载均衡也要做双机高可用,因此成本会比较高。
(2)互联网公司一般使用开源软件,因此大部分应用采用软件负载均衡;部分采用硬件负载均衡。
比如某互联网公司,目前是使用几台F5做全局负载均衡,内部使用Nginx等软件负载均衡。
福利
扫描添加小编微信,备注“姓名+公司职位”,加入【云计算学习交流群】,和志同道合的朋友们共同打卡学习!
推荐阅读:
极客头条
英特尔强势上新一大波数据产品,小伙伴们“奔走相告”…… | 极客头条
姚期智提出的"百万富翁"难题被破解? 多方安全计算MPC到底是个什么鬼?
全民 AI !教育部宣布 35 所高校新增 AI 本科专业
深度 | 人工智能究竟能否实现?
程序媛报告:调查了 12,000 名女性开发者发现,女性比男性更懂 Java!
程序员怒了!你敢削减专利奖金,我敢拒绝提交代码!

在现代Web应用开发中,MySQL数据库的性能优化是提升应用响应速度和用户体验的关键。本文将分享数据库优化的实战技巧,涵盖数据库设计、索引优化、查询语句优化等多个方面,帮助开发者在实际项目中显著提升数据库性能。
xml 使用 : [笔记] MyBatis-Plus XML 配置详解:从基础到高级,全面提升开发效率MyBatis-Plus 是一个 M - 掘金
一. 数据库设计优化
1. 选择合适的字段类型
设计表时,尽量选择存储空间小的字段类型:
- 整型字段:从
TINYINT
、SMALLINT
、INT
到BIGINT
。 - 小数类型:对于金额等需精确计算的数值使用
DECIMAL
,避免使用FLOAT
和DOUBLE
。 - 字符串:根据实际长度选择
CHAR
(定长)或VARCHAR
(变长)。VARCHAR
不宜超过5000字符;长度需求大的数据建议使用TEXT
,并将大字段拆分到单独的表中
2. 确定字段的合理长度
字段长度表示字符数或字节数,例如VARCHAR(32)
适用于用户名字段(通常为5到20个字符)。建议字段长度设为2的幂,如32、64、128等。
3. 控制表的字段数量
一张表的字段数量尽量控制在20个以内,以避免数据量过大导致查询效率低。如果字段较多,建议分拆为多张表。
4. 尽量定义字段为 NOT NULL
为防止空指针问题,并提升查询性能,除非有特殊需求,字段都应定义为NOT NULL
,可以通过默认值或常量来填充字段。
5. 使用数值类型代替字符串
数值类型占用存储空间小、比较速度更快。
例子: 性别字段建议用数值(如0代表女生,1代表男生)而非字符串(如"WOMEN"、“MAN”)。
6. 评估并添加必要的索引
根据表的数据量和查询需求设置索引:
-
索引数量不宜过多(单表索引个数不超过5个)。
-
区分度低的字段(如性别)不适合建立索引。
-
可通过联合索引优化多列条件查询。
-
定期分析和优化索引
sql代码解读复制代码ANALYZE TABLE user_info_tab;
7. 避免使用MySQL保留字
避免库名、表名或字段名使用MySQL
的保留字(如SELECT
、DESC
等),否则需要使用反引号引用,会增加代码复杂性。
8. 统一字符集的选择
字符集推荐使用utf8
或utf8mb4
以支持中英文及emoji
。其他字符集如GBK
仅适合中文环境,latin1
适合仅支持英文的场景。
9. 数据冗余
在某些场景下,适当的数据冗余可以提高查询效率,例如在读多写少的应用中。
10. 使用分区表
对于大数据量的表,使用分区表可以提高查询性能。
示例:
-
范围分区(Range Partitioning):
- 根据某个字段的值范围进行分区。适用于时间戳、日期等有序字段。
sql代码解读复制代码CREATE TABLE sales ( sale_id INT, sale_date DATE, amount DECIMAL(10,2) ) PARTITION BY RANGE (YEAR(sale_date)) ( PARTITION p2019 VALUES LESS THAN (2020), PARTITION p2020 VALUES LESS THAN (2021), PARTITION p2021 VALUES LESS THAN (2022) );
-
列表分区(List Partitioning):
- 根据某个字段的特定值列表进行分区。适用于类别、地区等离散字段。
sql代码解读复制代码CREATE TABLE employees ( emp_id INT, department VARCHAR(20) ) PARTITION BY LIST (department) ( PARTITION p_sales VALUES IN ('Sales'), PARTITION p_marketing VALUES IN ('Marketing'), PARTITION p_it VALUES IN ('IT') );
-
哈希分区(Hash Partitioning):
- 根据某个字段的哈希值进行分区。适用于均匀分布的数据。
sql代码解读复制代码CREATE TABLE customers ( cust_id INT, name VARCHAR(50) ) PARTITION BY HASH (cust_id) PARTITIONS 4;
-
复合分区(Composite Partitioning):
- 结合多种分区策略,先按一种策略分区,再在每个子分区中按另一种策略分区。适用于复杂的数据分布。
sql代码解读复制代码CREATE TABLE sales ( sale_id INT, sale_date DATE, region VARCHAR(20) ) PARTITION BY RANGE (YEAR(sale_date)) SUBPARTITION BY LIST (region) ( PARTITION p2019 VALUES LESS THAN (2020) ( SUBPARTITION p2019_north VALUES IN ('North'), SUBPARTITION p2019_south VALUES IN ('South') ), PARTITION p2020 VALUES LESS THAN (2021) ( SUBPARTITION p2020_north VALUES IN ('North'), SUBPARTITION p2020_south VALUES IN ('South') ) );
二. 分析与调优
1. 使用 EXPLAIN 分析查询计划
EXPLAIN
可以帮助你了解查询的执行计划,找出潜在的性能瓶颈。
例子:
sql 代码解读复制代码EXPLAIN SELECT * FROM orders WHERE user_id = 123;
2. 使用 PARTITION PRUNING 优化分区表查询
PARTITION PRUNING
可以显著提高分区表的查询性能,因为它可以跳过不需要的分区。
例子:
sql 代码解读复制代码SELECT * FROM sales WHERE sale_date BETWEEN '2023-01-01' AND '2023-12-31';
3. 使用 EXPLAIN ANALYZE 深入分析查询性能
EXPLAIN ANALYZE
可以提供详细的查询执行计划和实际执行时间,帮助你更好地优化查询。
例子:
sql 代码解读复制代码EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 123;
4. 使用 OPTIMIZE TABLE 优化表性能
OPTIMIZE TABLE
可以回收未使用的空间,提高表的性能。
sql 代码解读复制代码OPTIMIZE TABLE users;
5. 使用 ANALYZE TABLE 更新统计信息
ANALYZE TABLE
可以更新表的统计信息,帮助优化器生成更高效的查询计划。
例子:
sql 代码解读复制代码ANALYZE TABLE users;
三. 查询语句优化
1. 避免使用 SELECT *,使用具体字段
反例:
sql 代码解读复制代码SELECT * FROM employee;
正例:
sql 代码解读复制代码SELECT id, name, age FROM employee;
原因: 使用具体字段可以节省资源、减少网络开销,且能避免回表查询。
2. 避免在 WHERE 子句中使用 OR
反例:
sql 代码解读复制代码SELECT * FROM user WHERE userid=1 OR age=18;
正例:
sql 代码解读复制代码-- 使用 UNION ALL
SELECT * FROM user WHERE userid=1
UNION ALL
SELECT * FROM user WHERE age=18;
原因:当 OR
操作符连接的条件涉及多个列时,数据库优化器可能无法有效地使用索引。特别是当这些列上有不同的索引时,优化器可能无法选择最优的索引组合。
3. 避免在 WHERE 子句中使用函数
反例:
sql 代码解读复制代码SELECT * FROM users WHERE UPPER(username) = 'JOHN';
正例:
sql 代码解读复制代码SELECT * FROM users WHERE username = 'JOHN';
原因: 在 WHERE
子句中使用函数会导致索引失效。
4. 避免在 WHERE 子句中对字段进行表达式操作
反例:
sql 代码解读复制代码SELECT * FROM user WHERE age - 1 = 10;
正例:
sql 代码解读复制代码SELECT * FROM user WHERE age = 11;
原因 : 表达式操作会增加额外的计算开销。数据库引擎需要对每一行数据进行表达式计算,然后再进行过滤,这会显著增加查询的执行时间。
5. 使用 LIMIT 避免不必要的数据返回
反例:
sql 代码解读复制代码SELECT id, order_date FROM order_tab WHERE user_id=666 ORDER BY create_date DESC;
正例:
sql 代码解读复制代码SELECT id, order_date FROM order_tab WHERE user_id=666 ORDER BY create_date DESC LIMIT 1;
原因: LIMIT
提升查询效率,避免多余的数据返回。
6. 批量操作(插入、删除、查询)
反例:
sql 代码解读复制代码for(User u : list) {
INSERT INTO user(name, age) VALUES(#name#, #age#);
}
正例:
sql 代码解读复制代码INSERT INTO user(name, age) VALUES
<foreach collection="list" item="item" index="index" separator=",">
(#{item.name}, #{item.age})
</foreach>
原因: 批量插入性能更优。
7. 使用 UNION ALL 替换 UNION(无重复记录时)
反例:
sql 代码解读复制代码SELECT * FROM user WHERE userid=1
UNION
SELECT * FROM user WHERE age=10;
正例:
sql 代码解读复制代码SELECT * FROM user WHERE userid=1
UNION ALL
SELECT * FROM user WHERE age=10;
原因: UNION
会排序和合并,UNION ALL
则省去这一步。
8. 避免在索引列上使用内置函数
反例:
sql 代码解读复制代码SELECT * FROM orders WHERE MONTH(order_date) = 10 AND YEAR(order_date) = 2023;
正例:
sql 代码解读复制代码SELECT * FROM orders WHERE order_date >= '2023-10-01' AND order_date < '2023-11-01';
原因: 索引列上使用函数会导致索引失效。
9. 在 GROUP BY 前进行条件过滤
反例:
sql 代码解读复制代码SELECT user_id, SUM(amount) AS total_amount
FROM orders
GROUP BY user_id
HAVING city = '北京';
正例:
sql 代码解读复制代码SELECT user_id, SUM(amount) AS total_amount
FROM orders
WHERE city = '北京'
GROUP BY user_id;
10. 优化 LIKE 语句
反例:
sql 代码解读复制代码SELECT userId, name FROM user WHERE userId LIKE '%123';
正例:
sql 代码解读复制代码SELECT userId, name FROM user WHERE userId LIKE '123%';
原因: %
放在前面会导致索引失效。
11. 使用小表驱动大表
例子: 假设我们有个客户表和一个订单表。其中订单表有10万记录,客户表只有1000行记录。现在要查询下单过的客户信息,可以这样写:
正例:
sql 代码解读复制代码SELECT * FROM customers c
WHERE EXISTS (
SELECT 1 FROM orders o WHERE o.customer_id = c.id
);
使用 IN
实现:
sql 代码解读复制代码SELECT * FROM customers
WHERE id IN (
SELECT customer_id FROM orders
);
原因: EXISTS
会逐行扫描 customers
表(即小表),对每一行 c.id
,在 orders
表(大表)中检查是否有 customer_id = c.id
的记录。
12. IN 查询的元素不宜太多
反例:
sql 代码解读复制代码SELECT user_id, name FROM user WHERE user_id IN (1,2,3...1000000);
正例: 分批进行,比如每批200个:
sql 代码解读复制代码SELECT user_id, name FROM user WHERE user_id IN (1,2,3...200);
原因: 如果 IN
后面的元素过多,即使后面的条件加了索引,还是会影响性能。
13. 优化 LIMIT 分页
反例:
sql 代码解读复制代码SELECT id, name, balance FROM account WHERE create_time > '2020-09-19' LIMIT 100000, 10;
正例: 使用标签记录法:
sql 代码解读复制代码SELECT id, name, balance FROM account WHERE id > 100000 LIMIT 10;
延迟关联法:
sql 代码解读复制代码SELECT acct1.id, acct1.name, acct1.balance
FROM account acct1
INNER JOIN (
SELECT a.id
FROM account a
WHERE a.create_time > '2020-09-19'
LIMIT 100000, 10
) AS acct2
ON acct1.id = acct2.id;
14. 避免返回过多数据量
反例:
sql 代码解读复制代码SELECT * FROM LivingInfo WHERE watchId = userId AND watchTime >= DATE_SUB(NOW(), INTERVAL 1 YEAR);
正例:
sql 代码解读复制代码-- 分页查询
SELECT * FROM LivingInfo WHERE watchId = userId AND watchTime >= DATE_SUB(NOW(), INTERVAL 1 YEAR) LIMIT offset, pageSize;
原因:
- 查询效率: 当返回的数据量过大时,查询所需的时间会显著增加,导致数据库性能下降。
- 网络传输: 大量数据的传输会占用网络带宽,可能导致网络拥堵和延迟。
- 减少返回的数据量可以降低网络传输的负担,提高数据传输效率。
15. 优先使用连接查询而非子查询
反例:
sql 代码解读复制代码SELECT * FROM customers WHERE id IN (SELECT customer_id FROM orders);
正例:
sql 代码解读复制代码SELECT DISTINCT c.*
FROM customers c
JOIN orders o ON c.id = o.customer_id;
原因: 使用子查询可能会创建临时表。
16. INNER JOIN、LEFT JOIN、RIGHT JOIN,优先使用 INNER JOIN,如果是 LEFT JOIN,左边表结果尽量小
反例:
sql 代码解读复制代码SELECT * FROM tab1 t1 LEFT JOIN tab2 t2 ON t1.size = t2.size WHERE t1.id > 2;
正例:
sql 代码解读复制代码SELECT * FROM (SELECT * FROM tab1 WHERE id > 2) t1
LEFT JOIN tab2 t2 ON t1.size = t2.size;
原因: 如果 INNER JOIN
是等值连接,返回的行数可能较少,性能更好。使用 LEFT JOIN
时,左边表数据结果尽量小,条件尽量放在左边处理。
17. 避免 != 或 <> 操作符
反例:
sql 代码解读复制代码SELECT age, name FROM user WHERE age <> 18;
正例: 分为两个查询:
sql 代码解读复制代码SELECT age, name FROM user WHERE age < 18;
SELECT age, name FROM user WHERE age > 18;
原因: 某些情况下,使用 !=
或 <>
操作符可能导致索引失效,从而影响查询性能。这是因为 !=
和 <>
操作符通常会导致数据库引擎无法高效地利用索引。
18. 使用联合索引时遵循最左匹配原则
例子: 联合索引 (userId, age)
,查询 userId
和 age
时优先使用 userId
。
表结构:
sql 代码解读复制代码CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`userId` int(11) NOT NULL,
`age` int(11) DEFAULT NULL,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_userid_age` (`userId`, `age`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
反例:
sql 代码解读复制代码SELECT * FROM user WHERE age = 10;
正例:
sql 代码解读复制代码SELECT * FROM user WHERE userId = 10 AND age = 10;
正例:
sql 代码解读复制代码SELECT * FROM user WHERE userId = 10;
原因: 使用联合索引时遵循最左匹配原则是非常重要的,这有助于数据库引擎高效地利用索引,从而提高查询性能。最左匹配原则意味着在查询条件中,从联合索引的最左边开始匹配索引列,直到遇到不匹配的列为止。
19. 对 WHERE 和 ORDER BY 涉及的列建索引
反例:
sql 代码解读复制代码SELECT * FROM user WHERE address = '深圳' ORDER BY age;
正例:
sql 代码解读复制代码ALTER TABLE user ADD INDEX idx_address_age (address, age);
20. 使用覆盖索引
正例:
sql 代码解读复制代码SELECT id, name FROM user WHERE userId LIKE '123%';
21. 删除冗余索引
反例:
sql代码解读复制代码KEY `idx_userId` (`userId`) KEY `idx_userId_age` (`userId`, `age`)
正例:
sql 代码解读复制代码-- 删除 `userId` 索引,因为组合索引(A,B)相当于创建了(A)和(A,B)索引
KEY `idx_userId_age` (`userId`, `age`)
原因: 重复的索引需要维护,并且优化器在优化查询的时候也需要逐个地进行考虑,这会影响性能。
22. 使用 INDEX HINTS 强制使用特定索引
例子:
sql 代码解读复制代码SELECT * FROM users USE INDEX (idx_username) WHERE username = 'john';
原因: 在某些情况下,优化器可能选择错误的索引,使用 INDEX HINTS
可以强制使用特定索引。
23. 使用 FULLTEXT 索引进行全文搜索
例子:
sql 代码解读复制代码CREATE FULLTEXT INDEX idx_fulltext ON articles (content);
SELECT * FROM articles WHERE MATCH(content) AGAINST('search term');
原因: FULLTEXT
索引可以提高全文搜索的性能。
24. 避免在 ORDER BY 子句中使用表达式
反例:
sql 代码解读复制代码SELECT * FROM users ORDER BY LENGTH(username);
正例:
sql 代码解读复制代码SELECT * FROM users ORDER BY username;
原因: 在 ORDER BY
子句中使用表达式会导致索引失效。
25. 避免超过3个以上的表连接
原因: 连接表越多,编译的时间和开销也就越大。把连接表拆开成较小的几个执行,可读性更高。
26. 使用 CASE 语句替代复杂的 IF 条件
例子:
sql 代码解读复制代码SELECT
id,
CASE
WHEN age < 18 THEN 'Minor'
WHEN age BETWEEN 18 AND 60 THEN 'Adult'
ELSE 'Senior'
END AS age_group
FROM users;
原因: CASE
语句可以使逻辑更清晰,提高可读性和维护性。
27. 使用 WITH 子句(Common Table Expressions, CTE)
例子:
sql 代码解读复制代码WITH active_users AS (
SELECT id FROM users WHERE status = 'active'
)
SELECT * FROM orders WHERE user_id IN (SELECT id FROM active_users);
原因: CTE
可以使查询结构更清晰,便于理解和维护。
28. 避免使用 DISTINCT 除非必要
反例:
sql 代码解读复制代码SELECT DISTINCT user_id FROM orders;
正例:
sql 代码解读复制代码SELECT user_id FROM orders GROUP BY user_id;
原因: DISTINCT
会进行额外的排序和去重操作,影响性能。如果只需要去重,可以使用 GROUP BY
。
29. 使用 PARTITION PRUNING 优化分区表查询
例子:
sql 代码解读复制代码SELECT * FROM sales WHERE sale_date BETWEEN '2023-01-01' AND '2023-12-31';
原因: PARTITION PRUNING
可以显著提高分区表的查询性能,因为它可以跳过不需要的分区。
四. 补充
1. 合理利用视图(View)进行复杂查询
正例:
sql 代码解读复制代码CREATE VIEW view_user_orders AS
SELECT u.id, u.name, o.order_id, o.amount
FROM user u JOIN orders o ON u.id = o.user_id;
-- 使用视图查询
SELECT * FROM view_user_orders WHERE amount > 100;
2. 使用表分区(Partitioning)优化大表性能
正例:
sql 代码解读复制代码CREATE TABLE sales (
sale_id INT,
sale_date DATE,
amount DECIMAL(10,2)
) PARTITION BY RANGE (YEAR(sale_date)) (
PARTITION p2019 VALUES LESS THAN (2020),
PARTITION p2020 VALUES LESS THAN (2021),
PARTITION p2021 VALUES LESS THAN (2022)
);
3. 合理使用存储过程(Stored Procedure)来减少多次 SQL 交互
正例:
sql 代码解读复制代码CREATE PROCEDURE update_and_select(IN user_id INT)
BEGIN
UPDATE users SET last_login = NOW() WHERE id = user_id;
SELECT * FROM users WHERE id = user_id;
END;
4. 使用临时表(Temporary Tables)处理复杂查询
正例:
sql 代码解读复制代码CREATE TEMPORARY TABLE temp_users AS
SELECT id FROM users WHERE status = 'active';
SELECT * FROM orders WHERE user_id IN (SELECT id FROM temp_users);
原因: 临时表可以在处理复杂查询时提高性能,特别是在多次使用相同子查询结果的情况下。
5. 使用适当的隔离级别
原因: 在高并发环境中选择适当的事务隔离级别(如 READ COMMITTED
),可以避免不必要的锁竞争和阻塞,提升并发效率。
6. 避免在事务中执行非必要的操作
原因: 在事务中应避免执行耗时操作,比如网络请求或复杂计算,以减少锁的持有时间。优先确保事务操作集中在必要的数据变更上。
7. 使用批量更新或删除
正例:
sql 代码解读复制代码-- 分批删除
DELETE FROM orders WHERE status = 'obsolete' LIMIT 1000;
评论记录:
回复评论: