简介:《ECSHOP v3.6--3.0 完整版数据字典》是一份聚焦商城数据库核心结构的参考文档,由资深电商技术开发者整理上传,适合进行二次开发、数据迁移或日常维护时使用。文档内容基于ECSHOP v3.0版本,全面拆解商品模块相关数据表,包括商品分类表、商品数据表、商品货品表、商品关联文章表以及商品相册表等,逐项列出字段名称、数据类型、默认值、索引及业务备注,使读者无需登录后台即可清晰把握商城的数据存储逻辑。比如,商品数据表覆盖商品名称、货号、分类、品牌、库存、市场价格、促销价格、积分、上架状态、审核标识等几十个关键字段,并解释了字段间的关系及默认值含义,开发者可依据此字典快速定位某个字段对应的业务场景;对于价格促销逻辑、库存预警机制、虚拟商品与实体商品区分等,文档也给出了简明扼要的说明。资源包仅包含1个docx文档,压缩后大小约324KB,轻量便携,适合放在手边随时查阅。已有233人学习下载,是ECSHOP开发者积累查询表结构经验的实用参考资料。
1. 拿到 ECSHOP 数据字典先别急着翻表:这份 docx 能解决什么问题
接手一套 ECSHOP 商城的老项目,最怕的不是代码写得烂,而是没有一份能跟线上库对得上号的数据库结构。你手头这份「ECSHOP-v3.6--3.0-完整版数据字典-数据库结构.docx」,就是把 ECSHOP 从 3.0 到 3.6 的主要表、字段、类型、默认值、索引整理成一份可以照着建库的文档。数据字典这东西平时没人看,等你要二次开发、换服务器、升级版本、或者把数据从老库导到新库时,它就是唯一的后悔药。
文件名里的 3.6--3.0 是个版本区间,意味着这份文档不是某一个静态版本的快照,而是带上了两个版本之间表结构变化的内容。所以它的价值不只是「查一下 ecs_goods 有哪些字段」,更在于做版本升级时,你能从文档里看出哪些表是后加的、哪些字段被改过类型、哪些索引被调整过。
这份 docx 适合三类人:做 ECSHOP 二次开发、需要确认某个字段能不能直接改的程序员;做数据迁移或环境初始化、需要按文档批量建库的实施人员;以及接手别人商城、先要把表结构和代码理清的技术负责人。它的作用不是让你背下每张表,而是让你在「这个字段动下去会不会炸」的时候,有一个可信的核对依据。接下来我按自己的使用顺序,把这份文档从读到用拆开讲。
2. ECSHOP 数据字典的核心表:读懂一次下单的完整链路
ECSHOP 默认安装会生成四十多张表,但日常开发里高频接触的其实就九张,分布在用户与权限、商品、订单支付、系统配置四个板块里。先建立这个框架,后面查数据字典时就不用每张表都从头看起。
2.1 会员与管理员:ecs_users、ecs_admin_user 的表结构陷阱
ecs_users 是前台会员表,ecs_admin_user 是后台管理员表。两张表在数据字典里挨得很近,字段设计思路却完全不同,这是最容易看走眼的地方。
ecs_users 里要重点看四个字段:user_name、password、ec_salt、email。ECSHOP 3.6 的会员密码不是单纯的 md5(password),而是 md5(md5(password) + ec_salt),ec_salt 是注册时随机生成的一串字符,存在会员表里。如果你按 3.0 的老字典建表,漏掉 ec_salt 这个字段,老会员还能登录,新注册的会员会全部登录失败——因为注册时写入的密码格式和校验逻辑对不上。数据字典里 password 字段的长度是 varchar(32),这个是固定写死的,因为 md5 输出就是 32 位十六进制,改长反而没用。
ecs_admin_user 的坑在 action_list 字段。3.0 的管理员权限列表直接存在这个 text 字段里,是一串逗号分隔的权限标识;3.6 之后权限模型拆到了独立的管理员角色表里,action_list 字段还在,但含义已经变成「兼容旧代码」。在数据字典里看注释,两版几乎一样,实际逻辑差别很大。做权限功能二开时,不要只盯着 action_list 改,先去代码里确认当前版本用的是哪个权限类。
2.2 商品与货品:ecs_goods、ecs_products、ecs_goods_attr 怎么联查
商品相关的三张表构成了 ECSHOP 最复杂的联查模型,也是数据字典里字段最多的部分。
ecs_goods 是商品主表,一个商品在这里一行记录,goods_id 是主键。ecs_products 是货品表,对应我们常说的 SKU,一个商品如果有多个规格(颜色、尺码),就会在 ecs_products 里有多行,product_id 是主键,goods_id 关联回主表。ecs_goods_attr 是商品属性表,存的是这个商品的规格属性值,比如「颜色:红色」「尺码:XL」这种键值对。
三张表的关联逻辑是:ecs_products 表里有 goods_attr 字段,存放的是该货品对应哪些 ecs_goods_attr 里的 attr_id 组合,多个 ID 用竖线分隔。查某个 SKU 的价格和库存,要拿 ecs_products 的 product_id 去关联 ecs_products 和 ecs_goods_attr;查商品本身的信息,走 ecs_goods 就够了。数据字典里 ecs_products 的 goods_attr 字段注释只写了「货品属性」,没写分隔符,这个竖线分隔的格式是看代码才确认的——遇到这种字段,字典只能帮你定位,不能帮你理解格式。
2.3 订单与支付:ecs_order_info、ecs_order_goods、ecs_pay_log 的状态流转
一次下单在主流程上会写三张表。ecs_order_info 是订单主表,订单号、用户、金额、收货信息都在这;ecs_order_goods 是订单商品快照,下单那一刻的商品名、价格、数量、货品属性都复制一份过来,防止后续商品改价影响历史订单;ecs_pay_log 是支付日志表,记录支付请求和回调的结果。
数据字典里 ecs_order_info 最值得研究的不是字段,而是三个状态字段的组合:order_status、shipping_status、pay_status。这三个都是 tinyint 类型,各自取值不同,组合起来表示订单处在哪个阶段。比如 order_status 表示订单本身的状态(已确认、已取消),shipping_status 表示发货进度,pay_status 表示支付进度。做订单列表筛选时,很多人直接查 order_status,结果漏单——因为一个已完成订单的 order_status 可能和待发货订单一样,必须三个字段一起判断。数据字典不会告诉你这个组合逻辑,但你看字典时看到三个 tinyint 状态字段排在一起,就该意识到它们之间有关联。
2.4 配置与地区:ecs_shop_config 与 ecs_region 的使用习惯
ecs_shop_config 是整个 ECSHOP 的配置中心,商店名称、运费设置、支付方式参数、商品显示数量全在这张表里,每行是一个配置项,code 字段是配置键,value 字段是配置值。这表最坑的地方在于,很多配置项的 value 不是纯字符串,而是序列化数组,比如配送方式的配置,value 里存的是带结构的数据。直接在数据库里改这种配置项,改坏一个符号整个功能就崩,而且报错信息往往让人摸不着头脑。遇到配置问题,先用后台界面改,实在不行才动这张表。
ecs_region 是地区表,省市区三级数据都在里面,region_id 是自增主键,parent_id 指向上一级。订单里填的收货地址,最终存的是地区 ID 而不是地区名称字符串,前端展示时再 join 回 ecs_region 取名字。做地区联动、物流运费模板开发时这张表是核心。注意数据字典里 ecs_region 只有几百行初始数据,如果你做过后台地区数据的增删改,线上库很可能已经和字典不一致了,这种基础数据表不建议直接用字典覆盖。
3. 把 docx 数据字典落成 MySQL 建表脚本:半自动转换的完整代码
字典是 Word 文档,数据库是 MySQL 实例,中间隔着一层「照着敲」的体力活。表少的时候手敲无所谓,ECSHOP 全库四十多张表,每张表十几个字段,手敲一遍少说两小时,还容易把 unsigned、默认值这些细节抄错。我一般直接用 python-docx 写个小脚本,把文档里的表结构批量转成 CREATE TABLE 语句,再手工过一遍索引和特殊字段。
3.1 先判断文档是段落式还是表格式布局
动手写脚本前,先花两分钟确认数据字典的排版方式。常见的 ECSHOP 数据字典 docx 有两种布局。
第一种是段落式:每个表名是一个 Heading 级标题段落,下面用自然段列出字段,比如「goods_id int(10) unsigned NOT NULL auto_increment 商品 ID」。第二种是表格式:每个表名是 Heading,下面紧跟一个 Word 表格,表头一般是「字段名 / 类型 / 允许空 / 默认值 / 说明」,每个字段一行。绝大多数「完整版数据字典」用的是表格式,因为整理的人方便、读的人也清楚。两种布局的解析代码差别很大,先打开文档翻两页确认,再选下面的方案。
3.2 用 python-docx 抽取表名与字段:核心解析代码
下面这版代码针对表格式布局,这也是最省事的格式。核心思路分两步:先从 Heading 段落里提取表名,再把表名和文档里的表格按顺序配对。python-docx 的 doc.paragraphs 和 doc.tables 是两个独立列表,需要按文档顺序把它们对齐:
# -*- coding: utf-8 -*- """ECSHOP 数据字典 docx -> MySQL 建表语句""" import re from docx import Document DOCX_PATH = "ECSHOP-v3.6--3.0-完整版数据字典-数据库结构.docx" OUT_PATH = "ecshop_schema.sql" doc = Document(DOCX_PATH) # 1. 收集所有 Heading 段落里的表名 headings = [] for para in doc.paragraphs: style = para.style.name.lower() if not style.startswith("heading"): continue # 表名在数据字典里一定是标题行,正文里出现的 ecs_xxx 不处理 m = re.search(r"(ecs_[a-z0-9_]+)", para.text.strip(), re.I) if m: headings.append(m.group(1).lower()) # 2. 按顺序把表名和 Word 表格配对 tables = doc.tables if len(headings) != len(tables): raise ValueError(f"表名 {len(headings)} 个,表格 {len(tables)} 个,数量不一致,需人工核对") print(f"共识别 {len(headings)} 张表") for name, tbl in zip(headings, tables): print(f"{name}: {len(tbl.rows) - 1} 个字段")这里的参数说明:DOCX_PATH 改成你实际的文件路径;headings 列表保留了文档里表名的出现顺序,默认假设每个 Heading 表名后面紧跟一个表格,这个假设对绝大多数数据字典成立。如果 raise 了数量不匹配,说明文档里有些表只有标题没有表格,或者一个表名下面挂了多个表格,这时候需要根据控制台打印的表名清单,人工把 headings 里多余或缺失的表名删掉补上,再往下走。
3.3 字段类型转 DDL:unsigned、默认值与自增的处理
拿到表格还不够,Word 表格里的字段类型是给人看的,得转成 MySQL 能直接执行的 DDL。ECSHOP 的字段类型集中在 int、tinyint、smallint、varchar、text、decimal、datetime 几类,类型修饰词要原样保留,比如 int(10) unsigned 里的 unsigned 一旦丢掉,主键上限和取值范围都会变。
# 3. 把 Word 表格行转成 MySQL 字段定义 def normalize_type(raw: str) -> str: """整理字段类型,去掉空格,保留 unsigned 和长度参数""" t = raw.strip().lower().replace(" ", "") if t in ("int", "integer"): return "int(11)" if t.startswith("int(") or t.startswith("tinyint("): return t if t.startswith("smallint("): return t if t.startswith("varchar("): return t if t in ("text", "mediumtext", "longtext", "blob"): return t if t.startswith("decimal("): return t if t in ("datetime", "date", "timestamp"): return t return t # 遇到没见过的类型原样返回,人工确认 def build_column_def(cells: list) -> str: """cells: [字段名, 类型, 允许空, 默认值, 备注]""" name = cells[0].strip() type_str = normalize_type(cells[1]) nullable = cells[2].strip().lower() default_raw = cells[3].strip() comment = cells[4].strip().replace("'", "\\'") # 主键自增:字段名是 xxx_id 且类型是 int 时默认视为自增主键 is_pk = bool(re.match(r"^.*_id$", name)) and type_str.startswith("int") line = f" `{name}` {type_str}" if is_pk: line += " NOT NULL AUTO_INCREMENT" elif "not null" in nullable or nullable in ("no", "否", "非空"): line += " NOT NULL" else: line += " DEFAULT NULL" if default_raw and not is_pk: if default_raw.lower() in ("null", "none", "无"): pass elif re.match(r"^[0-9.]+$", default_raw): line += f" DEFAULT {default_raw}" else: line += f" DEFAULT '{default_raw}'" if comment and comment not in ("无", "-", ""): line += f" COMMENT '{comment}'" return line这段代码的判断逻辑要重点说明一下:is_pk 的识别方式是「字段名以 _id 结尾且类型是 int」,这是 ECSHOP 的习惯命名,不是普适规则。遇到 goods_id、cat_id、order_id 这类字段基本都命中,但 ecs_products 表的 product_id 也是这个模式,没问题。如果你手上的字典里主键不是这种命名,需要手动维护一个主键清单。默认值的处理上,数字类型直接写数字,字符串加单引号,NULL 和「无」都跳过不写,避免生成「DEFAULT '无'」这种错误 DDL。
3.4 建表后的三重校验:数量、字段数、索引
脚本生成 SQL 只是第一步,落地前必须做三重校验。第一重是表数量,grep 一下生成文件里的 CREATE TABLE 数量,跟数据字典目录页对一遍。第二重是字段数,每张表的字段行数在 3.2 步已经打印过,随机挑两三张表跟 Word 里手动数一遍。第三重是索引,这个脚本刻意没有生成 KEY 和 UNIQUE KEY,因为数据字典里索引标注的格式差异太大,有的写在类型列里,有的在备注里,有的干脆没有——索引我始终建议人工补,这是最后一道保险:
# 表数量校验 grep -c "CREATE TABLE" ecshop_schema.sql # 字段总数校验 mysql -uroot -p -e "USE ecshop; SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='ecshop';" # 索引核对:拿线上库和 docx 逐一对比 KEY 行 mysql -uroot -p -e "SHOW INDEX FROM ecshop.ecs_order_info;"字段类型转换、默认值、注释都可以交给脚本,唯独索引必须人工过一遍。ECSHOP 的 order_sn、goods_id、user_id 上都有索引,这些索引直接影响查询性能,漏一个在数据量小的时候看不出来,等商品过万、订单过十万就开始慢。脚本生成后,我一般会花二十分钟把四十多张表的索引全部对照字典补上,这段时间省不得。
4. 从 3.0 到 3.6 的表结构演进:升级迁移时重点比对这几处
数据字典文件名里的 3.6--3.0,提醒你这套东西不是静态的。ECSHOP 从 3.0 升到 3.6,数据库结构有增有改,如果直接拿旧库跑新代码,轻则功能异常,重则白屏报错。升级前把结构差异摸清楚,比什么都重要。
4.1 引擎与字符集:MyISAM 换成 InnoDB 时别只在迁移工具里点一下
ECSHOP 3.0 时代的表引擎大多是 MyISAM,字符集是 utf8。3.6 官方安装包改成 InnoDB 为主,字符集也推荐 utf8mb4。这个变化在数据字典里不一定标注,但实际迁移时影响很大。
MyISAM 不支持事务,订单表和支付表如果还是 MyISAM,支付回调写入和订单状态更新不在一个事务里,极端情况下会出现「钱扣了订单没生成」。升级时把所有业务表切成 InnoDB 是常规操作:
-- 批量把表切成 InnoDB,保留原字符集 ALTER TABLE ecs_goods ENGINE = InnoDB; ALTER TABLE ecs_order_info ENGINE = InnoDB; -- 也可以查 information_schema 拼出批量语句 SELECT CONCAT('ALTER TABLE ', TABLE_NAME, ' ENGINE=InnoDB;') FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'ecshop' AND ENGINE = 'MyISAM';第二条 SQL 会输出一串 ALTER 语句,复制到命令行执行即可。执行前注意:大表在线改引擎会锁表,选择业务低峰期操作;改完用 SHOW TABLE STATUS 抽查 ENGINE 字段是否都变成了 InnoDB。
字符集同理,3.6 的字段里有大量 varchar 和 text,如果库是 utf8,买家在备注里填 emoji 表情就会变成问号。这个细节数据字典里看不出来,因为字典只写 varchar(255),不写字符集。
4.2 字段级差异重点比对:用户认证、价格精度、商品图片
3.0 升 3.6 的字段变化里,有三个地方我每次都会重点核对。第一是会员相关表的认证字段,3.6 的 ecs_users 增加了手机号绑定、实名信息相关字段,做登录二开的人必看。第二是价格字段,ecs_goods 的 shop_price、promote_price 在 3.0 里是 decimal(10,2),如果 3.6 字典里改成了 decimal(10,4) 或加了 unsigned,老数据直接搬过去可能溢出。第三是商品图片,3.0 的商品图集中在几个固定字段里,3.6 有的版本增加了多图字段,字段名带 img 后缀的变多了。
拿你手上的 docx 一个个字段比对不现实,最可靠的方法是让数据库自己告诉你差异。
4.3 用 information_schema 导出两版结构差异:一条 SQL 就够了
把老库导入一个临时库,新库导入另一个临时库,然后用 information_schema.COLUMNS 做 LEFT JOIN,找出新增字段、删除字段、类型变更:
-- 字段级差异:老库在左,新库在右 SELECT a.TABLE_NAME, a.COLUMN_NAME, a.COLUMN_TYPE AS old_type, CASE WHEN b.COLUMN_NAME IS NULL THEN '已删除' WHEN a.COLUMN_TYPE <> b.COLUMN_TYPE THEN CONCAT('变更为: ', b.COLUMN_TYPE) ELSE '' END AS change_info FROM (SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'ecshop_old') a LEFT JOIN (SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'ecshop_new') b ON a.TABLE_NAME = b.TABLE_NAME AND a.COLUMN_NAME = b.COLUMN_NAME WHERE b.COLUMN_NAME IS NULL OR a.COLUMN_TYPE <> b.COLUMN_TYPE;结果里 change_info 为空的不用管,那是类型没变的正常行;看到「已删除」要确认是不是 3.6 重构时废弃的字段,比如某些旧版统计字段;看到「变更为」要注意类型收窄的字段,比如 varchar(255) 改成了 varchar(50),这种字段老数据里如果有超长内容,迁移后会被截断。再跑一张表级差异:新库里有、老库里没有的表,多半是 3.6 新增的功能模块表:
SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'ecshop_new' AND TABLE_NAME NOT IN ( SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'ecshop_old' );这两条 SQL 的执行结果,比对着 docx 数据字典看一遍,哪张表是新加的、哪个字段动过就一目了然。注意把 old 和 new 两个库名换成你实际的库名。
4.4 让 docx 跟上实际库:升级维护里的小习惯
数据字典最常见的死法是被遗忘。升级时用上面的 SQL 找出差异、改了库,但 docx 文档没人回去更新,三个月后再看,字典和线上库已经面目全非。我经手的项目里,这个现象几乎必然发生。
我的习惯是:每次升级或结构变更,先把 information_schema 导出的差异结果归档到变更记录里,再花十分钟把 docx 对应的字段注释改掉。如果团队有文档维护流程,字典就跟着流程走;没有流程的话,至少在升级完成后跑一遍结构导出,把导出的 SQL 连同版本号存进代码仓库。等到下次有人问「这个字段哪来的」,至少有个地方能查。
5. 从数据字典到真实数据库:建表落地避坑 5 记
照着字典建表这种事,看着简单,翻车率其实很高。下面五条都是我踩过的,按现象、原因、解决的顺序写,你建库时可以直接对着排查。
5.1 按文档建表后新用户登录失败:ec_salt 字段没建
现象:老用户登录正常,新注册用户注册成功但马上退出,再登录提示密码错误。后台管理员登录没问题。
原因:数据字典里 ecs_users 表如果按 3.0 版本建,少了 ec_salt 字段。3.6 的密码校验逻辑是 md5(md5(password) + ec_salt),注册时生成了 ec_salt 并写入,但表里根本没这个列,数据没存进去,登录校验时取不到 salt,密码自然对不上。
解决:ALTER TABLE ecs_users ADD COLUMN ec_salt VARCHAR(10) NOT NULL DEFAULT '' AFTER password; 然后清掉缓存重试。如果已经有一批注册了但登录不了的用户,把他们的密码字段重置为空字符串,让用户走一次找回密码流程。
5.2 商品图传上去前台不显示:goods_img 的空串与 NULL 之争
现象:后台传商品图成功,返回路径也正确,前台商品详情页图片裂开。后台看商品列表,缩略图能显示,详情页大图不显示。
原因:ecs_goods 的 goods_img、goods_thumb、original_img 三个字段,字典里有的行默认值写 NULL,有的行写空字符串。模板代码里常用 empty($goods['goods_img']) 判断,NULL 和空字符串都是假值,但后续拼接图片 URL 时,NULL 会被某些框架转成字符串 "NULL",拼出来的路径直接 404。
解决:建表后统一把这三个字段设为 DEFAULT '' NOT NULL,不要允许 NULL:
ALTER TABLE ecs_goods MODIFY goods_img VARCHAR(255) NOT NULL DEFAULT '', MODIFY goods_thumb VARCHAR(255) NOT NULL DEFAULT '', MODIFY original_img VARCHAR(255) NOT NULL DEFAULT '';改完后台上传一张测试图,确认三个字段都写入了路径再继续。
5.3 订单号莫名重复:唯一索引在数据字典里不是装饰
现象:线上订单管理页出现两条一模一样的 order_sn,点详情进的是同一单,但列表页多了一行。查询订单时 count() 和实际详情对不上。
原因:ecs_order_info 的 order_sn 字段在数据字典里标了 UNIQUE,但建表脚本生成时没有把唯一索引带上。3.6 的订单号生成逻辑在高并发下存在极小的碰撞概率,有唯一索引时第二次插入直接报错重试,没有索引就静默插入了重复数据。
解决:先查重复,再去重,最后补唯一索引:
-- 1. 查重复 SELECT order_sn, COUNT(*) FROM ecs_order_info GROUP BY order_sn HAVING COUNT(*) > 1; -- 2. 保留最早一条,删掉重复行(先备份) DELETE t1 FROM ecs_order_info t1 INNER JOIN ecs_order_info t2 WHERE t1.order_sn = t2.order_sn AND t1.order_id > t2.order_id; -- 3. 补唯一索引 ALTER TABLE ecs_order_info ADD UNIQUE KEY uk_order_sn (order_sn);如果线上已经有重复数据,第 3 步会失败,必须先把第 2 步做干净。数据量大的订单表建议先在备用库演练一次。
5.4 自增主键建成了普通 int:插入直接在第一条就撞车
现象:后台添加商品报主键冲突,错误信息类似 Duplicate entry '0' for key 'PRIMARY',或者自增 ID 从 0 开始,跟已有数据撞上。
原因:docx 里 goods_id 的字段描述是「int(10) unsigned 自增主键」,但转换脚本把 AUTO_INCREMENT 漏掉了。没有自增属性,应用层代码如果没有显式传 goods_id,MySQL 会填 0,第二行就撞主键。
解决:补救很简单,给主键补上自增属性:
ALTER TABLE ecs_goods MODIFY goods_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT;预防办法是在生成建表脚本时,把「_id 结尾 + int 类型 + KEY 列为 PRI」三个条件同时满足的字段自动补 AUTO_INCREMENT,这在第 3 章的脚本里已经做了。人工建表时,看到 id 字段一定要核对 EXTRA 列是否为 auto_increment。
5.5 中文正常但 emoji 全变问号:字符集层级不一致
现象:订单备注里输入 emoji 保存后变问号,商品名里的特殊符号也丢。数据库连接用 utf8,字段也是 utf8,看起来没问题,但字符就是存不进去。
原因:utf8 字符集在 MySQL 里最多支持 3 字节编码,emoji 是 4 字节,必须用 utf8mb4。老库从 MyISAM + utf8 迁移过来时,只改了表的 ENGINE,没改字符集,字段级别的字符集还是 utf8。
解决:把库、表、字段三级字符集统一切到 utf8mb4,连接参数也要同步:
-- 库级 ALTER DATABASE ecshop CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; -- 表级(批量拼 SQL) SELECT CONCAT('ALTER TABLE ', TABLE_NAME, ' CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;') FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'ecshop' AND TABLE_COLLATION LIKE 'utf8%';连接字符串里的 charset 参数从 utf8 改成 utf8mb4。PDO 连接串示例:mysql:host=localhost;dbname=ecshop;charset=utf8mb4。改完后之前已经变成问号的 emoji 无法恢复,属于永久损失。
6. 给每张表的表结构打指纹:比对着数据字典做日常巡检
数据字典 docx 是静态的交付物,线上库是动态的,两者迟早会分叉。与其每次等出问题了再后悔,不如给每张表算一个结构指纹,定期比对,一眼看出哪张表被动过。
MySQL 的 information_schema.COLUMNS 表里存着每张表的字段名、字段类型、字段顺序,把它们拼接起来算 MD5,就是这张表的结构指纹:
SET SESSION group_concat_max_len = 65535; SELECT TABLE_NAME, MD5(GROUP_CONCAT( COLUMN_NAME, ':', COLUMN_TYPE, ':', IS_NULLABLE ORDER BY ORDINAL_POSITION )) AS struct_fp FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'ecshop' GROUP BY TABLE_NAME;执行结果里每张表一行,后面跟一长串哈希值。把这个结果存成一个 baseline.txt,下次巡检再跑一遍,diff 一下,哪张表的指纹变了,结构就一定被动过。GROUP_CONCAT 默认最大长度是 1024,表字段多时会被截断,导致 MD5 算错,所以第一行先把 session 变量调大。COLUMN_NAME 和 COLUMN_TYPE 都有索引列的时候,GROUP_CONCAT 里加 IS_NULLABLE,这样 NULL 约束的变化也能捕捉到,但索引变化不在这个指纹里,要看索引变化得另外查 STATISTICS 表。
这个技巧对老项目尤其有用。我维护的一个 3.6 商城,上线半年后某次排查性能问题,发现 ecs_goods 的 goods_name 从 varchar(120) 被人改成了 varchar(255),字段长度变化本身无害,但它说明有人在线上库手动改了结构,而数据字典 docx 里根本没有记录。从那以后我每个月跑一次指纹,把变更记录补回 docx 的对应页,再也没出现过「线上结构和文档对不上」的窘境。数据字典的价值在持续维护,不在一次性建库,指纹就是让维护成本降到最低的办法。这个习惯你可以直接用起来,希望帮到你。
本文还有配套的精品资源,点击获取