404 Not Found

404 Not Found


nginx

综合项目:博客系统

综合项目:博客系统

本课是 SQL 教程的综合实战。我们将运用前面学到的所有知识,从零开始设计并实现一个完整博客系统的数据库。

🎯 生活类比

搭建博客系统就像开一家书店:

一、需求分析

功能需求

模块 功能
用户 注册、登录、个人资料、关注其他用户
文章 发布、编辑、删除(软删除)、草稿、发布
分类 文章分类管理(支持层级分类)
标签 文章打标签(多对多)
评论 文章评论、嵌套回复
统计 热门文章排行、用户统计、标签统计

数据量预估

预估数据量 读写比
users 10万 读多写少
articles 100万 读远多于写
comments 500万 读写均衡
tags 1000 读多写少
categories 100 读多写少

二、数据库设计

ER 图

[users] 1 ──── N [articles] N ──── N [tags]
   │                  │
   │                  │ 1
   │                  │
   │                  N
   │             [comments]
   │                  │
   N                  │ (自引用)
   │                  │
[users] 1 ──── N [comments]

[categories] 1 ──── N [articles]
     │
     └── 1 (自引用,parent_id)

关系说明

关系 类型 实现方式
用户 → 文章 一对多 articles.user_id
文章 → 标签 多对多 article_tag 中间表
文章 → 评论 一对多 comments.article_id
用户 → 评论 一对多 comments.user_id
评论 → 评论 自引用(嵌套) comments.parent_id
分类 → 文章 一对多 articles.category_id
分类 → 分类 自引用(层级) categories.parent_id
用户 → 用户 多对多(关注) user_follows 中间表

三、建表语句

SQL
-- =============================================
-- 博客系统数据库建表脚本
-- 字符集: utf8mb4(支持 emoji 和多语言)
-- 存储引擎: InnoDB(支持事务和外键)
-- =============================================

-- 创建数据库
CREATE DATABASE IF NOT EXISTS blog_system
    DEFAULT CHARACTER SET utf8mb4
    DEFAULT COLLATE utf8mb4_unicode_ci;

USE blog_system;

