MySQL 高级主题:索引优化、ORM 与数据库迁移

第五部分:索引优化

1. 为什么需要索引?

索引是提高数据库查询性能的关键数据结构,它类似于书籍的目录,可以帮助数据库快速定位到所需数据,而不必扫描整个表。

2. 索引类型

  • 主键索引 (PRIMARY KEY): 唯一且非空,每个表只能有一个

  • 唯一索引 (UNIQUE): 确保列中所有值都是唯一的

  • 普通索引 (INDEX): 最基本的索引,没有唯一性限制

  • 复合索引 (COMPOSITE INDEX): 基于多个列的索引

  • 全文索引 (FULLTEXT): 用于全文搜索

  • 空间索引 (SPATIAL): 用于地理空间数据类型

3. 创建索引的最佳实践

sql

-- 创建普通索引
CREATE INDEX idx_email ON users(email);-- 创建唯一索引
CREATE UNIQUE INDEX idx_unique_username ON users(username);-- 创建复合索引(注意列的顺序很重要)
CREATE INDEX idx_name_age ON users(last_name, first_name, age);-- 删除索引
DROP INDEX idx_email ON users;

4. 使用 EXPLAIN 分析查询性能

sql

-- 分析查询执行计划
EXPLAIN SELECT * FROM users WHERE email = 'test@example.com';-- 更详细的分析
EXPLAIN FORMAT=JSON SELECT * FROM users WHERE age > 25;

5. 索引优化建议

  • 为经常用于 WHERE、JOIN、ORDER BY 和 GROUP BY 的列创建索引

  • 避免在频繁更新的列上创建过多索引

  • 使用复合索引而不是多个单列索引

  • 定期分析慢查询日志,优化性能瓶颈

  • 使用覆盖索引(索引包含所有查询字段)避免回表操作

第六部分:使用 ORM(对象关系映射)

Python - SQLAlchemy 高级用法

1. 安装与配置

bash

pip install sqlalchemy
2. 定义模型与关系

python

from sqlalchemy import create_engine, Column, Integer, String, DateTime, ForeignKey, Text
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
from datetime import datetimeBase = declarative_base()class User(Base):__tablename__ = 'users'id = Column(Integer, primary_key=True)username = Column(String(50), unique=True, nullable=False)email = Column(String(100), nullable=False)password_hash = Column(String(128), nullable=False)created_at = Column(DateTime, default=datetime.utcnow)updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)# 定义关系posts = relationship("Post", back_populates="author", cascade="all, delete-orphan")profile = relationship("Profile", back_populates="user", uselist=False, cascade="all, delete-orphan")class Profile(Base):__tablename__ = 'profiles'id = Column(Integer, primary_key=True)user_id = Column(Integer, ForeignKey('users.id'), unique=True)first_name = Column(String(50))last_name = Column(String(50))bio = Column(Text)user = relationship("User", back_populates="profile")class Post(Base):__tablename__ = 'posts'id = Column(Integer, primary_key=True)title = Column(String(200), nullable=False)content = Column(Text, nullable=False)author_id = Column(Integer, ForeignKey('users.id'))created_at = Column(DateTime, default=datetime.utcnow)author = relationship("User", back_populates="posts")categories = relationship("Category", secondary="post_categories", back_populates="posts")class Category(Base):__tablename__ = 'categories'id = Column(Integer, primary_key=True)name = Column(String(50), unique=True, nullable=False)posts = relationship("Post", secondary="post_categories", back_populates="categories")# 多对多关联表
post_categories = Table('post_categories', Base.metadata,Column('post_id', Integer, ForeignKey('posts.id')),Column('category_id', Integer, ForeignKey('categories.id'))
)# 创建引擎和会话
engine = create_engine('mysql+pymysql://user:password@localhost/mydatabase?charset=utf8mb4')
Session = sessionmaker(bind=engine)
session = Session()# 创建所有表
Base.metadata.create_all(engine)
3. 高级查询示例

