数据库设计与优化实践
数据库设计是系统架构的核心,良好的设计直接影响应用性能和可维护性。
数据库设计原则
范式化理论
第一范式 (1NF)
CREATE TABLE users_orders ( user_id INT, name VARCHAR(100), orders TEXT );
CREATE TABLE users ( user_id INT PRIMARY KEY, name VARCHAR(100) );
CREATE TABLE orders ( order_id INT PRIMARY KEY, user_id INT, FOREIGN KEY (user_id) REFERENCES users(user_id) );
|
第二范式 (2NF)
CREATE TABLE order_details ( order_id INT, product_id INT, product_name VARCHAR(100), quantity INT, price DECIMAL(10,2) );
CREATE TABLE products ( product_id INT PRIMARY KEY, product_name VARCHAR(100), price DECIMAL(10,2) );
CREATE TABLE order_details ( order_id INT, product_id INT, quantity INT, FOREIGN KEY (product_id) REFERENCES products(product_id) );
|
第三范式 (3NF)
CREATE TABLE employees ( emp_id INT PRIMARY KEY, emp_name VARCHAR(100), dept_id INT, dept_name VARCHAR(100) );
CREATE TABLE departments ( dept_id INT PRIMARY KEY, dept_name VARCHAR(100) );
CREATE TABLE employees ( emp_id INT PRIMARY KEY, emp_name VARCHAR(100), dept_id INT, FOREIGN KEY (dept_id) REFERENCES departments(dept_id) );
|
反范式化
CREATE TABLE order_summary ( order_id INT PRIMARY KEY, user_id INT, total_amount DECIMAL(10,2), item_count INT, created_at TIMESTAMP );
CREATE TRIGGER update_order_summary AFTER INSERT ON order_details FOR EACH ROW BEGIN UPDATE order_summary SET total_amount = ( SELECT SUM(quantity * unit_price) FROM order_details WHERE order_id = NEW.order_id ), item_count = ( SELECT COUNT(*) FROM order_details WHERE order_id = NEW.order_id ) WHERE order_id = NEW.order_id; END;
|
索引设计
索引类型
CREATE INDEX idx_user_email ON users(email);
CREATE UNIQUE INDEX idx_user_username ON users(username);
CREATE INDEX idx_order_user_date ON orders(user_id, order_date);
CREATE INDEX idx_active_users ON users(id) WHERE status = 'active';
CREATE INDEX idx_user_email_lower ON users(LOWER(email));
CREATE FULLTEXT INDEX idx_article_content ON articles(title, content);
|
索引优化策略
CREATE INDEX idx_order_covering ON orders(user_id, status, total_amount);
SELECT COUNT(DISTINCT email) / COUNT(*) FROM users;
|
查询优化
执行计划分析
EXPLAIN SELECT * FROM orders WHERE user_id = 1 AND status = 'pending';
EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 1 AND status = 'pending';
|
查询优化技巧
SELECT id, name, email FROM users WHERE status = 'active';
SELECT id, name FROM users LIMIT 10 OFFSET 20;
SELECT u.name, o.total_amount FROM users u INNER JOIN orders o ON u.id = o.user_id WHERE u.status = 'active';
SELECT * FROM users WHERE id IN (SELECT user_id FROM orders WHERE total > 1000);
SELECT u.* FROM users u INNER JOIN orders o ON u.id = o.user_id WHERE o.total > 1000 GROUP BY u.id;
INSERT INTO users (name, email) VALUES ('John', 'john@example.com'), ('Jane', 'jane@example.com'), ('Bob', 'bob@example.com');
|
数据库性能优化
连接池配置
spring: datasource: hikari: maximum-pool-size: 20 minimum-idle: 5 idle-timeout: 300000 max-lifetime: 1200000 connection-timeout: 20000 leak-detection-threshold: 60000
|
分页优化
SELECT * FROM orders ORDER BY created_at LIMIT 10 OFFSET 1000000;
SELECT * FROM orders WHERE created_at > '2024-01-01 00:00:00' ORDER BY created_at LIMIT 10;
SELECT * FROM orders WHERE id > 1000000 ORDER BY id LIMIT 10;
|
批量操作
DELETE FROM orders WHERE status = 'cancelled' AND created_at < '2023-01-01' LIMIT 1000;
UPDATE products SET price = price * 0.9 WHERE category = 'electronics';
CREATE TEMPORARY TABLE temp_updates ( product_id INT, new_price DECIMAL(10,2) );
INSERT INTO temp_updates VALUES (1, 99.99), (2, 149.99);
UPDATE products p JOIN temp_updates t ON p.id = t.product_id SET p.price = t.new_price;
|
分库分表
水平分表
CREATE TABLE orders_0 LIKE orders; CREATE TABLE orders_1 LIKE orders;
function getTableSuffix(userId) { return userId % 10; }
// 查询示例 SELECT * FROM orders_5 WHERE user_id = 12345;
|
垂直分库
读写分离
spring: datasource: master: url: jdbc:mysql://master-db:3306/app username: master_user password: master_pass slave: url: jdbc:mysql://slave-db:3306/app username: slave_user password: slave_pass @ReadOnly public List<Order> getOrdersByUser(Long userId) { // 读操作走从库 }
@WriteOnly public void createOrder(Order order) { // 写操作走主库 }
|
缓存策略
查询缓存
SET GLOBAL query_cache_size = 67108864; SET GLOBAL query_cache_type = ON;
SHOW STATUS LIKE 'Qcache%';
|
应用层缓存
@Service public class ProductService { @Cacheable(value = "products", key = "#id") public Product getProductById(Long id) { return productRepository.findById(id); } @CacheEvict(value = "products", key = "#product.id") public void updateProduct(Product product) { productRepository.save(product); } @CacheEvict(value = "products", allEntries = true) public void clearProductCache() { } }
|
监控和维护
性能监控
SHOW PROCESSLIST; SHOW ENGINE INNODB STATUS; SHOW VARIABLES LIKE 'innodb%';
SET GLOBAL slow_query_log = ON; SET GLOBAL long_query_time = 2;
ANALYZE TABLE products; ANALYZE TABLE orders;
OPTIMIZE TABLE products;
|
定期维护
DELETE FROM log_entries WHERE created_at < DATE_SUB(NOW(), INTERVAL 30 DAY);
ALTER TABLE orders REBUILD INDEX idx_user_date;
CHECK TABLE orders; REPAIR TABLE orders;
mysqldump
|
安全考虑
权限管理
CREATE USER 'app_user'@'%' IDENTIFIED BY 'strong_password';
GRANT SELECT, INSERT, UPDATE ON app_db.orders TO 'app_user'@'%'; GRANT SELECT ON app_db.products TO 'app_user'@'%';
CREATE USER 'report_user'@'192.168.1.%' IDENTIFIED BY 'password';
|
数据加密
CREATE TABLE sensitive_data ( id INT PRIMARY KEY, credit_card VARBINARY(255), ssn VARBINARY(255) );
// Java 加密示例 String encrypted = AES.encrypt(creditCardNumber, secretKey);
|
最佳实践总结
- 合理设计表结构,遵循范式化原则
- 创建合适的索引,避免过度索引
- 定期监控性能,及时发现慢查询
- 使用连接池,避免频繁创建连接
- 实施缓存策略,减少数据库压力
- 定期备份,确保数据安全
- 权限最小化,提高安全性
[!tip]
- 使用 EXPLAIN 分析查询执行计划
- 定期优化表结构和索引
- 监控数据库性能指标
- 建立完善的备份恢复机制
参考资料