职位表应使用tinyint状态码+独立时间戳字段(published_at/closed_at)并建立联合索引,简历表需拆分教育/工作等子表避免字段爆炸,匹配采用标签倒排索引而非LIKE,申请记录须用事件表分离流程动作。
jobs 怎么设计才支持快速筛选和状态流转招聘网站的职位不是静态信息,要支撑「发布/下架/过期/暂停」多种状态,还要被搜索、排序、推荐。直接用一个 status 字符串字段会埋坑:'online'、'offline' 这类值难索引,也容易拼错。
建议用 tinyint + 注释方式定义状态码,配合 created_at、published_at、closed_at 三个时间戳字段,而不是只靠一个 updated_at 推断业务阶段:
CREATE TABLE `jobs` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, `title` varchar(128) NOT NULL, `company_id` bigint unsigned NOT NULL, `status` tinyint NOT NULL DEFAULT 1 COMMENT '1=草稿, 2=已发布, 3=已关闭, 4=已过期', `published_at` datetime NULL DEFAULT NULL, `closed_at` datetime NULL DEFAULT NULL, `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_company_status_published` (`company_id`, `status`, `published_at`) ) ENGINE=InnoDB;
status 不用 enum:MySQL enum 在 ALTER 时锁表且不易迁移;tinyint 更易写条件查询,比如 WHERE status IN (2,3)
published_at:避免用 status = 2 AND updated_at > DATE_SUB(NOW(), INTERVAL 7 DAY) 这种模糊逻辑判断“最近发布的职位”idx_company_status_published 覆盖高频查询:HR 查自己公司所有「已发布」职位,或运营查某状态下的最新职位resumes 如何避免字段爆炸又保留扩展性候选人填的信息五花八门:教育经历可能有 3 段,工作经历可能有 5 段,证书可能有 N 个。如果全堆在主表加 edu_1_school、edu_2_school……后期改字段、写查询、做统计全是灾难。
主表只存「快照式」核心字段,结构化数据拆到关联表:
CREATE TABLE `resumes` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, `user_id` bigint unsigned NOT NULL, `name` varchar(64) NOT NULL, `phone` varchar(20) NULL, `email` varchar(128) NULL, `current_salary` decimal(10,2) NULL, `expected_salary_min` decimal(10,2) NULL, `expected_salary_max` decimal(10,2) NULL, `job_status` tinyint NOT NULL DEFAULT 1 COMMENT '1=在职, 2=离职, 3=应届', `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_user_id` (`user_id`) ); CREATE TABLE `resume_educations` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, `resume_id` bigint unsigned NOT NULL, `school` varchar(128) NOT NULL, `degree` varchar(32) NULL, `major` varchar(64) NULL, `start_year` smallint NULL, `end_year` smallint NULL, `is_current` tinyint NOT NULL DEFAULT 0, PRIMARY KEY (`id`), KEY `idx_resume_id` (`resume_id`) );
resume_educations 和 resume_work_experiences 等子表必须带 resume_id 索引,否则 JOIN 时性能断崖下跌extra_1~extra_10:真有需要时再加表或用宽表异步聚合用户搜“Java 后端 上海”,后台不能简单写 WHERE title LIKE '%Java%' AND city = '上海'。这种写法无法利用索引,更没法支持「Java 或 Python」、「3 年经验以上」、「熟悉 Spring Cloud」等组合条件。
实际可行的做法是:把职位和简历都转成「标签向量」,存在独立匹配表中,用整数位运算或预计算提升效率:
CREATE TABLE `job_tags` ( `job_id` bigint unsigned NOT NULL, `tag_id` int unsigned NOT NULL, PRIMARY KEY (`job_id`, `tag_id`), KEY `idx_tag_id` (`tag_id`) ); CREATE TABLE `tags` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `name` varchar(64) NOT NULL, `category` varchar(32) NOT NULL COMMENT 'skill/company/degree/location', PRIMARY KEY (`id`), UNIQUE KEY `uk_name_cat` (`name`, `category`) );
description 提取关键词(如“Spring Boot”、“Docker”),查 tags 表获取 
tag_id,批量写入 job_tags
tag_id 列表,再用 SELECT resume_id FROM resume_tags WHERE tag_id IN (101,205,311) GROUP BY resume_id HAVING COUNT(DISTINCT tag_id) = 3
FULLTEXT):对中文分词支持弱,且无法和结构化字段(如薪资范围、学历要求)做混合过滤有人把 job_applications 设计成大宽表:job_id、resume_id、status、score、interviewer_id、feedback、next_step_at……看着完整,但很快会出问题。
真实流程中,一次申请可能经历「投递→HR 初筛→技术面试→HR 复谈→Offer→拒签」,每个环节产生新数据(面试纪要、评分细则、录音摘要链接),不是简单改一个 status 就完事。
application_events:每行记录一次状态变更、一次反馈提交、一次提醒发送,带 operator_id 和 created_at
job_applications 主表只保留最简态:id、job_id、resume_id、current_status、created_at,用于列表页快速展示复杂度藏在关联关系里,不在单表字段多寡上。字段越多的表,越早遇到 ALTER COLUMN 锁表超时、从库延迟飙升、ORM 映射臃肿的问题。