MYSQL----------数据库优化及锁机制详解

数据库优化及锁机制详解

数据库对象优化

优化表的数据类型

选择合适的数据类型对数据库性能影响巨大。例如,能用TINYINT就别用INT,因为前者只占用1个字节,而INT占用4个字节,存储空间的节省能减少磁盘I/O。对于字符型数据,如果字段长度固定且较短,CHAR类型比VARCHAR更高效,因为VARCHAR还需额外存储长度信息。

拆分表提升访问效率

  1. 垂直拆分:把包含很多列的宽表按业务逻辑拆分成多个窄表。比如电商订单表,将订单基础信息、订单商品详情、订单物流信息分别拆成独立表。如此一来,查询订单基础信息时,就无需读取大量无关的商品详情与物流数据,加快查询速度。
  2. 水平拆分:当表数据量超大时,依据某一规则(如时间、地域、用户ID范围)把数据分散到多个表。像社交平台的用户消息表,可按月份拆分,每月数据存于不同表,查询某个时间段消息时,直接定位对应表即可。

逆规范化

与规范化减少数据冗余相反,逆规范化适当引入冗余来提升查询性能。例如,在多表关联查询频繁的场景下,把常用关联字段冗余到主表,可减少复杂的连接操作,用空间换时间。

使用中间表加速统计查询

针对复杂统计需求,创建中间表预先计算、存储部分结果。例如电商平台要统计每日各品类销售总额,可每晚定时将当天订单数据聚合处理,存入中间表,后续直接查询该表,避免实时对海量订单表做聚合运算。

小结 综合运用上述优化手段,可显著提升数据库读写性能,降低系统响应时间,但要权衡空间、维护成本与性能提升之间的关系。

锁问题

MySQL锁概述

MySQL有多种锁机制,用来控制并发访问,保障数据一致性。不同存储引擎的锁实现差异很大,主流的MYISAMINNODB各有特点。

MYISAM表锁

  • 查询表级锁争用情况 :可通过SHOW STATUS LIKE 'Table_locks_waited';查看等待表锁的次数,Table_locks_immediate则表示立即获取到表锁的次数,二者比值能反映表锁争用程度。
  • MySQL表级锁的锁模式MYISAM有读锁(共享锁)和写锁(排他锁) 。读锁允许多个线程同时读取,但写锁会阻塞其他读、写操作。
  • 如何加表锁 :加读锁用LOCK TABLES table_name READ;,加写锁则是LOCK TABLES table_name WRITE;,操作完要用UNLOCK TABLES;释放。
  • 并发插入MYISAM支持并发插入,在满足一定条件下,即便表有读锁,新数据也能插入到表尾,提升插入性能。
  • MYISAM的锁调度:它的锁调度比较简单,写锁优先级高于读锁,所以读操作频繁的场景,容易因写锁等待出现阻塞。

INNODB锁问题

  • 背景知识INNODB基于行锁设计,不过也有表锁辅助,在高并发场景更具优势。
  • 获取INNODB行锁争用情况 :借助INFORMATION_SCHEMA.INNODB_LOCKSINFORMATION_SCHEMA.INNODB_LOCK_WAITS两张视图,能定位行锁争用的事务、锁类型与等待关系。
  • INNODB的行锁模式及加锁方法 :有共享锁(S锁)、排他锁(X锁)等。普通SELECT语句加S锁,UPDATEDELETE 则加X锁。可以用SELECT... FOR UPDATE显式加排他锁,SELECT... LOCK IN SHARE MODE加共享锁。
  • INNODB行锁实现方式:通过索引实现精准锁定,若没有合适索引,会升级为表锁,性能大打折扣。
  • 间隙锁INNODB为防止幻读引入间隙锁,它锁定索引记录间的间隙,不过这也可能在并发插入时导致锁争用。
  • 对INNODB锁机制的影响:不合理的索引、长事务都易引发锁问题,拖慢系统。要定期清理长事务,优化索引来维持高效并发。
  • INNODB在不同隔离级别下的一致性及读锁的差异READ UNCOMMITTED最低隔离级别,允许脏读,读锁限制少;READ COMMITTED避免脏读,但不可重复读;REPEATABLE READ是默认级别,保证可重复读,读锁管控更严格;SERIALIZABLE最高级别,完全串行化,锁争用最严重。

以下是一些与上述数据库操作和锁相关的代码示例,以 MySQL 为例:

sql 复制代码
-- 创建表
CREATE TABLE orders (
    order_id INT AUTO_INCREMENT PRIMARY KEY,
    customer_id INT,
    order_date DATE,
    order_amount DECIMAL(10, 2)
);

-- 插入数据
INSERT INTO orders (customer_id, order_date, order_amount) VALUES (1, '2025-01-01', 100.00);
INSERT INTO orders (customer_id, order_date, order_amount) VALUES (2, '2025-01-02', 200.00);

-- 加 MYISAM 表读锁
LOCK TABLES orders READ;
-- 在此处可以进行读取操作,如
SELECT * FROM orders;
-- 释放锁
UNLOCK TABLES;