python

# 复杂查询
from sqlalchemy import and_, or_, func# 基本查询
users = session.query(User).filter(User.email.like('%@example.com')).all()# 使用join查询
posts_with_authors = (session.query(Post, User.username).join(User, Post.author_id == User.id).filter(Post.created_at >= '2023-01-01').order_by(Post.created_at.desc()).all())# 聚合查询
user_post_counts = (session.query(User.username, func.count(Post.id).label('post_count')).join(Post, User.id == Post.author_id).group_by(User.id).having(func.count(Post.id) > 5).all())# 使用子查询
subquery = (session.query(Post.author_id, func.max(Post.created_at).label('last_post')).group_by(Post.author_id).subquery())recent_authors = (session.query(User, subquery.c.last_post).join(subquery, User.id == subquery.c.author_id).all())# 事务处理
try:new_user = User(username='newuser', email='new@example.com', password_hash='hashed_password')session.add(new_user)session.flush()  # 获取新用户的ID但不提交事务new_profile = Profile(user_id=new_user.id, first_name='John', last_name='Doe')session.add(new_profile)session.commit()
except Exception as e:session.rollback()print(f"Error: {e}")

Node.js - TypeORM 高级用法

1. 安装与配置

bash

npm install typeorm reflect-metadata mysql2
npm install @types/node --save-dev
2. 创建实体与关系

typescript

// src/entity/User.ts
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, OneToOne, OneToMany } from "typeorm";
import { Profile } from "./Profile";
import { Post } from "./Post";@Entity()
export class User {@PrimaryGeneratedColumn()id: number;@Column({ unique: true, length: 50 })username: string;@Column({ length: 100 })email: string;@Column({ length: 128 })passwordHash: string;@CreateDateColumn()createdAt: Date;@UpdateDateColumn()updatedAt: Date;@OneToOne(() => Profile, profile => profile.user, { cascade: true })profile: Profile;@OneToMany(() => Post, post => post.author, { cascade: true })posts: Post[];
}// src/entity/Profile.ts
import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn } from "typeorm";
import { User } from "./User";@Entity()
export class Profile {@PrimaryGeneratedColumn()id: number;@Column({ nullable: true, length: 50 })firstName: string;@Column({ nullable: true, length: 50 })lastName: string;@Column({ type: "text", nullable: true })bio: string;@OneToOne(() => User, user => user.profile)@JoinColumn()user: User;
}// src/entity/Post.ts
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, ManyToMany, JoinTable, CreateDateColumn } from "typeorm";
import { User } from "./User";
import { Category } from "./Category";@Entity()
export class Post {@PrimaryGeneratedColumn()id: number;@Column({ length: 200 })title: string;@Column({ type: "text" })content: string;@CreateDateColumn()createdAt: Date;@ManyToOne(() => User, user => user.posts)author: User;@ManyToMany(() => Category, category => category.posts)@JoinTable()categories: Category[];
}// src/entity/Category.ts
import { Entity, PrimaryGeneratedColumn, Column, ManyToMany } from "typeorm";
import { Post } from "./Post";@Entity()
export class Category {@PrimaryGeneratedColumn()id: number;@Column({ unique: true, length: 50 })name: string;@ManyToMany(() => Post, post => post.categories)posts: Post[];
}
3. 数据源配置与查询

typescript

