Files
nl-video-api/scripts/optimize_database.sql
2025-08-03 00:11:15 +08:00

376 lines
11 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- nl-video-api 数据库性能优化脚本
-- 执行前请备份数据库
-- ================================
-- 1. 索引优化
-- ================================
-- 用户表索引优化
ALTER TABLE `user` ADD INDEX `idx_username` (`username`);
ALTER TABLE `user` ADD INDEX `idx_phone` (`phone`);
ALTER TABLE `user` ADD INDEX `idx_email` (`email`);
ALTER TABLE `user` ADD INDEX `idx_status_vip` (`status`, `vip_level`);
ALTER TABLE `user` ADD INDEX `idx_created_at` (`created_at`);
ALTER TABLE `user` ADD INDEX `idx_last_login` (`last_login_at`);
-- 管理员表索引优化
ALTER TABLE `admin` ADD INDEX `idx_username` (`username`);
ALTER TABLE `admin` ADD INDEX `idx_email` (`email`);
ALTER TABLE `admin` ADD INDEX `idx_status` (`status`);
-- 影片表索引优化
ALTER TABLE `movie` ADD INDEX `idx_title` (`title`);
ALTER TABLE `movie` ADD INDEX `idx_type_category` (`type`, `category_id`);
ALTER TABLE `movie` ADD INDEX `idx_year` (`year`);
ALTER TABLE `movie` ADD INDEX `idx_country` (`country`);
ALTER TABLE `movie` ADD INDEX `idx_status` (`status`);
ALTER TABLE `movie` ADD INDEX `idx_rating` (`rating`);
ALTER TABLE `movie` ADD INDEX `idx_created_at` (`created_at`);
ALTER TABLE `movie` ADD INDEX `idx_updated_at` (`updated_at`);
-- 剧集表索引优化
ALTER TABLE `episode` ADD INDEX `idx_movie_id` (`movie_id`);
ALTER TABLE `episode` ADD INDEX `idx_episode_number` (`episode_number`);
ALTER TABLE `episode` ADD INDEX `idx_status` (`status`);
-- 分类表索引优化
ALTER TABLE `category` ADD INDEX `idx_parent_id` (`parent_id`);
ALTER TABLE `category` ADD INDEX `idx_sort` (`sort`);
ALTER TABLE `category` ADD INDEX `idx_status` (`status`);
-- 角色表索引优化
ALTER TABLE `role` ADD INDEX `idx_code` (`code`);
ALTER TABLE `role` ADD INDEX `idx_level` (`level`);
ALTER TABLE `role` ADD INDEX `idx_status` (`status`);
-- 权限表索引优化
ALTER TABLE `permission` ADD INDEX `idx_parent_id` (`parent_id`);
ALTER TABLE `permission` ADD INDEX `idx_type` (`type`);
ALTER TABLE `permission` ADD INDEX `idx_status` (`status`);
-- 角色权限关联表索引优化
ALTER TABLE `role_permission` ADD INDEX `idx_role_id` (`role_id`);
ALTER TABLE `role_permission` ADD INDEX `idx_permission_id` (`permission_id`);
-- ================================
-- 2. 表结构优化
-- ================================
-- 优化用户表字段类型
ALTER TABLE `user` MODIFY COLUMN `balance` DECIMAL(10,2) DEFAULT 0.00 COMMENT '余额';
ALTER TABLE `user` MODIFY COLUMN `points` INT UNSIGNED DEFAULT 0 COMMENT '积分';
ALTER TABLE `user` MODIFY COLUMN `vip_level` TINYINT UNSIGNED DEFAULT 0 COMMENT 'VIP等级';
-- 优化影片表字段类型
ALTER TABLE `movie` MODIFY COLUMN `rating` DECIMAL(3,1) DEFAULT 0.0 COMMENT '评分';
ALTER TABLE `movie` MODIFY COLUMN `duration` SMALLINT UNSIGNED DEFAULT 0 COMMENT '时长(分钟)';
ALTER TABLE `movie` MODIFY COLUMN `total_episodes` SMALLINT UNSIGNED DEFAULT 0 COMMENT '总集数';
-- 优化角色表字段类型
ALTER TABLE `role` MODIFY COLUMN `level` TINYINT UNSIGNED DEFAULT 1 COMMENT '角色等级';
ALTER TABLE `role` MODIFY COLUMN `sort` SMALLINT UNSIGNED DEFAULT 0 COMMENT '排序';
-- ================================
-- 3. 分区优化适用于大数据量
-- ================================
-- 用户表按注册时间分区年度分区
-- 注意分区需要在表创建时定义这里仅作为参考
/*
ALTER TABLE `user` PARTITION BY RANGE (YEAR(created_at)) (
PARTITION p2023 VALUES LESS THAN (2024),
PARTITION p2024 VALUES LESS THAN (2025),
PARTITION p2025 VALUES LESS THAN (2026),
PARTITION p_future VALUES LESS THAN MAXVALUE
);
*/
-- ================================
-- 4. 查询优化视图
-- ================================
-- 创建用户统计视图
CREATE OR REPLACE VIEW `v_user_stats` AS
SELECT
COUNT(*) as total_users,
COUNT(CASE WHEN status = 1 THEN 1 END) as active_users,
COUNT(CASE WHEN vip_level > 0 THEN 1 END) as vip_users,
COUNT(CASE WHEN DATE(created_at) = CURDATE() THEN 1 END) as today_new_users,
AVG(balance) as avg_balance,
SUM(balance) as total_balance
FROM `user`;
-- 创建影片统计视图
CREATE OR REPLACE VIEW `v_movie_stats` AS
SELECT
COUNT(*) as total_movies,
COUNT(CASE WHEN type = 1 THEN 1 END) as movie_count,
COUNT(CASE WHEN type = 2 THEN 1 END) as tv_series_count,
COUNT(CASE WHEN status = 1 THEN 1 END) as published_count,
AVG(rating) as avg_rating,
COUNT(CASE WHEN DATE(created_at) = CURDATE() THEN 1 END) as today_new_movies
FROM `movie`;
-- 创建热门影片视图
CREATE OR REPLACE VIEW `v_popular_movies` AS
SELECT
m.id,
m.title,
m.type,
m.rating,
m.year,
c.name as category_name
FROM `movie` m
LEFT JOIN `category` c ON m.category_id = c.id
WHERE m.status = 1
ORDER BY m.rating DESC, m.created_at DESC;
-- ================================
-- 5. 存储过程优化
-- ================================
-- 用户余额更新存储过程
DELIMITER //
CREATE PROCEDURE `sp_update_user_balance`(
IN p_user_id INT,
IN p_amount DECIMAL(10,2),
IN p_type TINYINT,
IN p_remark VARCHAR(255)
)
BEGIN
DECLARE v_current_balance DECIMAL(10,2) DEFAULT 0;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
RESIGNAL;
END;
START TRANSACTION;
-- 获取当前余额
SELECT balance INTO v_current_balance FROM `user` WHERE id = p_user_id FOR UPDATE;
-- 检查余额是否足够扣款时
IF p_type = 2 AND v_current_balance < ABS(p_amount) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '余额不足';
END IF;
-- 更新余额
IF p_type = 1 THEN
UPDATE `user` SET balance = balance + p_amount WHERE id = p_user_id;
ELSE
UPDATE `user` SET balance = balance - ABS(p_amount) WHERE id = p_user_id;
END IF;
-- 记录余额变动日志如果有日志表的话
-- INSERT INTO `balance_log` (user_id, amount, type, remark, created_at)
-- VALUES (p_user_id, p_amount, p_type, p_remark, NOW());
COMMIT;
END //
DELIMITER ;
-- VIP升级存储过程
DELIMITER //
CREATE PROCEDURE `sp_upgrade_user_vip`(
IN p_user_id INT,
IN p_vip_level TINYINT,
IN p_days INT
)
BEGIN
DECLARE v_current_vip_expire DATETIME;
DECLARE v_new_vip_expire DATETIME;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
RESIGNAL;
END;
START TRANSACTION;
-- 获取当前VIP到期时间
SELECT vip_expire_at INTO v_current_vip_expire FROM `user` WHERE id = p_user_id;
-- 计算新的到期时间
IF v_current_vip_expire IS NULL OR v_current_vip_expire < NOW() THEN
SET v_new_vip_expire = DATE_ADD(NOW(), INTERVAL p_days DAY);
ELSE
SET v_new_vip_expire = DATE_ADD(v_current_vip_expire, INTERVAL p_days DAY);
END IF;
-- 更新用户VIP信息
UPDATE `user` SET
vip_level = p_vip_level,
vip_expire_at = v_new_vip_expire,
updated_at = NOW()
WHERE id = p_user_id;
COMMIT;
END //
DELIMITER ;
-- ================================
-- 6. 数据清理和维护
-- ================================
-- 清理过期的VIP用户
UPDATE `user` SET vip_level = 0 WHERE vip_expire_at < NOW() AND vip_level > 0;
-- 清理软删除的数据超过30天
DELETE FROM `movie` WHERE deleted_at IS NOT NULL AND deleted_at < DATE_SUB(NOW(), INTERVAL 30 DAY);
DELETE FROM `user` WHERE deleted_at IS NOT NULL AND deleted_at < DATE_SUB(NOW(), INTERVAL 30 DAY);
-- ================================
-- 7. 性能监控查询
-- ================================
-- 查看慢查询日志状态
SHOW VARIABLES LIKE 'slow_query_log%';
SHOW VARIABLES LIKE 'long_query_time';
-- 查看索引使用情况
SELECT
TABLE_SCHEMA,
TABLE_NAME,
INDEX_NAME,
CARDINALITY,
SUB_PART,
PACKED,
NULLABLE,
INDEX_TYPE
FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = 'nl_video_db'
ORDER BY TABLE_NAME, INDEX_NAME;
-- 查看表大小和行数
SELECT
TABLE_NAME,
TABLE_ROWS,
ROUND(((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024), 2) AS 'Size(MB)',
ROUND((DATA_LENGTH / 1024 / 1024), 2) AS 'Data(MB)',
ROUND((INDEX_LENGTH / 1024 / 1024), 2) AS 'Index(MB)'
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'nl_video_db'
ORDER BY (DATA_LENGTH + INDEX_LENGTH) DESC;
-- ================================
-- 8. 配置优化建议
-- ================================
/*
MySQL配置优化建议my.cnf
[mysqld]
# 基础配置
default-storage-engine = InnoDB
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
# 内存配置
innodb_buffer_pool_size = 1G # 设置为物理内存的70-80%
innodb_log_file_size = 256M
innodb_log_buffer_size = 16M
key_buffer_size = 256M
max_connections = 500
thread_cache_size = 50
# 查询缓存
query_cache_type = 1
query_cache_size = 256M
query_cache_limit = 2M
# 慢查询日志
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
# InnoDB配置
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
innodb_file_per_table = 1
innodb_open_files = 400
# 临时表配置
tmp_table_size = 256M
max_heap_table_size = 256M
# 排序和连接配置
sort_buffer_size = 2M
join_buffer_size = 2M
read_buffer_size = 1M
read_rnd_buffer_size = 1M
*/
-- ================================
-- 9. 定期维护脚本
-- ================================
-- 创建定期维护事件
DELIMITER //
CREATE EVENT IF NOT EXISTS `ev_daily_maintenance`
ON SCHEDULE EVERY 1 DAY
STARTS CURRENT_TIMESTAMP
DO
BEGIN
-- 更新过期VIP用户
UPDATE `user` SET vip_level = 0 WHERE vip_expire_at < NOW() AND vip_level > 0;
-- 优化表
OPTIMIZE TABLE `user`;
OPTIMIZE TABLE `movie`;
OPTIMIZE TABLE `episode`;
-- 分析表
ANALYZE TABLE `user`;
ANALYZE TABLE `movie`;
ANALYZE TABLE `episode`;
-- 记录维护日志
INSERT INTO `system_log` (type, message, created_at)
VALUES ('maintenance', 'Daily maintenance completed', NOW());
END //
DELIMITER ;
-- 启用事件调度器
SET GLOBAL event_scheduler = ON;
-- ================================
-- 10. 备份建议
-- ================================
/*
数据库备份脚本示例:
#!/bin/bash
# 数据库备份脚本
DB_NAME="nl_video_db"
DB_USER="root"
DB_PASS="password"
BACKUP_DIR="/backup/mysql"
DATE=$(date +%Y%m%d_%H%M%S)
# 创建备份目录
mkdir -p $BACKUP_DIR
# 全量备份
mysqldump -u$DB_USER -p$DB_PASS --single-transaction --routines --triggers $DB_NAME > $BACKUP_DIR/full_backup_$DATE.sql
# 压缩备份文件
gzip $BACKUP_DIR/full_backup_$DATE.sql
# 删除7天前的备份
find $BACKUP_DIR -name "full_backup_*.sql.gz" -mtime +7 -delete
echo "Backup completed: full_backup_$DATE.sql.gz"
*/
-- ================================
-- 执行完成提示
-- ================================
SELECT 'Database optimization completed successfully!' as message;
SELECT 'Please restart MySQL service to apply configuration changes.' as note;
SELECT 'Run SHOW PROCESSLIST; to monitor current queries.' as monitoring_tip;