mysql不发送邮件,仅存储邮件数据;应用层调用smtp服务发信,mysql通过email_tasks、email_templates、email_logs三表分别管理任务、模板和日志,严禁用事件或存储过程发信。

MySQL 本身不发送邮件,它只是存储邮件相关数据的数据库;真正发邮件得靠应用层(比如 Python、PHP、Java)调用 SMTP 服务,而 MySQL 负责存收件人、模板、发送记录、状态等。
设计邮件任务表 email_tasks:存待发邮件的核心元数据
别把原始 HTML 内容或大附件塞进这张表,否则查询慢、备份重、还容易拖垮事务。重点存控制字段:
-
id:主键,自增或 UUID(高并发建议 UUID 避免暴露数量) -
to_email:收件人邮箱(VARCHAR(255),加索引,但别建唯一索引——同一人可收多封) -
template_key:模板标识(如"welcome_v2"),不是直接存 HTML,方便统一管理与热更新 -
params_json:JSON 字符串(JSON类型或TEXT),存动态参数:{"name": "张三", "order_id": "ORD-789"} -
status:枚举值,如"pending"/"sent"/"failed"/"retrying",加索引用于轮询 -
created_at和updated_at:时间戳,updated_at在状态变更时更新 -
error_message:失败时存简短错误(如"550 mailbox full"),长度限制 500 字符以内
建邮件模板表 email_templates:分离内容与逻辑
硬编码模板到代码里等于给自己埋雷;存在 DB 里才能灰度、A/B 测试、运营后台修改。关键字段:
-
key:主键,VARCHAR(64),作为业务调用入口(如调用send_email("password_reset", {"token": "abc"})) -
subject:支持变量插值,如"重置您的密码 — {{site_name}} -
body_html:HTML 模板,含{{}}占位符(后端渲染时替换) -
body_text:纯文本备用版本(部分邮箱客户端只读 text/plain) -
is_active:开关字段,停用旧模板不用删数据 -
updated_by:记录谁改的(运维追责用)
注意:body_html 别用 TEXT 就完事——如果模板超大(比如带内联 CSS 的营销邮件),考虑拆出独立表或对象存储,MySQL 只存 URL。
采用三层架构开发,前台集成了产品在线展示,用户注册、在线调查、在线投稿后台有类别管理\图书管理\订单管理\会员管理\配送范围管理\邮件列表\广告管理\友情链接管理等后台添加图书时自动生成缩略图和文字水印主要参考了petshop的设计架构、使用了Asp.net2.0中很多MemberShip、master等新功能后台管理地址/web/admin/ 超级管理员账号密码均为aspx1特别提示:该系统需要
用 email_logs 表追踪每封实际发出的邮件
email_tasks 是“要做什么”,email_logs 是“做过什么”,二者必须通过外键或业务 ID 关联。日志表字段要克制:
-
task_id:关联email_tasks.id,允许为 NULL(比如手工触发没走任务队列) -
message_id:SMTP 返回的唯一 ID(如""),用于查投递状态或投诉溯源 -
from_email和to_email:冗余记录,避免 JOIN 查不到原始任务 -
smtp_status_code:整数,如250(OK)、451(临时失败)、554(拒绝) -
sent_at:精确到秒的时间戳,用于统计时效性 -
size_bytes:整数,记录最终发出的邮件字节数(监控是否意外膨胀)
这张表务必按 sent_at 分区或定期归档,否则半年后几千万行,SELECT COUNT(*) 都卡。
为什么不能用 MySQL 的事件(EVENT)或存储过程发邮件
MySQL 不提供 SMTP 客户端能力,官方也不支持调用外部网络服务。有人试过用 Sys_exec() 或 UDF 调 shell 脚本,后果是:
- 权限失控:MySQL 进程以系统用户运行,脚本可能被注入执行任意命令
- 阻塞主线程:发信耗时几百毫秒到几秒,会卡住所有 SQL 查询
- 无重试/死信机制:失败了就真丢了,连日志都难追
- 无法监控吞吐:你根本不知道每分钟发了多少封,峰值在哪
正确做法是写一个独立的 worker 进程(比如用 Celery + Python smtplib,或 Go 的 gomail),定时拉取 email_tasks 中 status = 'pending' 的记录,发完再原子更新状态和写日志。MySQL 在这里只做“可靠消息队列”的轻量替代,不是邮局。