// src/data-source.ts
import "reflect-metadata";
import { DataSource } from "typeorm";
import { User } from "./entity/User";
import { Profile } from "./entity/Profile";
import { Post } from "./entity/Post";
import { Category } from "./entity/Category";export const AppDataSource = new DataSource({type: "mysql",host: "localhost",port: 3306,username: "test",password: "test",database: "test",synchronize: true, // 生产环境应该设为false,使用迁移logging: false,entities: [User, Profile, Post, Category],migrations: [],subscribers: [],
});// src/index.ts
import { AppDataSource } from "./data-source";
import { User } from "./entity/User";
import { Profile } from "./entity/Profile";
import { Post } from "./entity/Post";
import { Category } from "./entity/Category";
import { In } from "typeorm";AppDataSource.initialize().then(async () => {console.log("Data Source has been initialized!");// 创建新用户const user = new User();user.username = "timber";user.email = "timber@example.com";user.passwordHash = "hashed_password";const profile = new Profile();profile.firstName = "Timber";profile.lastName = "Saw";user.profile = profile;await AppDataSource.manager.save(user);// 复杂查询const userRepository = AppDataSource.getRepository(User);const users = await userRepository.find({where: {email: Like("%@example.com")},relations: {profile: true,posts: true}});// 使用QueryBuilderconst posts = await AppDataSource.getRepository(Post).createQueryBuilder("post").leftJoinAndSelect("post.author", "author").leftJoinAndSelect("post.categories", "category").where("post.createdAt > :date", { date: new Date("2023-01-01") }).orderBy("post.createdAt", "DESC").getMany();// 事务处理await AppDataSource.manager.transaction(async transactionalEntityManager => {const newUser = new User();newUser.username = "transaction_user";newUser.email = "transaction@example.com";newUser.passwordHash = "hashed_password";await transactionalEntityManager.save(newUser);const newPost = new Post();newPost.title = "Transaction Post";newPost.content = "This post was created in a transaction";newPost.author = newUser;await transactionalEntityManager.save(newPost);});}).catch((error) => console.log(error));

第七部分:数据库迁移工具

Python - Alembic 高级用法

1. 初始化与配置

bash

# 安装
pip install alembic# 初始化
alembic init alembic# 修改 alembic.ini 中的数据库连接
sqlalchemy.url = mysql+pymysql://user:password@localhost/mydatabase
2. 配置环境脚本 (alembic/env.py)

python

import sys
from os.path import abspath, dirname# 添加项目根目录到Python路径
sys.path.insert(0, dirname(dirname(abspath(__file__))))from myapp.models import Base  # 导入你的Base
target_metadata = Base.metadata# 其他配置...
3. 创建与运行迁移

bash

# 创建自动迁移脚本
alembic revision --autogenerate -m "添加用户个人资料表"# 检查生成的迁移脚本,必要时手动调整
# 运行迁移
alembic upgrade head# 降级迁移
alembic downgrade -1# 查看迁移历史
alembic history# 查看当前版本
alembic current
4. 复杂迁移示例

python

# 迁移脚本示例
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysqldef upgrade():# 创建新表op.create_table('profiles',sa.Column('id', sa.Integer(), nullable=False),sa.Column('user_id', sa.Integer(), nullable=False),sa.Column('first_name', sa.String(length=50), nullable=True),sa.Column('last_name', sa.String(length=50), nullable=True),sa.Column('bio', sa.Text(), nullable=True),sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),sa.PrimaryKeyConstraint('id'),sa.UniqueConstraint('user_id'))# 添加新列op.add_column('users', sa.Column('updated_at', sa.DateTime(), nullable=True))# 创建索引op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=True)# 数据迁移conn = op.get_bind()conn.execute(sa.text("UPDATE users SET updated_at = created_at WHERE updated_at IS NULL"))# 修改列属性op.alter_column('users', 'updated_at',existing_type=mysql.DATETIME(),nullable=False)def downgrade():# 删除索引op.drop_index(op.f('ix_users_email'), table_name='users')# 删除列op.drop_column('users', 'updated_at')# 删除表op.drop_table('profiles')

Node.js - TypeORM 迁移

1. 配置迁移

json

// package.json 中添加脚本
{"scripts": {"typeorm": "typeorm-ts-node-commonjs","migration:generate": "npm run typeorm -- migration:generate -d src/data-source.ts","migration:run": "npm run typeorm -- migration:run -d src/data-source.ts","migration:revert": "npm run typeorm -- migration:revert -d src/data-source.ts"}
}
2. 生成与运行迁移

bash

# 生成迁移(根据实体变更自动生成)
npm run migration:generate -- src/migrations/CreateProfileTable# 运行迁移
npm run migration:run# 回滚迁移
npm run migration:revert
3. 迁移文件示例

typescript

// src/migrations/1678888888888-CreateProfileTable.ts
import { MigrationInterface, QueryRunner } from "typeorm";export class CreateProfileTable1678888888888 implements MigrationInterface {name = 'CreateProfileTable1678888888888'public async up(queryRunner: QueryRunner): Promise<void> {await queryRunner.query(`CREATE TABLE \`profiles\` (\`id\` int NOT NULL AUTO_INCREMENT,\`firstName\` varchar(50) NULL,\`lastName\` varchar(50) NULL,\`bio\` text NULL,\`userId\` int NULL,UNIQUE INDEX \`REL_9e70fe39bace1b4fe0a96e5720\` (\`userId\`),PRIMARY KEY (\`id\`)) ENGINE=InnoDB`);await queryRunner.query(`ALTER TABLE \`profiles\`ADD CONSTRAINT \`FK_9e70fe39bace1b4fe0a96e57203\`FOREIGN KEY (\`userId\`) REFERENCES \`users\`(\`id\`)ON DELETE CASCADE ON UPDATE NO ACTION`);// 数据迁移await queryRunner.query(`INSERT INTO profiles (firstName, lastName, userId)SELECT 'Default', 'User', id FROM users`);}public async down(queryRunner: QueryRunner): Promise<void> {await queryRunner.query(`ALTER TABLE \`profiles\` DROP FOREIGN KEY \`FK_9e70fe39bace1b4fe0a96e57203\``);await queryRunner.query(`DROP INDEX \`REL_9e70fe39bace1b4fe0a96e5720\` ON \`profiles\``);await queryRunner.query(`DROP TABLE \`profiles\``);}
}

第八部分:最佳实践总结

1. 索引优化

  • 使用 EXPLAIN 分析查询性能

  • 为频繁查询的字段创建索引

  • 使用复合索引优化多条件查询

  • 定期检查和优化索引

2. ORM 使用

  • 合理定义实体关系(一对一、一对多、多对多)

  • 使用懒加载避免 N+1 查询问题

  • 利用 ORM 的查询构建器进行复杂查询

  • 使用事务确保数据一致性

3. 数据库迁移

  • 所有数据库变更都应通过迁移脚本进行

  • 迁移脚本应该是可逆的(提供 up 和 down 方法)

  • 在生产环境运行迁移前,先在测试环境验证

  • 使用版本控制系统管理迁移脚本

4. 性能优化

  • 使用连接池管理数据库连接

  • 批量操作数据而不是逐条处理

  • 使用分页处理大量数据

  • 定期清理不再需要的数据

5. 安全考虑

  • 使用预处理语句防止 SQL 注入

  • 对敏感数据进行加密

  • 遵循最小权限原则设置数据库用户权限

  • 定期备份数据库

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/919689.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/919689.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Eplan教程:网络与PLC

欢迎大家来到“Eplan带你做项目”第六个过程。在第五个过程中&#xff0c;Eplan基于实际项目的绘制&#xff08;电气设计中的电源回路以及电源分配相关回路&#xff09;重点分享分了“电机的供电和控制图纸的绘制”。本文中&#xff0c;先猜个问题&#xff0c;设计一个PLC系统&…

大模型落地全攻略:从技术实现到场景应用

大语言模型&#xff08;LLM&#xff09;的快速发展正在重塑各行各业的智能化进程&#xff0c;但其落地应用仍面临技术适配、场景融合、成本控制等多重挑战。本文将系统解析大模型落地的四大核心方向 ——微调技术、提示词工程、多模态应用和企业级解决方案&#xff0c;通过代码…

【论文】Zotero文献管理

Zotero文献管理 写论文前查找阅读大量文献&#xff0c;写论文时引用文献&#xff0c;都是一件非常麻烦的事情&#xff0c;一款合适的文献管理工具可以帮助我们更快捷地完成这些任务。zotero作为一款免费开源的工具&#xff0c;可以实现文献阅读、同步管理以及引用管理。 安装…

MsSQL 函数,实现数字转换成人民币大写

MsSQL 函数&#xff0c;实现数字转换成人民币大写-- 如果函数已存在则删除 IF OBJECT_ID(dbo.ConvertToRMBChineseNew, FN) IS NOT NULLDROP FUNCTION dbo.ConvertToRMBChineseNew GOCREATE FUNCTION dbo.ConvertToRMBChineseNew (NumberInput SQL_VARIANT -- 使用 SQL_VARIANT…

OpenHarmony深度定制:从系统到模块的全景剖析与自定义模块实战

摘要:OpenHarmony 作为面向万物互联时代的开源操作系统,其“系统-子系统-部件-模块”的四层架构设计,为开发者提供了高度可裁剪、可扩展的能力。本文将系统梳理这四层结构的职责边界与协作关系,并手把手演示如何向 OpenHarmony 新增一个可交付的自定义模块(Module),帮助…

数字社会学是干什么的?数字社会学理论与数字社会学家唐兴通讲数字社会学书籍有哪些?AI社会学人工智能社会学理论框架

在当今社会&#xff0c;传统物理空间和人际关系网络成为了许多年轻人寻找合适伴侣的重大障碍。以深圳为例&#xff0c;这座移民城市的大部分居民都来自外地&#xff0c;年轻人的人脉关系、尤其是亲戚关系大多仍在家乡。这使得深圳的单身男女在交友和婚恋方面的选择面变得狭窄&a…

数据库-MYSQL配置下载

目录 一.数据库概念 一、数据库的基本定义 二、数据库管理系统&#xff08;DBMS&#xff09; 三、数据库系统&#xff08;DBS&#xff09; 四、数据模型 五、数据库的特点 六、数据库的应用领域 二.MySql 一、开源免费&#xff0c;降低中大型项目成本 二、跨平台与兼容…

Java 中表示数据集的常用集合类

Java 中表示数据集的常用集合类 Java 集合框架提供了多种数据结构来表示和操作数据集&#xff0c;每种集合类都有其特定的用途和性能特征。以下是主要的集合类及其特点&#xff1a; 一、List 接口及其实现类 1. ArrayList 特点&#xff1a;基于动态数组实现优点&#xff1a;随机…

Django REST框架核心:GenericAPIView详解

Django REST framework (DRF) 中 GenericAPIView 的源码核心部分。 它是所有“泛型视图”的基础类&#xff0c;比如常用的 ListAPIView、RetrieveAPIView、CreateAPIView 都是继承自它。&#x1f31f; 作用继承自 APIView&#xff0c;因此仍然是一个标准的 DRF 视图。提供了常用…

深入解析HashMap的存储机制:扰动函数、哈希计算与索引定位

今天复习了一下HashMap的部分&#xff0c;写一篇博客记录一下今天学习内容虽然之前学习过&#xff0c;但由于后来没怎么使用过而且也没复习基本忘得差不多了在Java的HashMap中&#xff0c;高效存储键值对的核心在于哈希算法和索引定位。本文将结合源码逐步拆解存储流程&#xf…

【机器学习 / 深度学习】基础教程

阶段一&#xff1a;机器学习 / 深度学习基础教程定位&#xff1a;针对准备进入 AI多智能体开发 的初学者&#xff0c;打牢机器学习与深度学习的基础。一、为什么需要学习机器学习/深度学习 在进入智能体&#xff08;Agent&#xff09;开发之前&#xff0c;必须具备一定的 机器学…

ESP32应用——HTTP client(ESP-IDF框架)

目录 一、前言 二、URL 2.1 URL简介 2.2 URL示例 三、HTTP 3.1 HTTP协议概述 3.2 HTTP的工作原理 3.2.1 HTTP 请求-响应流程 3.2.2 HTTP 请求结构 3.2.3 HTTP请求方法 3.2.4 HTTP响应结构 3.2.5 HTTP状态码 四、ESP HTTP 客户端流程 五、ESP HTTP 客户端实战解析…

动学学深度学习07-现代卷积神经网络

动学学深度学习pytorch 参考地址&#xff1a;https://zh.d2l.ai/ 文章目录动学学深度学习pytorch1-第07章-现代卷积神经网络1. AlexNet1.1 AlexNet 的核心贡献是什么&#xff1f;1.2 AlexNet 与 LeNet 的主要区别有哪些&#xff1f;1.3 为什么 AlexNet 需要 GPU 训练&#xff1…

详细讲解Java中的反射和经典面试题(保姆级别)

1.1 反射的概述&#xff1a;专业的解释&#xff08;了解一下&#xff09;&#xff1a;是在运行状态中&#xff0c;对于任意一个类&#xff0c;都能够知道这个类的所有属性和方法&#xff1b;对于任意一个对象&#xff0c;都能够调用它的任意属性和方法&#xff1b;这种动态获取…

MyCAT完整实验报告

MyCAT完整实验报告 ‍ 前言 刚刚看了一下前面的那篇MyCAT的文章 感觉有一些问题 所以拿出一篇文章再说一下 单独构建了完整的实验环境 这样会全面一点 ‍ 安装MyCAT #跳过‍ 主从配置 #不多追溯 因为我们选择的主从 也可以做双主机 但我们后边再说‍ 环境搭建 一、环境规划 服务…

机器翻译论文阅读方法:顶会(ACL、EMNLP)论文解析技巧

更多内容请见: 机器翻译修炼-专栏介绍和目录 文章目录 一、论文选择:快速判断论文价值 1.1 关注核心会议与子领域 1.2 筛选标准 1.3 预读筛选 1.4 快速定位关键信息 二、精读解析 2.1 问题定义(5分钟) 2.2 方法解剖(15分钟) 2.3 实验深挖(20分钟) 2.4 批判性思考(10分…

Transformer模型实战篇

引入 基于Transformers的NLP解决方案的步骤如下&#xff1a;&#xff08;以文本分类为例&#xff09; 导入相关包&#xff0c;General&#xff0c;可以询问ai需要导什么包加载数据集&#xff0c;Data_loader&#xff0c;Datasets数据集划分&#xff0c;测试机&#xff0c;验证集…

深入(流批【牛批】框架)Flink的机制

flink本身是专注有状态的无限流处理&#xff0c;有限流处理【batch批次】是无限流处理的一中特殊情况&#xff01;应用场景实时ETL 集成流计算现有的诸多数据通道和SQL灵活的加工能力&#xff0c;对流式数据进行实时清洗、归并和结构化 处理&#xff1b;同时&#xff0c;对离线…

Git 2.15.0 64位安装步骤Windows详细教程从下载到验证(附安装包下载)

一、下载后双击运行 安装包下载&#xff1a;https://pan.quark.cn/s/7200b32a1ecf&#xff0c;找到下载好的文件&#xff1a;​Git-2.15.0-64-bit.exe​双击这个文件&#xff0c;就会弹出安装向导窗口&#xff0c;点 ​​“Next”&#xff08;下一步&#xff09;​​ 二、选择…

在职老D渗透日记day23:sqli-labs靶场通关(第29关-31关)http参数过滤

5.29.第29关 http参数过滤 闭合5.29.1.手动注入&#xff08;1&#xff09;判断注入类型、注入点闭合&#xff08;2&#xff09;有回显&#xff0c;优先用联合查询注入&#xff0c;判读字段数?id1&id2 order by 3 -- ?id1&id2 order by 4 --&#xff08;3&#xff09;…