文章目录
- [一. 概念](#一. 概念)
- [二. 一般流程](#二. 一般流程)
-
- [1. 性能测试](#1. 性能测试)
- [2. 瓶颈分析](#2. 瓶颈分析)
- [3. 调优手段](#3. 调优手段)
- [4. 注意事项](#4. 注意事项)
- [三. 数据库优化](#三. 数据库优化)
-
- [1. 硬件配置-升级硬件](#1. 硬件配置-升级硬件)
- [2. 表设计优化](#2. 表设计优化)
- [3. 索引优化](#3. 索引优化)
- [4. 分库分表-适用于数据量较大的表](#4. 分库分表-适用于数据量较大的表)
-
- [1. 分库](#1. 分库)
-
- [1. 概念](#1. 概念)
- [2. 分库策略](#2. 分库策略)
-
- [1. 按业务模块分库](#1. 按业务模块分库)
- [2. 按地区分库](#2. 按地区分库)
- [2. 水平分表-根据数据行拆分](#2. 水平分表-根据数据行拆分)
- 3.垂直分表-根据字段拆分
- [4. 实战:-shardingsphere(开源的分布式数据库中间件)](#4. 实战:-shardingsphere(开源的分布式数据库中间件))
-
- [1. 架构](#1. 架构)
- [2. 核心功能](#2. 核心功能)
- [3. 分片规则](#3. 分片规则)
- [5. 分开分表可能带来的问题](#5. 分开分表可能带来的问题)
-
- [1. 事务一致性问题](#1. 事务一致性问题)
- [2. 跨节点关联查询join问题](#2. 跨节点关联查询join问题)
- [3. 全局唯一ID问题](#3. 全局唯一ID问题)
- [4. 分布式事务问题](#4. 分布式事务问题)
- [5. 数据缓存](#5. 数据缓存)
- [6. 查询语句优化](#6. 查询语句优化)
- [四. 适合加索引的字段以及索引失效的场景](#四. 适合加索引的字段以及索引失效的场景)
-
- [1. 适合加索引的字段](#1. 适合加索引的字段)
- [2. 不适合加索引的字段](#2. 不适合加索引的字段)
- [3. 索引失效的场景](#3. 索引失效的场景)
一. 概念
通过各种手段优化软件系统的性能,以达到我们预期的运行速度、响应时间、资源使用效率。
二. 一般流程
1. 性能测试
通过性能测试工具(Jmeter、LoadRunner)对系统进行压力测试,获取基本数据.
2. 瓶颈分析
根据测试结果,定位性能瓶颈-cpu、内存、I/O操作、数据库查询等.
3. 调优手段
1.硬件层面:增加服务器数量或升级配置-增加内存、使用更快的硬盘
2.系统配置:优化操作系统和中间件的配置参数,线程池大小、微服务xms、xmx的值(堆内存大小)
3.应用层面:代码优化:利用缓存、分布式部署等提高性能
4.数据库优化:表设计优化、数据库分库分表、索引优化、查询语句优化等,
4. 注意事项
1.需求明确:调优前明确性能目标,比如流程启动:响应时间不超过3秒,并发数为600/分钟
2.监控工具:合理使用监控工具,确保优化效果.
三. 数据库优化
1. 硬件配置-升级硬件
2. 表设计优化
2.1 三范式
2.2 选择合适数据类型:整型代替字符型
2.3 避免使用大字段
2.4 根据业务进行适当的字段冗余,减少关联查询
2.5 进行适当的拆分和关联
3. 索引优化
根据查询频率和条件,创建合适的索引,删除不必要的索引,来提高查询效率
4. 分库分表-适用于数据量较大的表
将数据拆分到多个数据库或者表中,提高读写性能.
1. 分库
1. 概念
将数据拆分到不同的数据库实例中,从而减少单个数据库的访问压力:
2. 分库策略
1. 按业务模块分库
- 适用场景:适用于大型系统,不同业务模块之间的数据关联较少。
- 示例:将用户模块、订单模块、支付模块分别存储在不同的数据库中。
- 优点:减少单个数据库的负载,提高系统的可扩展性和可用性。
- 缺点:跨库查询较复杂,需要分布式事务管理。
2. 按地区分库
- 适用场景:适用于多地区分布的业务,如电商、物流等。
- 示例:将不同地区的用户数据存储在不同的数据库中,如 db_cn 、 db_us 等。
- 优点:减少网络延迟,提高查询效率,便于数据的本地化管理。
- 缺点:跨地区查询较复杂,需要分布式事务管理。
2. 水平分表-根据数据行拆分
1.概念
- 将表中的行分散到多个表中,每个表的结构相同,但数据行不同。
- 例如,将用户数据按地区或时间分表。
2.水平分表策略
1.按时间分表
- 适用场景:适用于日志数据、历史记录等按时间顺序增长的数据。
- 示例:将用户操作日志按月份分表,如 log_202401 、 log_202402 等。
- 优点:便于数据的归档和清理,老数据可以定期迁移到冷存储。
- 缺点:跨表查询较复杂,需要联合查询多个表。
2.按范围分表
- 适用场景:适用于数据有明显范围划分的场景,如用户ID、地区等。
- 示例:将用户数据按用户ID范围分表,如 user_0_10000 、 user_10001_20000 等。
- 优点:查询效率高,每个表的数据量相对较小。
- 缺点:需要维护分表规则,跨表查询较复杂。
3. 按哈希分表
- 适用场景:适用于数据分布均匀的场景,如用户ID、订单ID等。
- 示例:将用户数据按用户ID的哈希值分表,如 user_0 、 user_1 等,哈希值取模N(N为表的数量)。
- 优点:数据分布均匀,查询效率高。
- 缺点:跨表查询较复杂,扩展性较差,增加或减少表的数量时需要重新分配数据。
3.垂直分表-根据字段拆分
1.概念
- 将表中的列分散到多个表中,每个表包含部分列。
- 例如,将用户的基本信息和详细信息分别存储在不同的表中。
2.分表策略
1. 按业务逻辑分表
- 适用场景:适用于表中列较多,且不同列的访问频率差异较大的场景。
- 示例:将用户表分为基本信息表 user_basic 和详细信息表 user_detail 。
- 优点:减少单表的列数,提高查询效率,减少锁竞争。
- 缺点:增加了表之间的关联复杂度,需要多表连接查询。
2.按访问频率分表
- 适用场景:适用于表中部分列访问频繁,部分列访问较少的场景。
- 示例:将订单表分为订单基本信息表 order_basic 和订单扩展信息表 order_extend 。
- 优点:提高热点数据的查询效率,减少锁竞争。
- 缺点:增加了表之间的关联复杂度,需要多表连接查询。
4. 实战:-shardingsphere(开源的分布式数据库中间件)
1. 架构
核心层、中间件层、存储层
2. 核心功能
数据分片、分布式事务
3. 分片规则
- 在配置文件中进行中间件配置,通过tablestrategy(表分片策略)/databasestrategy(数据库分片策略)来设置shardingcolumn(分片字段)
yaml
dataSources:
ds0:
type: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://localhost:3306/ds0
username: root
password: root
ds1:
type: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://localhost:3306/ds1
username: root
password: root
sharding:
tables:
orders:
actualDataNodes: ds${0..1}.orders_${0..1} # 指定了实际的数据节点,ds0 和 ds1 是数据库实例,orders_0 和 orders_1 是表名,表示数据被拆分到这些表中
tableStrategy: # 定义了表分片策略,order_id 字段的模值决定了数据应存储到哪个表中
inline:
shardingColumn: order_id
algorithmExpression: orders_${order_id % 2}
databaseStrategy: # 定义了数据库分片策略,order_id 字段的模值决定了数据应存储到哪个数据库中
inline:
shardingColumn: order_id
algorithmExpression: ds${order_id % 2}
defaultDatabaseStrategy:
none:
5. 分开分表可能带来的问题
1. 事务一致性问题
- 跨库事务 :分库后,若一个事务需操作多个数据库实例,如更新分布在不同库中的用户表和订单表,传统单机数据库的事务特性(ACID)无法直接保证,会出现原子性、一致性、隔离性、持久性问题,如事务中某步骤失败,回滚困难,部分操作成功部分失败致数据不一致,或部分数据库提交事务而另一部分因故障未提交等。
- 跨表事务:即使在同数据库实例中,数据分散到不同表,更新主表和多个分表时,也难在不增复杂度情况下确保事务原子性。
2. 跨节点关联查询join问题
拆分前,列表和详情表数据可通过join连表查询完成,切分后数据分布在不同节点,join查询变得麻烦,且为保证性能,应尽量避免使用join查询。
3. 全局唯一ID问题
分库分表后,每张表的自增ID仅在本表唯一,无法保证全局唯一,如订单表1和订单表2的主键都从1开始,在需全局唯一ID的场景会发生冲突。
4. 分布式事务问题
跨库事务需特别注意,可采用分布式事务框架如Seata或XA协议来保证一致性。
5. 数据缓存
6. 查询语句优化
1.根据慢查询日志,定位sql
开启慢查询-设置慢查询时间-查询日志文件-确定当前sql的具体响应时间
# 通过Mysql的set命令开启慢查询
1.1 set global slow_query_log='ON';
# 记录慢查询日志的保存位置
1.2 set global slow_query_log_file='/usr/local/mysql/data/slow.log';
# 设置查询响应时间阈值-2秒以上为慢查询,记录该条sql到慢查询日志中
1.3 set global long_query_time=2;
2.使用explain 分析该sql,确认是否在使用索引,索引的适用是否正确.
关注点1:type属性
all-全表扫描;index:指从索引中读取想要的数据
比如:explain select count(id_) from PORTAL_SYS_LOGS;从索引中读取行数量
关注点2: extra属性
using index:所有被查询的字段都是索引列(覆盖索引)
using where:被查询的列未被索引覆盖
3.修改sql或者使用索引
四. 适合加索引的字段以及索引失效的场景
1. 适合加索引的字段
1)主键字段:用于唯一标识每个记录的字段,通常是数据库表中的一个自增或唯一标识字段。
2)外键字段:用于连接两个表之间关系的字段,通常是一个表中的字段与另一个表中的主键字段关联。
3)经常用于查询的字段:如果某个字段经常被用于查询条件,例如用户的用户名、邮箱或订单的编号,那么为该字段添加索引可以提高查询性能。
4)经常用于排序的字段:如果某个字段经常用于排序操作,例如文章的发布时间或销售订单的日期,为该字段添加索引可以加快排序操作的速度。
5)经常用于连接操作的字段:如果某个字段用于连接多个表进行关联查询,例如用户表和订单表中的用户 ID 字段,为该字段添加索引可以提高连接操作的性能。
总结:Select、Where、Join、Order By 后面跟着的字段建议加索引。
2. 不适合加索引的字段
1)低选择性的列:低选择性的列指的是具有很少不同值的列。如果一列只有很少几个不同的值,那么为它添加索引可能不会提供显著的性能改进,而且可能浪费存储空间。例如,性别列通常只有两个不同的值(男和女),对其添加索引通常没有太大意义。
2)频繁更新的列:如果一个列经常被更新,特别是大规模的批量更新,那么索引会增加更新操作的开销。每次更新索引列都需要维护索引结构,这可能会导致性能下降。在这种情况下,需要仔细权衡查询性能和更新性能。
3)小表:对于非常小的表,查询通常非常快,即使没有索引。在这种情况下,添加索引可能只会增加存储开销,而不会明显提高性能。
总结:低选择性、频繁更新、数据量较小的表不推荐加索引。
3. 索引失效的场景
1)左或左右模糊匹配,因为 MySQL 采用最左匹配原则。
SELECT * FROM users WHERE name LIKE '%abc';
2)查询条件中队索引列使用函数。
SELECT * FROM users WHERE LOWER(name) = 'abc';
3)查询条件对索引列使用表达式计算。
SELECT * FROM users WHERE age + 10 = 30;
4)字段类型不一致导致索引失效-如果索引列是字符串,输入参数是数字,那么索引列会产生隐式类型转换,CAST 函数实现,等同于对索引列使用函数,导致索引失效,反之索引列是数字,输入参数是字符串,那么不会失效。
SELECT * FROM users WHERE str = 1234 ;
5)在 Where 子句中,如果在 OR 前的条件列是索引列,但 OR 后面的条件列不是索引列。
SELECT * FROM users WHERE name = 'abc' OR age = 30;
6)使用 Select * 语句,大概率不会走索引,因为不是每一列都加索引。
SELECT * FROM users;
7)使用 Not Exists /IN关键字,索引也会失效,本质上是 Where 查询范围过大。
SELECT * FROM users WHERE NOT EXISTS (SELECT * FROM orders WHERE orders.user_id = users.id);
select * from logs where log_type in ('业务日志','异常日志');
8)使用 Order by 注意最左匹配,并且要加 limit 或者 Where 关键字,否则索引会失效。
SELECT * FROM users ORDER BY name LIMIT 10;