-- ----------------------------
-- 1. 用户表
-- ----------------------------
CREATE TABLE users (
    id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '用户ID',
    username VARCHAR(50) NOT NULL COMMENT '用户名',
    email VARCHAR(100) NOT NULL COMMENT '邮箱',
    password_hash VARCHAR(255) NOT NULL COMMENT '密码哈希',
    nickname VARCHAR(50) COMMENT '昵称',
    avatar_url VARCHAR(500) COMMENT '头像URL',
    bio VARCHAR(500) COMMENT '个人简介',
    website VARCHAR(200) COMMENT '个人网站',
    status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 1-正常 0-禁用 2-待验证',
    role VARCHAR(20) NOT NULL DEFAULT 'user' COMMENT '角色: user/author/admin',
    article_count INT NOT NULL DEFAULT 0 COMMENT '文章数(冗余字段)',
    follower_count INT NOT NULL DEFAULT 0 COMMENT '粉丝数(冗余字段)',
    last_login_at DATETIME COMMENT '最后登录时间',
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间',
    updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    is_deleted TINYINT NOT NULL DEFAULT 0 COMMENT '软删除: 0-正常 1-已删除',
    UNIQUE KEY uk_username (username),
    UNIQUE KEY uk_email (email),
    INDEX idx_status (status),
    INDEX idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';

-- ----------------------------
-- 2. 用户关注关系表(多对多)
-- ----------------------------
CREATE TABLE user_follows (
    follower_id BIGINT NOT NULL COMMENT '关注者ID',
    following_id BIGINT NOT NULL COMMENT '被关注者ID',
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '关注时间',
    PRIMARY KEY (follower_id, following_id),
    INDEX idx_following_id (following_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户关注关系表';

-- ----------------------------
-- 3. 分类表(支持层级分类)
-- ----------------------------
CREATE TABLE categories (
    id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '分类ID',
    name VARCHAR(50) NOT NULL COMMENT '分类名称',
    slug VARCHAR(100) NOT NULL COMMENT 'URL 别名',
    description VARCHAR(200) COMMENT '分类描述',
    parent_id BIGINT DEFAULT NULL COMMENT '父分类ID,NULL表示顶级分类',
    sort_order INT NOT NULL DEFAULT 0 COMMENT '排序',
    article_count INT NOT NULL DEFAULT 0 COMMENT '文章数(冗余字段)',
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    UNIQUE KEY uk_slug (slug),
    INDEX idx_parent_id (parent_id),
    INDEX idx_sort (sort_order)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分类表';

-- ----------------------------
-- 4. 标签表
-- ----------------------------
CREATE TABLE tags (
    id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '标签ID',
    name VARCHAR(50) NOT NULL COMMENT '标签名称',
    slug VARCHAR(100) NOT NULL COMMENT 'URL 别名',
    article_count INT NOT NULL DEFAULT 0 COMMENT '文章数(冗余字段)',
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    UNIQUE KEY uk_name (name),
    UNIQUE KEY uk_slug (slug)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='标签表';

-- ----------------------------
-- 5. 文章表
-- ----------------------------
CREATE TABLE articles (
    id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '文章ID',
    user_id BIGINT NOT NULL COMMENT '作者ID',
    category_id BIGINT COMMENT '分类ID',
    title VARCHAR(200) NOT NULL COMMENT '标题',
    slug VARCHAR(200) NOT NULL COMMENT 'URL 别名',
    summary VARCHAR(500) COMMENT '摘要',
    content LONGTEXT NOT NULL COMMENT '正文内容',
    cover_image_url VARCHAR(500) COMMENT '封面图片',
    status TINYINT NOT NULL DEFAULT 0 COMMENT '状态: 0-草稿 1-已发布 2-已下架',
    is_pinned TINYINT NOT NULL DEFAULT 0 COMMENT '是否置顶: 0-否 1-是',
    view_count INT NOT NULL DEFAULT 0 COMMENT '浏览数',
    like_count INT NOT NULL DEFAULT 0 COMMENT '点赞数',
    comment_count INT NOT NULL DEFAULT 0 COMMENT '评论数(冗余字段)',
    published_at DATETIME COMMENT '发布时间',
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    is_deleted TINYINT NOT NULL DEFAULT 0 COMMENT '软删除: 0-正常 1-已删除',
    UNIQUE KEY uk_slug (slug),
    INDEX idx_user_id (user_id),
    INDEX idx_category_id (category_id),
    INDEX idx_status_published (status, published_at DESC),
    INDEX idx_is_pinned (is_pinned),
    INDEX idx_created_at (created_at DESC),
    FULLTEXT INDEX ft_title_content (title, content)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='文章表';

-- ----------------------------
-- 6. 文章-标签关联表(多对多)
-- ----------------------------
CREATE TABLE article_tag (
    article_id BIGINT NOT NULL COMMENT '文章ID',
    tag_id BIGINT NOT NULL COMMENT '标签ID',
    PRIMARY KEY (article_id, tag_id),
    INDEX idx_tag_id (tag_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章标签关联表';

-- ----------------------------
-- 7. 评论表(支持嵌套回复)
-- ----------------------------
CREATE TABLE comments (
    id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '评论ID',
    article_id BIGINT NOT NULL COMMENT '文章ID',
    user_id BIGINT NOT NULL COMMENT '评论者ID',
    parent_id BIGINT DEFAULT NULL COMMENT '父评论ID,NULL表示顶级评论',
    reply_to_user_id BIGINT DEFAULT NULL COMMENT '被回复的用户ID',
    content TEXT NOT NULL COMMENT '评论内容',
    like_count INT NOT NULL DEFAULT 0 COMMENT '点赞数',
    status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-待审核 1-已通过 2-已拒绝',
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    is_deleted TINYINT NOT NULL DEFAULT 0 COMMENT '软删除',
    INDEX idx_article_id (article_id),
    INDEX idx_user_id (user_id),
    INDEX idx_parent_id (parent_id),
    INDEX idx_created_at (created_at DESC)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='评论表';

-- ----------------------------
-- 8. 文章点赞表
-- ----------------------------
CREATE TABLE article_likes (
    user_id BIGINT NOT NULL COMMENT '用户ID',
    article_id BIGINT NOT NULL COMMENT '文章ID',
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (user_id, article_id),
    INDEX idx_article_id (article_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章点赞表';

四、CRUD 操作

用户管理

SQL
-- 注册新用户
INSERT INTO users (username, email, password_hash, nickname)
VALUES ('zhangsan', 'zhangsan@example.com', SHA2('mypassword123', 256), '张三');

-- 用户登录(根据用户名查询)
SELECT id, username, nickname, avatar_url, role
FROM users
WHERE username = 'zhangsan' AND password_hash = SHA2('mypassword123', 256)
    AND status = 1 AND is_deleted = 0;

-- 更新个人资料
UPDATE users
SET nickname = '张三丰',
    bio = '热爱编程,热爱生活',
    website = 'https://zhangsan.dev'
WHERE id = 1 AND is_deleted = 0;

-- 软删除用户
UPDATE users SET is_deleted = 1 WHERE id = 1;

-- 查询用户详情(排除已删除)
SELECT id, username, email, nickname, avatar_url, bio, website,
       article_count, follower_count, created_at
FROM users
WHERE id = 1 AND is_deleted = 0;

分类管理

SQL
-- 创建顶级分类
INSERT INTO categories (name, slug, description, sort_order)
VALUES ('技术', 'tech', '技术相关文章', 1),
       ('生活', 'life', '生活随笔', 2),
       ('读书', 'reading', '读书笔记', 3);

-- 创建子分类
INSERT INTO categories (name, slug, description, parent_id, sort_order)
VALUES ('前端', 'frontend', '前端开发技术', 1, 1),
       ('后端', 'backend', '后端开发技术', 1, 2),
       ('数据库', 'database', '数据库技术', 1, 3),
       ('JavaScript', 'javascript', 'JavaScript 语言', 4, 1),
       ('Python', 'python', 'Python 语言', 5, 1);

-- 查询分类树
SELECT
    c1.id, c1.name AS level1,
    c2.name AS level2,
    c3.name AS level3
FROM categories c1
LEFT JOIN categories c2 ON c2.parent_id = c1.id
LEFT JOIN categories c3 ON c3.parent_id = c2.id
WHERE c1.parent_id IS NULL
ORDER BY c1.sort_order, c2.sort_order, c3.sort_order;

标签管理

SQL
-- 创建标签
INSERT INTO tags (name, slug) VALUES
    ('MySQL', 'mysql'),
    ('JavaScript', 'javascript'),
    ('Python', 'python'),
    ('Docker', 'docker'),
    ('Redis', 'redis'),
    ('Vue.js', 'vuejs'),
    ('React', 'react'),
    ('算法', 'algorithms');

-- 查询标签(按文章数排序)
SELECT t.id, t.name, t.slug, t.article_count
FROM tags t
ORDER BY t.article_count DESC
LIMIT 20;

文章管理

SQL
-- 创建文章(草稿)
INSERT INTO articles (user_id, category_id, title, slug, summary, content, status)
VALUES (
    1,
    7,  -- JavaScript 分类
    'MySQL索引优化实战指南',
    'mysql-index-optimization-guide',
    '本文详细讲解MySQL索引优化的核心策略和实战技巧',
    '# MySQL索引优化\n\n## 什么是索引\n\n索引是...',
    0  -- 草稿
);

-- 发布文章
UPDATE articles
SET status = 1,
    published_at = NOW()
WHERE id = 1 AND user_id = 1;

-- 给文章打标签
INSERT INTO article_tag (article_id, tag_id) VALUES
    (1, 1),  -- MySQL
    (1, 8);  -- 算法

-- 更新文章标签(先删后插)
DELETE FROM article_tag WHERE article_id = 1;
INSERT INTO article_tag (article_id, tag_id) VALUES
    (1, 1),
    (1, 5);  -- MySQL + Redis

-- 软删除文章
UPDATE articles SET is_deleted = 1 WHERE id = 1 AND user_id = 1;

-- 更新冗余字段:用户文章数
UPDATE users SET article_count = (
    SELECT COUNT(*) FROM articles WHERE user_id = 1 AND is_deleted = 0
) WHERE id = 1;

-- 更新冗余字段:分类文章数
UPDATE categories SET article_count = (
    SELECT COUNT(*) FROM articles WHERE category_id = 7 AND status = 1 AND is_deleted = 0
) WHERE id = 7;

-- 更新冗余字段:标签文章数
UPDATE tags SET article_count = (
    SELECT COUNT(*) FROM article_tag WHERE tag_id = 1
) WHERE id = 1;

评论管理

SQL
-- 发表评论
INSERT INTO comments (article_id, user_id, content)
VALUES (1, 2, '这篇文章写得很好,学到了很多!');

-- 回复评论
INSERT INTO comments (article_id, user_id, parent_id, reply_to_user_id, content)
VALUES (1, 3, 1, 2, '我也觉得,特别是索引那部分讲得很清楚');

-- 更新文章评论数
UPDATE articles SET comment_count = (
    SELECT COUNT(*) FROM comments WHERE article_id = 1 AND status = 1 AND is_deleted = 0
) WHERE id = 1;

-- 软删除评论
UPDATE comments SET is_deleted = 1 WHERE id = 1 AND user_id = 2;

用户关注

SQL
-- 关注用户
INSERT INTO user_follows (follower_id, following_id) VALUES (2, 1);
-- 更新粉丝数冗余字段
UPDATE users SET follower_count = follower_count + 1 WHERE id = 1;

-- 取消关注
DELETE FROM user_follows WHERE follower_id = 2 AND following_id = 1;
UPDATE users SET follower_count = follower_count - 1 WHERE id = 1;

-- 查询我的关注列表
SELECT u.id, u.username, u.nickname, u.avatar_url, u.bio
FROM user_follows f
JOIN users u ON f.following_id = u.id
WHERE f.follower_id = 2 AND u.is_deleted = 0
ORDER BY f.created_at DESC;

-- 查询我的粉丝列表
SELECT u.id, u.username, u.nickname, u.avatar_url, u.bio
FROM user_follows f
JOIN users u ON f.follower_id = u.id
WHERE f.following_id = 1 AND u.is_deleted = 0
ORDER BY f.created_at DESC;

五、复杂查询

热门文章排行(近30天,综合浏览量和点赞数)

SQL
SELECT
    a.id,
    a.title,
    a.slug,
    a.view_count,
    a.like_count,
    a.comment_count,
    a.published_at,
    u.username AS author,
    u.avatar_url AS author_avatar,
    c.name AS category_name,
    -- 综合热度分:浏览量×1 + 点赞数×5 + 评论数×3
    (a.view_count * 1 + a.like_count * 5 + a.comment_count * 3) AS hot_score,
    GROUP_CONCAT(t.name ORDER BY t.name SEPARATOR ', ') AS tags
FROM articles a
JOIN users u ON a.user_id = u.id
LEFT JOIN categories c ON a.category_id = c.id
LEFT JOIN article_tag at2 ON a.id = at2.article_id
LEFT JOIN tags t ON at2.tag_id = t.id
WHERE a.status = 1
    AND a.is_deleted = 0
    AND a.published_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY a.id
ORDER BY hot_score DESC
LIMIT 20;

用户统计数据面板

SQL
SELECT
    u.id,
    u.username,
    u.nickname,
    u.article_count,
    u.follower_count,
    -- 总浏览量
    IFNULL(SUM(a.view_count), 0) AS total_views,
    -- 总点赞数
    IFNULL(SUM(a.like_count), 0) AS total_likes,
    -- 总评论数
    IFNULL(SUM(a.comment_count), 0) AS total_comments,
    -- 最近发布文章时间
    MAX(a.published_at) AS last_published_at,
    -- 平均每篇文章浏览量
    IFNULL(ROUND(AVG(a.view_count), 0), 0) AS avg_views_per_article
FROM users u
LEFT JOIN articles a ON u.id = a.user_id AND a.status = 1 AND a.is_deleted = 0
WHERE u.id = 1 AND u.is_deleted = 0
GROUP BY u.id;

文章详情页(含标签列表)

SQL
SELECT
    a.id,
    a.title,
    a.content,
    a.cover_image_url,
    a.view_count,
    a.like_count,
    a.comment_count,
    a.published_at,
    a.updated_at,
    u.id AS author_id,
    u.username AS author_username,
    u.nickname AS author_nickname,
    u.avatar_url AS author_avatar,
    u.bio AS author_bio,
    c.name AS category_name,
    c.slug AS category_slug
FROM articles a
JOIN users u ON a.user_id = u.id
LEFT JOIN categories c ON a.category_id = c.id
WHERE a.slug = 'mysql-index-optimization-guide'
    AND a.status = 1
    AND a.is_deleted = 0;

-- 查询文章标签
SELECT t.id, t.name, t.slug
FROM article_tag at2
JOIN tags t ON at2.tag_id = t.id
WHERE at2.article_id = 1;

-- 增加浏览量
UPDATE articles SET view_count = view_count + 1 WHERE id = 1;

文章评论列表(嵌套结构)

SQL
-- 查询顶级评论
SELECT
    c.id,
    c.content,
    c.like_count,
    c.created_at,
    u.id AS user_id,
    u.username,
    u.nickname,
    u.avatar_url
FROM comments c
JOIN users u ON c.user_id = u.id
WHERE c.article_id = 1
    AND c.parent_id IS NULL
    AND c.status = 1
    AND c.is_deleted = 0
ORDER BY c.created_at DESC
LIMIT 20;

-- 查询某条评论的回复
SELECT
    c.id,
    c.content,
    c.like_count,
    c.created_at,
    u.username,
    u.nickname,
    u.avatar_url,
    ru.username AS reply_to_username,
    ru.nickname AS reply_to_nickname
FROM comments c
JOIN users u ON c.user_id = u.id
LEFT JOIN users ru ON c.reply_to_user_id = ru.id
WHERE c.parent_id = 1
    AND c.status = 1
    AND c.is_deleted = 0
ORDER BY c.created_at ASC;

标签关联文章列表

SQL
-- 查询某个标签下的所有文章
SELECT
    a.id,
    a.title,
    a.slug,
    a.summary,
    a.view_count,
    a.like_count,
    a.published_at,
    u.username AS author,
    u.avatar_url AS author_avatar
FROM article_tag at2
JOIN articles a ON at2.article_id = a.id
JOIN users u ON a.user_id = u.id
WHERE at2.tag_id = 1
    AND a.status = 1
    AND a.is_deleted = 0
ORDER BY a.published_at DESC
LIMIT 20;

分类文章统计

SQL
-- 各分类文章数量和平均浏览量
SELECT
    c.id,
    c.name,
    c.slug,
    c.article_count,
    IFNULL(COUNT(a.id), 0) AS actual_count,
    IFNULL(ROUND(AVG(a.view_count)), 0) AS avg_views,
    IFNULL(MAX(a.published_at), NULL) AS latest_article_at
FROM categories c
LEFT JOIN articles a ON c.id = a.category_id
    AND a.status = 1 AND a.is_deleted = 0
GROUP BY c.id
ORDER BY c.sort_order;

-- 查询某分类及其所有子分类的文章数
WITH RECURSIVE category_tree AS (
    SELECT id, name, parent_id
    FROM categories
    WHERE id = 1  -- 技术分类
    UNION ALL
    SELECT c.id, c.name, c.parent_id
    FROM categories c
    JOIN category_tree ct ON c.parent_id = ct.id
)
SELECT
    ct.id,
    ct.name,
    COUNT(a.id) AS article_count
FROM category_tree ct
LEFT JOIN articles a ON a.category_id = ct.id AND a.status = 1 AND a.is_deleted = 0
GROUP BY ct.id, ct.name;

关注用户的文章流

SQL
-- 查询我关注的用户发布的最新文章
SELECT
    a.id,
    a.title,
    a.slug,
    a.summary,
    a.view_count,
    a.like_count,
    a.published_at,
    u.username AS author,
    u.nickname AS author_nickname,
    u.avatar_url AS author_avatar
FROM user_follows f
JOIN articles a ON f.following_id = a.user_id
JOIN users u ON a.user_id = u.id
WHERE f.follower_id = 2
    AND a.status = 1
    AND a.is_deleted = 0
ORDER BY a.published_at DESC
LIMIT 20;

六、视图创建

SQL
-- 已发布文章视图(常用,简化查询)
CREATE VIEW v_published_articles AS
SELECT
    a.id,
    a.user_id,
    a.category_id,
    a.title,
    a.slug,
    a.summary,
    a.cover_image_url,
    a.status,
    a.is_pinned,
    a.view_count,
    a.like_count,
    a.comment_count,
    a.published_at,
    a.created_at,
    u.username AS author_username,
    u.nickname AS author_nickname,
    u.avatar_url AS author_avatar,
    c.name AS category_name,
    c.slug AS category_slug
FROM articles a
JOIN users u ON a.user_id = u.id
LEFT JOIN categories c ON a.category_id = c.id
WHERE a.status = 1 AND a.is_deleted = 0;

-- 使用视图查询
SELECT * FROM v_published_articles ORDER BY published_at DESC LIMIT 20;
SELECT * FROM v_published_articles WHERE category_id = 1 ORDER BY view_count DESC;

-- 文章标签汇总视图
CREATE VIEW v_article_tags AS
SELECT
    at2.article_id,
    GROUP_CONCAT(t.name ORDER BY t.name SEPARATOR ', ') AS tag_names,
    GROUP_CONCAT(t.slug ORDER BY t.name SEPARATOR ', ') AS tag_slugs,
    COUNT(t.id) AS tag_count
FROM article_tag at2
JOIN tags t ON at2.tag_id = t.id
GROUP BY at2.article_id;

-- 带标签的文章查询
SELECT
    vpa.*,
    vat.tag_names,
    vat.tag_count
FROM v_published_articles vpa
LEFT JOIN v_article_tags vat ON vpa.id = vat.article_id
ORDER BY vpa.published_at DESC
LIMIT 20;

七、索引优化

SQL
-- 1. 文章列表查询优化(覆盖索引)
-- 针对 WHERE status=1 AND is_deleted=0 ORDER BY published_at DESC
CREATE INDEX idx_articles_list ON articles(status, is_deleted, published_at DESC, id, title, slug, summary);

-- 2. 热门文章排行优化
CREATE INDEX idx_articles_hot ON articles(status, is_deleted, published_at, view_count, like_count, comment_count);

-- 3. 分类筛选优化
CREATE INDEX idx_articles_category ON articles(category_id, status, is_deleted, published_at DESC);

-- 4. 用户文章列表优化
CREATE INDEX idx_articles_user ON articles(user_id, status, is_deleted, published_at DESC);

-- 5. 评论列表优化
CREATE INDEX idx_comments_article ON comments(article_id, status, is_deleted, parent_id, created_at);

-- 6. 用户关注关系优化
CREATE INDEX idx_follows_following ON user_follows(following_id, follower_id);

-- 7. 查看索引使用情况
SELECT
    TABLE_NAME,
    INDEX_NAME,
    COLUMN_NAME,
    CARDINALITY,
    SEQ_IN_INDEX
FROM INFORMATION_SCHEMA.STATISTICS
WHERE TABLE_SCHEMA = 'blog_system'
ORDER BY TABLE_NAME, INDEX_NAME, SEQ_IN_INDEX;

-- 8. 分析慢查询
EXPLAIN SELECT * FROM v_published_articles WHERE category_id = 1 ORDER BY view_count DESC LIMIT 20;

❓ 常见问题

Q:冗余字段(如 article_count)怎么保证数据一致性? A: 在增删操作时同步更新冗余字段。可以使用触发器自动维护,或在应用层代码中显式更新。定期运行一致性校验脚本修正偏差。

Q:软删除和物理删除怎么选? A: 核心业务数据(用户、文章、评论)用软删除,便于恢复和审计。日志、临时数据用物理删除,节省存储空间。

Q:文章内容应该存在数据库还是文件系统? A: 短文本(<64KB)存 MySQL 的 TEXT/LONGTEXT 足够。超长内容或需要全文搜索优化时,考虑存储到 Elasticsearch 或对象存储,数据库只存摘要和索引。

Q:如何处理高并发下的文章浏览量更新? A: 不要每次浏览都 UPDATE。用 Redis 缓存计数器,定时批量同步到数据库,或使用消息队列异步处理。

📖 小节

本课综合运用了全部 SQL 知识:

📝 作业

  1. 为博客系统添加"收藏"功能:用户可以收藏文章,设计收藏表并编写查询"我的收藏列表"的 SQL。
  2. 添加"文章浏览历史"功能:记录用户最近浏览过的文章,编写查询"最近浏览的10篇文章"的 SQL。
  3. 实现全文搜索:使用 MATCH AGAINSTLIKE 搜索文章标题和内容,对比两种方式的性能差异。
  4. 为博客系统编写一个数据一致性校验脚本:检查 article_countcomment_count 等冗余字段是否与实际数据一致。

🎉 恭喜你完成了 SQL 教程的全部课程!继续练习,不断提升!

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