"小王,咱们新上线的促销活动页面怎么加载这么慢?用户都投诉了!"产品经理老张急冲冲地跑过来,作为刚入职半年的后端开发,小王额头冒汗——他负责的正是电商平台的商品数据库模块,打开MySQL Workbench一看,商品表居然没加索引,上百万条数据全表扫描,难怪慢得像蜗牛...
这样的场景每天都在真实发生,MySQL作为最流行的开源关系型数据库,看似简单,但要在实际项目中用好它,可不是会几句SELECT、INSERT就能搞定的,今天我就带你从零开始,完成一个完整的MySQL数据库项目实战。
首先确保你已经安装了MySQL(推荐8.0以上版本),如果你用的是Mac,可以用Homebrew安装:
brew install mysql brew services start mysql
Windows用户可以直接从MySQL官网下载安装包,安装完成后,记得运行安全脚本:
mysql_secure_installation
登录MySQL后,我们创建一个名为ecommerce
的数据库:
CREATE DATABASE ecommerce CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE ecommerce;
注意使用了utf8mb4
字符集,这是为了完整支持emoji等特殊字符——现代电商系统必备。
CREATE TABLE users ( user_id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, email VARCHAR(100) NOT NULL UNIQUE, password_hash VARCHAR(255) NOT NULL, phone VARCHAR(20), avatar_url VARCHAR(255), status ENUM('active', 'inactive', 'banned') DEFAULT 'active', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB;
设计要点:
CREATE TABLE products ( product_id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) NOT NULL, description TEXT, price DECIMAL(10, 2) NOT NULL, stock INT NOT NULL DEFAULT 0, category_id INT, seller_id INT NOT NULL, is_featured BOOLEAN DEFAULT FALSE, rating DECIMAL(3, 2) DEFAULT 0.00, review_count INT DEFAULT 0, main_image_url VARCHAR(255), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FULLTEXT INDEX idx_search (name, description), FOREIGN KEY (category_id) REFERENCES categories(category_id), FOREIGN KEY (seller_id) REFERENCES users(user_id) ) ENGINE=InnoDB;
性能优化点:
CREATE TABLE orders ( order_id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, total_amount DECIMAL(12, 2) NOT NULL, status ENUM('pending', 'paid', 'shipped', 'delivered', 'cancelled') DEFAULT 'pending', shipping_address TEXT NOT NULL, payment_method ENUM('credit_card', 'paypal', 'bank_transfer') NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_user (user_id), INDEX idx_status (status), FOREIGN KEY (user_id) REFERENCES users(user_id) ) ENGINE=InnoDB; CREATE TABLE order_items ( item_id INT AUTO_INCREMENT PRIMARY KEY, order_id INT NOT NULL, product_id INT NOT NULL, quantity INT NOT NULL, unit_price DECIMAL(10, 2) NOT NULL, FOREIGN KEY (order_id) REFERENCES orders(order_id) ON DELETE CASCADE, FOREIGN KEY (product_id) REFERENCES products(product_id) ) ENGINE=InnoDB;
事务处理示例(下单减库存):
START TRANSACTION; -- 1. 扣减库存 UPDATE products SET stock = stock - 1 WHERE product_id = 123 AND stock >= 1; -- 2. 创建订单 INSERT INTO orders (user_id, total_amount, status, shipping_address, payment_method) VALUES (456, 99.99, 'pending', '上海市浦东新区...', 'credit_card'); SET @last_order_id = LAST_INSERT_ID(); -- 3. 添加订单项 INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (@last_order_id, 123, 1, 99.99); -- 如果以上任何一步失败则回滚 COMMIT;
假设我们发现这个查询很慢:
SELECT * FROM products WHERE category_id = 5 ORDER BY created_at DESC LIMIT 20;
优化步骤:
使用EXPLAIN分析:
EXPLAIN SELECT * FROM products WHERE category_id = 5 ORDER BY created_at DESC LIMIT 20;
发现没有合适的索引,添加复合索引:
ALTER TABLE products ADD INDEX idx_category_created (category_id, created_at);
进一步优化只查询必要字段:
SELECT product_id, name, price, main_image_url FROM products WHERE category_id = 5 ORDER BY created_at DESC LIMIT 20;
常见错误写法(大数据量时性能差):
SELECT * FROM products LIMIT 10000, 20;
优化方案(利用主键):
SELECT * FROM products WHERE product_id > 10000 ORDER BY product_id LIMIT 20;
创建计算商品销售排名的存储过程:
DELIMITER // CREATE PROCEDURE CalculateProductRankings(IN days INT) BEGIN -- 创建临时表存储结果 DROP TEMPORARY TABLE IF EXISTS temp_product_rankings; CREATE TEMPORARY TABLE temp_product_rankings ( product_id INT PRIMARY KEY, sales_volume INT, sales_amount DECIMAL(12, 2), ranking INT ); -- 计算销量和销售额 INSERT INTO temp_product_rankings (product_id, sales_volume, sales_amount) SELECT oi.product_id, SUM(oi.quantity) AS sales_volume, SUM(oi.quantity * oi.unit_price) AS sales_amount FROM order_items oi JOIN orders o ON oi.order_id = o.order_id WHERE o.status = 'delivered' AND o.created_at >= DATE_SUB(NOW(), INTERVAL days DAY) GROUP BY oi.product_id; -- 计算排名 SET @rank = 0; UPDATE temp_product_rankings SET ranking = (@rank := @rank + 1) ORDER BY sales_amount DESC; -- 返回结果 SELECT p.product_id, p.name, p.main_image_url, tr.sales_volume, tr.sales_amount, tr.ranking FROM temp_product_rankings tr JOIN products p ON tr.product_id = p.product_id ORDER BY tr.ranking; END // DELIMITER ; -- 调用示例 CALL CalculateProductRankings(30); -- 最近30天销售排名
创建触发器自动更新商品评分:
DELIMITER // CREATE TRIGGER update_product_rating AFTER INSERT ON product_reviews FOR EACH ROW BEGIN DECLARE avg_rating DECIMAL(3,2); SELECT AVG(rating) INTO avg_rating FROM product_reviews WHERE product_id = NEW.product_id; UPDATE products SET rating = avg_rating, review_count = (SELECT COUNT(*) FROM product_reviews WHERE product_id = NEW.product_id) WHERE product_id = NEW.product_id; END // DELIMITER ;
通过这个电商数据库项目实战,我们覆盖了MySQL开发的完整流程,总结几个关键经验:
好的数据库设计不是一蹴而就的,随着业务发展,你需要持续监控和优化,比如当单表数据超过千万时,就要考虑分库分表方案了。
当产品经理再跑来问为什么页面加载慢时,你就能自信地打开MySQL,快速定位问题并给出优化方案了!
本文由 泥亦竹 于2025-08-02发表在【云服务器提供商】,文中图片由(泥亦竹)上传,本平台仅提供信息存储服务;作者观点、意见不代表本站立场,如有侵权,请联系我们删除;若有图片侵权,请您准备原始证明材料和公证书后联系我方删除!
本文链接:https://vps.7tqx.com/wenda/513010.html
发表评论