那会儿我去一家亮片厂帮他们搭生产管理系统,老板一上来就给我看了一摞手工记账的Excel,生产进度靠吼,库存数量靠猜,月底对账更是能把人磨疯。亮片这个品类很有意思——规格多、颜色多、批次杂,同一个库存编码下可能有几十种颜色和尺寸,出入库频率又高,稍不注意账实就背离了。我当时给出的核心方案,就是基于MySQL搭一套轻量但足够结实的生产与库存管理系统,把生产订单、车间流转、仓库走账全部统一到数据库里。
这篇文章不聊花架子,就说说我用MySQL做这套系统的完整思路、表结构设计、关键SQL怎么写、部署时踩了哪些坑、日常运维有哪些坑需要绕开。不管你是工厂里的IT人员,还是想给中小型制造企业做数字化改造的开发者,只要手头有类似的生产+库存场景,这套设计思路可以直接抄作业。
1. 系统整体设计与数据库选型思路
1.1 亮片厂业务场景的独特之处
亮片厂的生产链条其实不复杂,但数据量很“琐碎”。订单来了,先拆成生产批次,批次下再分到对应的规格型号;车间领料之后开始冲压、染色、分片、包装;完工之后入成品库,然后按销售订单发货。这里最要命的是:
- 同一个品名的亮片,颜色有几百种(金色、银色、幻彩、哑光),尺寸从0.6mm到12mm不等,库存不可能按一个“总数量”来管。
- 生产过程中有大量“在制品”流转,半成品和成品之间可能还会跨批次调剂。
- 账目需要实时反映在库数量、可用数量、在途数量,否则销售答应客户交期的时候全是拍脑袋。
所以这套系统的核心,就是要把“生产履约”和“库存流转”这两条线拧在同一个数据模型里。MySQL在这里扮演的角色就是那个“总账房”,所有业务动作最终都要反映到几张大表里,通过事务保证多个环节的数据不会出现“生产记录入库了,但库存数没加上”这类低级问题。
1.2 为什么选MySQL作为核心存储
很多厂家的第一反应是用Excel,或者买个现成的ERP。但现实是,Excel在多人同时录入时基本等于薛定谔的数据,ERP采购周期长、价格贵,而且很多功能对这个规模的厂子根本用不上。MySQL的优势很清楚:
- 免费开源,部署成本低,一台普通服务器甚至高性能PC就能跑。
- 事务机制成熟,符合生产领料、完工入库这类“必须全对或全不对”的业务要求。
- 生态成熟,周边工具多,Java、Python、Nacos这些生态都能方便对接,后续做Web端、扫码枪、手机端都容易扩展。
- 中小数据量下性能完全够用,亮片厂就算一年几十万条出入库流水,MySQL的InnoDB引擎也能轻松扛。
当然,选MySQL也有一个前提:你得接受它不像所谓“工业级数据库”那样在万亿级数据下依然横冲直撞,但这不是我们这类场景的重点。把表设计好、SQL写规范,比盲目追求数据库的大牌重要得多。说白了,业务一面再大,也大不过一个设计合理的MySQL。
1.3 系统功能模块与数据流概览
我把整个系统分成五个核心模块:
- 基础资料:物料(亮片规格、颜色、尺寸)、客户、供应商、仓库、库位。
- 生产管理:生产订单、生产批次、工序报工、完工入库。
- 库存管理:采购入库、生产入库、销售出库、生产领料、库内调拨、盘点。
- 销售管理:销售订单、发货单(可与库存出库联动)。
- 报表统计:月度产出入库统计、库存台账、生产进度追踪、呆滞库存分析。
数据流的主线是“订单→批次→领料→完工→入库→出库”。每一步操作都会同时影响业务状态表和库存表,所以我在数据库里特意设计了“流水表”和“余额表”的分离模式——流水表记录每一次动作,余额表保存当前最新数量。查历史对账就看流水,日常查询就看余额,两者靠事务保持同步。
这个设计一开始看起来多了一张表,但实际运维下来非常值得,因为盘点时只需要核对流水和余额的差异,就能快速定位是哪一笔操作出了问题。
2. 数据库表结构设计与核心表详解
2.1 生产订单、生产批次与工序流转表
生产模块的核心表是这几张:
- production_order:生产订单主表,字段包括order_no、product_code、order_qty、plan_start_date、plan_end_date、status。
- production_batch:批次表,一个订单可以拆成多个批次,记录batch_no、order_id、batch_qty、current_process、status。
- production_process_log:工序流转记录,记录batch_id、process_name、operator、input_qty、output_qty、scrap_qty、work_date。
为什么要把批次单独拆出来?因为生产不是一次性做完的,车间可能分批投料,而且质量追溯也需要精确到批次。比如同一张订单交了两批货给同一个客户,后面发现其中某一批有色差,这时候就能通过批次反查是哪天、哪台机器、哪名工人干的活,追责和改进都有依据。
工序流转表我强调的是“报工”概念。工人每完成一道工序,就在系统里录入合格数、不良数和报废数,生产进度自动累加。这样管理者随时打开系统就能看到某个批次目前“走到哪了”,而不是跑到车间问一圈才能拼出大概。实际使用中,很多操作人员嫌麻烦,不愿意急着录入,所以前端界面上最好做成扫码枪一扫、数字一填就能完成报工,后台利用MySQL的高并发写入能力完全扛得住一天几千条的流水记录。
2.2 库存台账与出入库流水表设计
库存是这套系统里最敏感的部分,我用了“双表”设计:
- inventory_balance:库存余额表,唯一标识一个库存项的实时数量。
- inventory_transaction:库存流水表,每一条都记录一次库存增减的“凭证”。
inventory_balance的主要字段:
- id、material_code、spec_value、color_name、warehouse_code、location_code、quantity、locked_qty、update_time。
这里要特别说明locked_qty(锁定数量)的用途。销售订单审核后,我们会先预占库存,把可用数量从quantity里扣减到locked_qty上,等实际发货完成后再做最终核销。这么做可以防止“同一批货被两个销售同时承诺给客户”的问题。类似电商的超卖防护,制造工厂同样需要。
inventory_transaction的字段包括:
- id、trans_no、trans_type(in/out)、biz_type(采购入库、生产入库、销售出库、领料出库等)、material_code、spec_value、color_name、warehouse_code、location_code、qty、before_qty、after_qty、operator、create_time、remark。
before_qty和after_qty特别关键。很多系统只记录一个变化量,出问题的时候很难追查。有了变化前和变化后的余额,每个凭证的来龙去脉一清二楚,写错了也能快速定位是哪个环节。比如某次盘点发现A物料少了20件,查流水发现最后一笔出库记录after_qty是80,而实际库存应该是60,那问题就锁定在这笔或后续操作上。
2.3 物料BOM与亮片规格属性建模
亮片不像机械设备那样有复杂的BOM结构,但它的规格属性多到让人崩溃。一套好的属性建模,能让你免受“建几百个物料编码”的折磨。
我采用的是“基础物料+属性拆分”的模式:
- material_base表:存放通用的物料编码(比如LP001),对应亮片的基础品类。
- material_spec表:存放规格属性组合,比如spec_value=“0.8mm_金色_圆形”等。
实际操作中,我建议把颜色、尺寸、形状作为独立的字典表,而不是全塞在一个逗号分隔的字符串里。因为后续会频繁按颜色、尺寸筛选统计,独立字段更容易用索引加速。
如果实在想省事,也可以用JSON字段,MySQL 5.7以上支持JSON类型,但查询性能和字段校验都不如拆列来得踏实。作为一个有洁癖的工程师,我推荐拆列,并且给组合字段建联合索引。
2.4 关键字段类型选择与索引设计心得
这是很多新手容易踩坑的地方。给几个我实测得到的经验:
- 物料编码字段用VARCHAR(32),不要用TEXT。TEXT没法直接加索引前缀可控性差,而且查询慢。
- 数量字段统一用DECIMAL(12,2),不要用FLOAT。亮片虽然小,但数量可能会很大,而且涉及金额核算时FLOAT的精度问题会让你欲哭无泪。DECIMAL按字符串存储,没有精度损失。
- 时间字段尽量用DATETIME,不要用VARCHAR存字符串时间,原因很简单:可以比较、可以排序、可以用MySQL的时间函数做统计。
- 状态字段用TINYINT,不要用VARCHAR。0代表待审核、1代表进行中、2代表完成,代码里做好枚举映射,既省存储又查得快。
- 唯一键和索引:inventory_balance表上给(material_code, spec_value, color_name, warehouse_code, location_code)建唯一索引,防止同一条库存记录被重复插入。流水表上给trans_no建唯一索引,并给create_time建普通索引,因为按时间段汇总报表非常频繁。
索引不是越多越好,但每个业务查询高频字段都值得一个B+树索引。我实际生产环境里,这几张业务表加起来也只有十来个索引,日常操作没有出现过明显的性能瓶颈。
3. 核心业务逻辑的SQL实现与实操要点
3.1 生产领料与入库的事务控制
生产领料是个典型的多表更新场景:扣减原料库存、增加批次领料记录、更新订单状态。任何一个步骤失败都要回滚,否则就会出现“原料已经领走了,但系统批次还没记录”。
我建议把这类操作封装成存储过程,或者至少在应用层使用事务。这里给出一个简化版的示例:
START TRANSACTION; -- 1. 检查原料库存是否足够 SELECT quantity FROM inventory_balance WHERE material_code = 'M001' AND spec_value = '银色' FOR UPDATE; -- 2. 扣减库存前,判断扣减后不能为负数 UPDATE inventory_balance SET quantity = quantity - 50 WHERE material_code = 'M001' AND spec_value = '银色' AND quantity >= 50; -- 3. 写入库存流水 INSERT INTO inventory_transaction ( trans_no, trans_type, biz_type, material_code, spec_value, color_name, warehouse_code, location_code, qty, before_qty, after_qty, operator, create_time, remark ) VALUES ( 'IT20250101001', 'out', '领料出库', 'M001', '银色', '银色', 'WH01', 'LOC-A1', 50, 200, 150, 'zhangsan', NOW(), '生产领料-批次B001' ); -- 4. 更新批次表 UPDATE production_batch SET total_issued_qty = total_issued_qty + 50 WHERE batch_no = 'B001'; COMMIT;这里有两个细节值得琢磨:
一是SELECT ... FOR UPDATE。这一句不是可有可无的,它在事务内对这条库存记录加锁,防止并发情况下两个工人同时领用同一种物料,各自都觉得“库存够”,结果最后实际库存变成负数。
二是UPDATE inventory_balance SET quantity = quantity - 50 WHERE ... AND quantity >= 50。这是一种乐观的“原子扣减”,比先查出来判断、再更新更安全,因为数据库在一条UPDATE语句里对行加锁并保证条件判断的原子性,能有效防止超扣。
3.2 库存扣减与"超卖"防护
亮片厂的销售发货和电商系统一样,也面临超卖问题:两个销售同时看到库存还有100包,结果都承诺客户“有货”,实际发货时库存只有100包。防护方案主要有三层:
第一层是前面提到的locked_qty,在生成销售订单或出库单时,先做预占。预占的SQL可以这样:
UPDATE inventory_balance SET locked_qty = locked_qty + 30, available_qty = quantity - locked_qty - 30 WHERE material_code = 'LP001' AND spec_value = '0.8mm_金色' AND quantity - locked_qty >= 30;第二层是在实际出库扣减时,再次校验locked_qty是否足够,并且更新locked_qty与quantity:
UPDATE inventory_balance SET quantity = quantity - 30, locked_qty = locked_qty - 30 WHERE material_code = 'LP001' AND spec_value = '0.8mm_金色' AND locked_qty >= 30;第三层是数据库层面的兜底,给库存表增加一个CHECK约束,确保quantity永远不小于0。虽然MySQL 8.0之前对CHECK约束支持并不严谨(8.0.16之后才真正强制生效),但在应用逻辑上多做校验永远没有坏处。
实际运行中,我们还遇到过“同一笔库存被多个事务同时预占”的问题。解决办法就是把预占和状态更新放到同一事务里,并用SELECT ... FOR UPDATE先锁定记录。虽然会有锁等待,但这个场景的并发量远没到闹心程度,锁等待几毫秒问题不大。
3.3 用存储过程生成月报与统计报表
按照老板的习惯,每个月要盘点、要看产量、看库存周转。如果每次都用临时SQL在Navicat里手动查,费时易错。我建议把常用统计写成存储过程,定时调用。
比如统计当月每天的生产完工量:
CREATE PROCEDURE sp_monthly_output_report(IN p_month VARCHAR(7)) BEGIN SELECT DATE(work_date) AS work_day, COUNT(DISTINCT batch_id) AS batch_count, SUM(output_qty) AS total_output_qty, SUM(scrap_qty) AS total_scrap_qty FROM production_process_log WHERE DATE_FORMAT(work_date, '%Y-%m') = p_month GROUP BY DATE(work_date) ORDER BY work_day; END;再比如统计库存占用金额(假设亮片按每公斤单价核算):
CREATE PROCEDURE sp_inventory_amount(IN p_warehouse VARCHAR(20)) BEGIN SELECT b.material_code, b.spec_value, b.quantity, b.quantity * IFNULL(m.unit_price, 0) AS amount FROM inventory_balance b LEFT JOIN material_base m ON b.material_code = m.material_code WHERE (p_warehouse IS NULL OR b.warehouse_code = p_warehouse) ORDER BY amount DESC; END;调用存储过程非常简单:
CALL sp_monthly_output_report('2025-06'); CALL sp_inventory_amount(NULL);有一点要提醒,存储过程的调试比普通SQL麻烦,所以不是所有逻辑都塞进存储过程,像简单查询、页面列表这类动态条件多的场景,放在应用层拼SQL更灵活。存储过程的合适场景是:固定逻辑、固定参数、高频调用。比如月报、日报、定时盘点脚本。
3.4 常用查询优化:JOIN、子查询与排序
生产管理系统里,最频繁的查询就是“查某个订单目前的进度”以及“某个物料在所有仓库的库存分布”。如果SQL写得不对,单表几万数据也能慢成龟速。
关于JOIN,我的原则是:能JOIN就JOIN,少用子查询,尤其是相关子查询(子查询里引用了外层表的字段),在数据量稍大时性能会急剧下降。举一个例子,查所有生产订单及对应客户名称:
-- 推荐写法:显式JOIN SELECT o.order_no, o.product_code, c.customer_name, o.status FROM production_order o LEFT JOIN customer c ON o.customer_id = c.id WHERE o.status IN (1, 2) ORDER BY o.plan_start_date DESC;SQL优化的本质是让MySQL使用尽量少的行来完成查询。所以我们可以利用索引来避免临时表和文件排序。比如上面的查询,如果经常需要按plan_start_date排序,一定要在production_order表的plan_start_date字段上建索引。
排序也有讲究。MySQL有升序(ASC)和降序(DESC),如果业务经常按创建时间倒序,就建一个CREATE INDEX idx_create_time ON production_order(create_time DESC),但这里要先明确是否需要反向索引,以免索引失效。实际场景中,我一般按普通升序索引加ORDER BY DESC,MySQL优化器也能通过倒序扫描做到高效。
关于UPDATE里的子查询,很多人喜欢这样写:
-- 这种写法在MySQL里可能会报错 UPDATE inventory_balance SET quantity = quantity - ( SELECT used_qty FROM production_batch WHERE batch_no = 'B001' ) WHERE material_code = 'M001';MySQL不允许在更新目标表的同时直接查询该表,如果你非要这么写,会触发错误。解决办法是先把子查询的结果放到变量里,或者改成JOIN更新:
UPDATE inventory_balance ib JOIN ( SELECT used_qty FROM production_batch WHERE batch_no = 'B001' ) b SET ib.quantity = ib.quantity - b.used_qty WHERE ib.material_code = 'M001';这种基于JOIN的UPDATE既灵活又安全,强烈推荐掌握。
4. 部署环境与MySQL安装配置实战
4.1 MySQL 8.0安装与初始化(Windows/Linux)
很多小型工厂没有专职DBA,装数据库都是技术员“遇到什么装什么”。我建议统一用MySQL 8.0社区版,不要再用5.7了,一方面8.0的性能和窗口函数等特性更好,另一方面官方长期支持也更稳妥。
Windows下的安装其实很“傻瓜”,下载MySQL Installer,选择Developer Default,一路Next就行。需要注意的点有两个:
- 字符集一定要选utf8mb4,否则后续存中文和特殊符号容易出现乱码,utf8mb4是UTF-8的完整实现,能存emoji和生僻字。
- 设置root密码时,别像测试环境一样用空密码或123456,生产环境还是复杂一点,并且创建专用的业务账号,分配只针对业务库的权限,不要所有设备都用root连。
Linux下的安装更推荐用包管理器。比如在Ubuntu/Debian上:
sudo apt update sudo apt install mysql-server sudo systemctl enable mysql sudo systemctl start mysql sudo mysql_secure_installation安装完后,默认root账号可能只能通过auth_socket登录,也就是本地用sudo mysql进去。如果想让外部客户端能连,需要单独创建账号并授权:
CREATE USER 'lpc_user'@'%' IDENTIFIED BY 'YourStrongPassword'; GRANT ALL PRIVILEGES ON lpc_db.* TO 'lpc_user'@'%'; FLUSH PRIVILEGES;注意,授权范围尽量只给业务用到的数据库,不要顺手把权限变成ALL ON.,最小权限原则能防内部误操作。
4.2 参数调优:宁可慢一分,不可错一毫
生产环境不是性能压测场,我坚持的原则是“一致性优先于极致性能”。对于亮片厂这种规模的数据量,大多数默认参数其实够用了,但有几个参数值得调。
修改方法是编辑my.cnf(Linux)或my.ini(Windows),改完重启MySQL服务。
[mysqld] # 缓冲区大小,建议设为物理内存的50%-70%,例如机器8GB就给4GB-5GB innodb_buffer_pool_size = 4G # 日志缓冲,适当调大可以减少磁盘IO innodb_log_buffer_size = 32M # 允许最大连接数,根据并发量调整 max_connections = 500 # 事务隔离级别:默认REPEATABLE READ,保持默认即可 # 如果你能非常确定需求,可以改为READ COMMITTED降低锁范围 transaction-isolation = REPEATABLE-READ # 打开慢查询日志,帮助优化日常卡顿问题 slow_query_log = 1 slow_query_log_file = /var/log/mysql/slow.log long_query_time = 2这里最需要注意的是innodb_buffer_pool_size。如果机器内存只有2GB,你硬要设4G,MySQL启动直接失败,所以做任何调优前先free -h看内存大小。
关于事务隔离级别,我一直劝大家不要在生产环境随便改成READ COMMITTED。虽然READ COMMITTED能减少间隙锁导致的死锁概率,但它也会改变行为语义,如果业务逻辑依赖REPEATABLE READ下的可重复读特性,改成之后会出现莫名其妙的“同一事务内两次查询结果不一致”。这套系统里,库存扣减和流水写入都是短事务,默认隔离级别完全够用。
慢查询日志是个好帮手。我在部署完成后会在夜间跑一轮自动化的汇总任务,然后第二天检查慢日志,把查询超过2秒的SQL揪出来逐条优化,库存系统最怕的就是一段时间后“越来越卡”。
4.3 定期备份与恢复演练
备份这事,几乎所有小厂都会忽略,直到数据库磁盘挂了一次才追悔莫及。前面说数据很重要,问题是怎么保护数据。
常用的备份方案是mysqldump逻辑备份,简单可靠。我建议做每日凌晨全量备份,保留最近7天。备份命令可以写成脚本:
#!/bin/bash BACKUP_DIR=/backup/mysql DATE=$(date +%Y%m%d_%H%M%S) DB_NAME=lpc_db DB_USER=backup_user DB_PASS='backup_pass' mysqldump -u$DB_USER -p$DB_PASS --single-transaction --quick --skip-lock-tables $DB_NAME | gzip > $BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz # 删除7天前的备份文件 find $BACKUP_DIR -name "*.sql.gz" -type f -mtime +7 -exec rm {} \;--single-transaction很重要,它会在事务中导出数据,避免锁表,不影响生产业务。
备份不是终点,恢复演练才是。我每个月都会在测试机上做一次全量恢复演练,确保备份文件是能用的,同时记录一下恢复需要多长时间。平时不练,真出事时你会发现只剩一堆僵尸备份,那个瞬间血压绝对飙升。
5. 常见问题与排查技巧实录
5.1 数据不一致、更新子查询误用
实际使用中,第一类高频问题是“账实不符”。原因五花八门,常见的是:
- 业务操作在应用层不是事务,先更新了订单状态,但后续库存扣减失败,没有回滚。
- 多个窗口同时操作,没有使用FOR UPDATE锁,产生超扣。
- 领料和入库的流水只记录了数字,没有记录before_qty和after_qty,导致对不上时没法追溯。
排查思路是:先看库存余额表里异常的物料,再查它的流水表,按时间顺序列出每次变更前后的余额,找到第一次出现“对不上”的时间点,再反查当时的业务单据。
第二类高频问题是MySQL的UPDATE子查询报错。就像3.4里写的,MySQL不支持一边更新一张表,一边从这张表查数据来参与更新。解决方案一般是改成JOIN,或者把子查询结果放入变量:
SELECT used_qty INTO @used_qty FROM production_batch WHERE batch_no = 'B001'; UPDATE inventory_balance SET quantity = quantity - @used_qty WHERE material_code = 'M001';这个方法也很实用,尤其在写存储过程或脚本时,步骤清晰、可读性好。
5.2 锁等待与死锁处理
InnoDB引擎在并发写同一行时,会出现锁等待。现象是“进程卡住不动,过了几十秒才报错”,典型的错误信息是:
ERROR 1205: Lock wait timeout exceeded; try restarting transaction排查方法,先看当前是否有事务未提交:
SELECT * FROM information_schema.innodb_trx\G如果发现某个事务已经开了很久,一直没COMMIT或ROLLBACK,那很可能就是它占着锁不释放。可以用下面的SQL杀掉这个卡住的会话(谨慎操作):
-- 查询连接ID SELECT trx_mysql_thread_id FROM information_schema.innodb_trx; -- 杀掉连接 KILL 12345;死锁比锁等待更棘手,表现为错误Deadlock found when trying to get lock; try restarting transaction。死锁发生后,InnoDB会自动回滚其中一个小事务,应用层需要捕获这个异常并重试。最佳策略是业务上约定统一的更新顺序,比如先更新生产批次表,再更新库存表,所有操作都按照这个顺序做,能显著降低死锁概率。
5.3 性能慢查询定位
当系统跑了两三个月后,如果出现页面打开变慢,优先排查是不是SQL忘了索引。一个简单却有效的方法是:
EXPLAIN SELECT * FROM inventory_transaction WHERE material_code = 'LP001' AND create_time > '2025-01-01';EXPLAIN会展示MySQL的执行计划,重点关注type列和rows列。type如果是ALL,说明全表扫描,基本就是索引没建好或者没走索引。rows列越大,扫描行数越多,优化空间越大。
还有一种情况是查询条件里有函数包裹字段,导致索引失效。比如:
-- 索引失效 SELECT * FROM production_order WHERE DATE(plan_start_date) = '2025-06-01';改成范围查询就能用上索引:
SELECT * FROM production_order WHERE plan_start_date >= '2025-06-01' AND plan_start_date < '2025-06-02';日常多看一眼慢查询日志,经常能发现这类低级但致命的写法。
5.4 踩坑记录:默认值、外键、字符集
最后分享几个零碎的坑,每一个都是我真实踩过的。
第一个是关于默认值。MySQL 8.0里,给字段设置默认值不能随便写个函数,比如:
CREATE TABLE demo ( create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );这个没问题。但如果你想把整型字段默认值设为0,直接DEFAULT 0就行。需要注意的是,如果表里已经存在数据,新增带DEFAULT的字段时MySQL会自动填充默认值,这往往是好事,但也会掩盖某些列的本意,建表之前想清楚默认值到底代表什么,别让“0”变成“无意义占位符”。
第二个坑是外键约束。MySQL的外键能保证引用完整性,但也会带来额外的锁开销和插入性能下降。对于亮片厂这种频繁上下线的系统,我通常只保留“应用层约束”,不在数据库里建物理外键,而是在业务代码里做校验。这样做的好处是上线灵活、批量导入方便、避免外键引发的死锁问题。坏处是你得自己保证逻辑正确。如果团队不靠谱,还是建议老老实实建外键,让数据库兜底。
第三个坑是字符集。整个库、每张表、每个字段,都要统一为utf8mb4,同时把排序规则设为utf8mb4_general_ci或utf8mb4_0900_ai_ci。如果部分表是latin1,部分表是utf8mb4,跨表JOIN时可能出现无法为字符串列排序这类奇葩报错,处理起来极其耗时。建议在建库时就这样:
CREATE DATABASE lpc_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;第四个坑是端口和防火墙。MySQL默认端口3306,如果外部客户端连不上,先检查服务器防火墙是否放行了3306端口,再检查MySQL的bind-address是否设置成了127.0.0.1。很多刚装完MySQL的用户,都是卡在这两个地方,白白折腾一晚上。
这套系统我们最终跑了一年多,累计几百万条流水,日常查询都保持在毫秒级。说到底,没有哪套系统是靠数据库本身撑住的,真正的底气来自表结构设计合理、事务边界清楚、SQL写法规范。尤其在工厂这种环境里,每个操作人员都可能犯错,数据库能做的就是让错误变得可追溯、可纠正,而MySQL已经把这些基础能力给得够用了。如果你也在做类似的制造管理系统,不妨从一张好的库存流水表开始,把底层打磨扎实,上层业务怎么长都不会歪。