-- 加 MYISAM 表写锁
LOCK TABLES orders WRITE;
-- 在此处可以进行修改操作,如
UPDATE orders SET order_amount = 150.00 WHERE order_id = 1;
-- 释放锁
UNLOCK TABLES;


-- 使用 INNODB 行锁
-- 开启事务
START TRANSACTION;
-- 显式加共享锁,可用于读取,其他事务也能加共享锁,但不能加排他锁
SELECT * FROM orders WHERE order_id = 1 LOCK IN SHARE MODE;
-- 显式加排他锁,用于修改等操作,其他事务不能加共享或排他锁
SELECT * FROM orders WHERE order_id = 1 FOR UPDATE;
-- 执行修改操作
UPDATE orders SET order_amount = 120.00 WHERE order_id = 1;
-- 提交事务
COMMIT;


-- 查看 INNODB 行锁争用情况
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;


-- 垂直拆分表的示例,将订单表拆分为订单信息表和订单商品表
CREATE TABLE order_info (
    order_id INT AUTO_INCREMENT PRIMARY KEY,
    customer_id INT,
    order_date DATE
);

CREATE TABLE order_products (
    order_product_id INT AUTO_INCREMENT PRIMARY KEY,
    order_id INT,
    product_id INT,
    quantity INT,
    FOREIGN KEY (order_id) REFERENCES order_info(order_id)
);


-- 水平拆分表的示例,假设按年份拆分订单表,这里创建 2025 年的订单表
CREATE TABLE orders_2025 (
    order_id INT AUTO_INCREMENT PRIMARY KEY,
    customer_id INT,
    order_date DATE,
    order_amount DECIMAL(10, 2)
);


-- 创建中间表用于统计查询
CREATE TABLE daily_sales_summary (
    summary_date DATE PRIMARY KEY,
    total_sales DECIMAL(10, 2)
);

-- 插入统计数据到中间表
INSERT INTO daily_sales_summary (summary_date, total_sales)
SELECT order_date, SUM(order_amount)
FROM orders
WHERE order_date = '2025-01-01'
GROUP BY order_date;

代码解释:

  1. 创建表和插入数据
    • CREATE TABLE语句用于创建表,指定表名、列名、数据类型和约束条件(如主键)。
    • INSERT INTO语句向表中插入数据。
  2. MYISAM 表锁操作
    • LOCK TABLES orders READ;orders 表加读锁,允许多个事务同时读取,但不允许写操作。
    • LOCK TABLES orders WRITE; 加写锁,阻塞其他读和写操作,确保独占修改权限。
    • UNLOCK TABLES; 释放锁,确保锁在使用后及时释放,以免影响其他事务。
  3. INNODB 行锁操作
    • START TRANSACTION; 开启事务,为使用行锁提供事务环境。
    • SELECT... LOCK IN SHARE MODE; 对指定行加共享锁,允许多个事务读取,但不允许修改。
    • SELECT... FOR UPDATE; 加排他锁,仅允许当前事务修改数据。
    • COMMIT; 提交事务,释放锁。
  4. 查看 INNODB 行锁争用情况
    • SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS; 可查看正在使用的锁和等待锁的信息,帮助诊断锁冲突。
  5. 表拆分示例
    • 垂直拆分将订单表的不同信息分到不同表,通过外键关联。
    • 水平拆分按时间范围将数据分到不同表,如 orders_2025 存储 2025 年的订单。
  6. 中间表创建和使用
    • CREATE TABLE daily_sales_summary 创建用于存储统计数据的中间表。
    • INSERT INTO daily_sales_summary 插入统计数据,从 orders 表中聚合而来,方便后续统计查询。

这些示例涵盖了 MySQL

数据库操作、锁使用、表拆分和中间表创建的基本操作,你可根据具体场景和性能需求灵活运用,同时注意在高并发场景下优化锁使用和表结构,避免性能瓶颈。在使用锁时,确保锁的使用范围和时长合理,避免长时间占用锁影响系统性能。

相关推荐
学会沉淀。23 分钟前
Redis
数据库·redis·缓存
deadknight92 小时前
Oracle重启后业务连接大量library cache lock
数据库·oracle
万事可爱^2 小时前
【SQL】进阶知识 -- 删除表的几种方法(包含表内单个字段的删除方法)
数据库·hive·sql·oracle
非凡的世界2 小时前
关于 ThinkPHP 与 PostgreSQL 结合使用的一些要点
数据库·postgresql
fox08152 小时前
wsl2上mysql出现ip端口冲突问题
网络·数据库·tcp/ip·mysql·wsl
m0_672449603 小时前
MySQL高级(事务隔离界别)
数据库·mysql
fengyehongWorld3 小时前
Oracle 批量投入数据方法总结
数据库·oracle
数据小爬虫@3 小时前
爬虫程序如何存储数据到数据库?
数据库
weisian1514 小时前
Mysql--运维篇--备份和恢复(逻辑备份,mysqldump,物理备份,热备份,温备份,冷备份,二进制文件备份和恢复等)
运维·mysql
夏炎正好眠4 小时前
mysql概述
数据库·mysql·oracle