系统性能调优

文章目录

  • [一. 概念](#一. 概念)
  • [二. 一般流程](#二. 一般流程)
    • [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. 按业务模块分库
  1. 适用场景:适用于大型系统,不同业务模块之间的数据关联较少。
  2. 示例:将用户模块、订单模块、支付模块分别存储在不同的数据库中。
  3. 优点:减少单个数据库的负载,提高系统的可扩展性和可用性。
  4. 缺点:跨库查询较复杂,需要分布式事务管理。
2. 按地区分库
  1. 适用场景:适用于多地区分布的业务,如电商、物流等。
  2. 示例:将不同地区的用户数据存储在不同的数据库中,如 db_cn 、 db_us 等。
  3. 优点:减少网络延迟,提高查询效率,便于数据的本地化管理。
  4. 缺点:跨地区查询较复杂,需要分布式事务管理。

2. 水平分表-根据数据行拆分

1.概念
  1. 将表中的行分散到多个表中,每个表的结构相同,但数据行不同。
  2. 例如,将用户数据按地区或时间分表。
2.水平分表策略
1.按时间分表
  1. 适用场景:适用于日志数据、历史记录等按时间顺序增长的数据。
  2. 示例:将用户操作日志按月份分表,如 log_202401 、 log_202402 等。
  3. 优点:便于数据的归档和清理,老数据可以定期迁移到冷存储。
  4. 缺点:跨表查询较复杂,需要联合查询多个表。
2.按范围分表
  1. 适用场景:适用于数据有明显范围划分的场景,如用户ID、地区等。
  2. 示例:将用户数据按用户ID范围分表,如 user_0_10000 、 user_10001_20000 等。
  3. 优点:查询效率高,每个表的数据量相对较小。
  4. 缺点:需要维护分表规则,跨表查询较复杂。
3. 按哈希分表
  1. 适用场景:适用于数据分布均匀的场景,如用户ID、订单ID等。
  2. 示例:将用户数据按用户ID的哈希值分表,如 user_0 、 user_1 等,哈希值取模N(N为表的数量)。
  3. 优点:数据分布均匀,查询效率高。
  4. 缺点:跨表查询较复杂,扩展性较差,增加或减少表的数量时需要重新分配数据。

3.垂直分表-根据字段拆分

1.概念
  1. 将表中的列分散到多个表中,每个表包含部分列。
  2. 例如,将用户的基本信息和详细信息分别存储在不同的表中。
2.分表策略
1. 按业务逻辑分表
  1. 适用场景:适用于表中列较多,且不同列的访问频率差异较大的场景。
  2. 示例:将用户表分为基本信息表 user_basic 和详细信息表 user_detail 。
  3. 优点:减少单表的列数,提高查询效率,减少锁竞争。
  4. 缺点:增加了表之间的关联复杂度,需要多表连接查询。
2.按访问频率分表
  1. 适用场景:适用于表中部分列访问频繁,部分列访问较少的场景。
  2. 示例:将订单表分为订单基本信息表 order_basic 和订单扩展信息表 order_extend 。
  3. 优点:提高热点数据的查询效率,减少锁竞争。
  4. 缺点:增加了表之间的关联复杂度,需要多表连接查询。

4. 实战:-shardingsphere(开源的分布式数据库中间件)

1. 架构

核心层、中间件层、存储层

2. 核心功能

数据分片、分布式事务

3. 分片规则
  1. 在配置文件中进行中间件配置,通过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. 事务一致性问题
  1. 跨库事务 :分库后,若一个事务需操作多个数据库实例,如更新分布在不同库中的用户表和订单表,传统单机数据库的事务特性(ACID)无法直接保证,会出现原子性、一致性、隔离性、持久性问题,如事务中某步骤失败,回滚困难,部分操作成功部分失败致数据不一致,或部分数据库提交事务而另一部分因故障未提交等。
  2. 跨表事务:即使在同数据库实例中,数据分散到不同表,更新主表和多个分表时,也难在不增复杂度情况下确保事务原子性。
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;

相关推荐
JosieBook2 小时前
【数据库】时序数据库选型指南:在大数据与工业4.0时代,为何 Apache IoTDB 成为智慧之选?
大数据·数据库·时序数据库
程序员三明治2 小时前
详解Redis锁误删、原子性难题及Redisson加锁底层原理、WatchDog续约机制
java·数据库·redis·分布式锁·redisson·watchdog·看门狗
chenzhou__2 小时前
MYSQL学习笔记(个人)(第十五天)
linux·数据库·笔记·学习·mysql
一只自律的鸡3 小时前
【MySQL】第二章 基本的SELECT语句
数据库·mysql
liliangcsdn4 小时前
如何使用python创建和维护sqlite3数据库
数据库·sqlite
TDengine (老段)10 小时前
TDengine 数学函数 DEGRESS 用户手册
大数据·数据库·sql·物联网·时序数据库·iot·tdengine
TDengine (老段)10 小时前
TDengine 数学函数 GREATEST 用户手册
大数据·数据库·物联网·时序数据库·iot·tdengine·涛思数据
安当加密10 小时前
云原生时代的数据库字段加密:在微服务与 Kubernetes 中实现合规与敏捷的统一
数据库·微服务·云原生
爱喝白开水a11 小时前
LangChain 基础系列之 Prompt 工程详解:从设计原理到实战模板_langchain prompt
开发语言·数据库·人工智能·python·langchain·prompt·知识图谱
想ai抽11 小时前
深入starrocks-多列联合统计一致性探查与策略(YY一下)
java·数据库·数据仓库