前段时间,我们线上系统出现了一个事故:用户创建了商品,在商城的商品列表页看不到,也搜索不到。、
这个问题持续了大概半个小时,最后发现竟然是我的锅。
这个事情怎么说呢,完全是我自己把自己坑了。到底怎么回事呢?
1. 从需求说起 1.1 背景由于我们这个迭代是个大版本,上线的日子要临近了。这次上线需要运营配合提供很多商品属性的数据,他们需要把第三方的属性和我方系统的属性,在excel表格中匹配起来。
原本是这样规划的:由运营同学在excel表格中手动对应双方属性的映射关系。
但后来,他们觉得属性太多了,如果他们人工在excel表格中对应属性映射关系,可能时间上有点来不及。
于是,他们在某次会议上,特意给我提了需求,希望我可以通过程序帮他们在excel中,把双方的属性值映射上。
有一个要求就是要:快
。
因为其他同事,还要基于这份excel数据,做一些后续处理。
1.2 原始需求刚开始开会时,运营说的需求是:他们提供一个excel表格,里面有分类和属性字段,然后让我在程序中全匹配,把能够匹配上的属性编号和属性,在excel的另外两列中返回给他们。
然后,他们根据这份excel数据,把匹配不上的(即另外两列为空)数据,在我们系统中手动录入,这样最终都能匹配上。
1.3 加戏了本来我觉得这个需求挺简单的。
但后来,运营加戏了(加新需求)。
其实,刚开始运营没说完。
后来发现,他们要接入两个厂商。
而且运营提供的两个厂商的excel表格中字段的格式不一样,没法用一套程序搞定。
而且,我们发现有部分属性中包含了一个区间范围,跟我们系统的数据肯定是对应不上的,必须要拆分属性后再匹配。
很显然,运营是不愿意做这种手动拆分的工作的,这事他们想我帮他们用程序处理,这就给我增加了不少工作量。
此外,厂商1还有一个特殊需求:运营手动把excel中的部分数据剔除掉,然后基于这份新数据重新匹配一份新excel数据。
2. 最快的方案了解运营的需求之后,我简单的分析了一下。按需求的优先级,排了一个顺序:
- 导出厂商1的属性数据。
- 导出厂商2的属性数据。
- 给厂商1导一份特殊的属性数据。
- 导出区间范围能匹配上的数据。
如果这些需求都写程序处理,可能要写4个程序,而且还需要花时间部署代码,我怕时间上来不及。
于是我想了一个快速处理需求1、2、3的办法即:直接通过sql语句查询出所需数据
。
不过这套方案的前提是:需要把excel中的数据导入到生产环境中
。
为了保险起见,我先把excel中的数据导入dev环境中。等我写好sql,测试好数据之后,再导入生产环境。
使用数据库管理工具:Navicat Premium
的Import Wizard
功能,可以轻松将excel表格中的数据直接导入一张新表中。
它里面可以指定excel的sheet对于哪张表,指定excel中的列对应表中哪些列。
由于这些需求都是新表,无需特别指定,我就按默认的表名和字段名导入数据了。
但有个问题就是:表名和字段名都是中文的,因为excel中的sheet名和sheet中的字段名都是中文的。
其实,我当时已经发现了这个问题。
但当时又想了想,表中字段比较多,要一一改成英文的,光起名字要花些时间。这些字段最终还是要转换成运营可以看得懂的中文的字段名,这样转来转去有点画蛇添足,浪费时间。
而且这张表导入生产环境之后,是一张临时表,用完了就会被删除的,影响不大。
此外,这张表是新加的,如果没有程序使用的话,应该是不会有问题的。
所以,当时没多想,就找人把数据导入生产环境了。
导出数据的方法很简单:
使用Navicat Premium
的Dump SQL File
中的Structure + Data
即可。
这样该数据库工具,就会把相关表的建表的create
语句,和插入数据的insert
语句,导出到一个.sql
后缀的文件中。
有个小问题就是:每条数据会生成一个insert语句,如果太多了放到生成环境执行,执行效率会比较低。
这时可以将insert脚本复制到idea或者其他的工具中打开,然后全文替换一下,去掉多余的insert,拼接成一个insert语句。
然后再用在线的sql压缩工具,比如:https://tool.lu/sql
,压缩一下去掉多余的空格。
这样插入数据的sql放到生产环境执行,效率要快很多。
运营提供的excel表格中的数据,被导入生产环境之后。按计划,通过一条sql语句,把运营所需要的结果直接查询出来,然后把结果复制到excel表格中。(注意:如果查询结果的数据太多,不建议这么玩)。
按上面的做法,我很快完成了需求:1、2、3,并且把运营所需要的数据及时给他们了。
3. 一个插曲原本按计划,导完数据之后,生产环境中临时表是要删除的。
但出现了一个小插曲,运营给我提了一个临时需求:需要重新导一份厂商2的数据给他们。
他们已经按照表格中的内容,把需要添加的属性已经添加到系统中了。需要我们重新导一份数据,确认一下,现在是否所有数据都能匹配上。
此时,我当时在庆幸幸好数据没删。
运营的这个临时需求,在线上执行相同的sql很快就把数据导出来了。
1分钟实现需求,当时那叫一个:爽
。
而且我观察了一下,系统没有出现异样。
给运营把数据导完之后,我就忙其他事情去了,把删除数据这个事情给忘了。
4. 线上出现问题第二天上午,领导把我叫过去说:canal服务挂了
。
我们分析canal了异常日志后发现,这个问题是由于canal订阅者,读取中文的表名时,出现了乱码,没有成功读取到。该程序直接抛了异常,导致canal订阅者不能正常工作了。
这个问题对用户的影响是:用户创建了商品,在商城的商品列表页看不到,也搜索不到,有用户投诉到运营那边了。
我当时的第一反应是:这也能挂?
我当时不知道下游的业务系统,通过canal监听了我这边的整个数据库。
话不多说,先把问题解决了吧。
我们当时为了快速解决问题,先把中文的临时表都删了,然后把canal重启一下。
果然,这个办法是有效的。
canal监听者立马恢复正常了。
当天,没有再出现过问题。
第二天,领导把我叫过去说:canal服务又挂了
。
我当时一脸懵逼。我什么都没干呀。
Canal解析数据报错:column size is not match for table xxxx 8 vs 9
。
后来,我们经过分析之后发现,canal有一份缓存,如果canal出现异常,可能跟数据库真实的情况不一致。
然后,我们把canal的meta.dat
删除了,然后重启服务,果然恢复正常了。
后面也一直没出现过问题。
6. 确定需求4的方案前面说过运营总共提了4个需求,我通过前面的骚操作,完成了3个需求。
但第4个需求,里面还有点特殊要求,通过sql脚本不容易搞定,只能硬着头皮写java程序了。
运营的需求是把他们提供的excel表格中的数据导入系统,然后由系统匹配某个区间范围内的数据,把结果写入excel的另外两列中,最后返回该excel文件。
拿到这个需求我脑子里想了三个方案:
- 写一个可执行的springboot工具项目,直接放到线上环境中执行jar包。
- 使用一个job处理,本身已经有xxl-job了,接入非常方便。
- 写一个api接口。
最终我选择了第3个方案。
为什么?
其实这3个方案代码的工作量差不多,但前面两个方案需要先上传excel到应用服务器
,或者到OSS
等文件服务器
。
而如果运营需要导多次数据,每次都需要上传一次excel,不仅浪费服务器资源,而且比较费时,还麻烦。
如果用api接口的话,可以直接使用postman
远程调用,直接上传文件,通过输入流的形式读取数据,不保存到服务器。然后处理完数据,在将excel内容以输出流的形式返回给我们下载即可。
使用postman调用远程接口时,入参选择form-data
格式,key
那里输入File
,然后在右侧下拉列表中选择File
,就会出现Select Files
按钮。
通过该按钮,就能选择我们需要上传的excel文件。
如果想调用接口后直接下载excel文件:
在postman中可以选择Send and Download
按钮,即可下载文件。
注意,在图片中的请求api接口地址是localhost,我只是举了个例子,实际情况中是接口的域名。
此时,有些小伙伴可能会问题:这个接口不需要登录就能访问?
答:确实不需要登录,我在网关层放开了该接口的访问权限。
那不是有安全问题?
答:为了解决接口安全问题,也避免发版影响正常用户的使用。我的想法是基于master
分支新拉一个分支:hotfix
,而pre
环境(预生产环境,能访问生产环境的数据库)部署hotfix
分支的代码。
还有一个非常关键,而且我们一直在用的策略是:访问pre的所有接口都必须使用指定的代理
。
公司外面的人肯定是不知道这个代理的存在的,换句话说,只有我们公司内部的人才能访问pre环境的接口。
因此,新加的excel处理接口是非常安全的,而且该接口只部署pre环境,对正常用户不会造成影响
这个方案看似挺完美的。
然后三下五除二,我把代码写完,本地测试通过了,准备发到pre环境导数据。
7. jar包冲突该功能部署pre环境其实非常简单,只需要部署hotfix分支的代码即可。
代码部署好之后,我准备开始访问接口。
先在postman的这个地方配置pre的代理。
代码部署好之后,就能通过上一节中介绍的内容上传excel文件,然后下载结果excel文件了。
但我第一次调用接口时,没有返回想要的数据。从应用服务器的日志中看到,该接口报错了。
报的竟然是某个类找不到。。。。
我这次为了快速导入和导出excel文件,选择了阿里的easyexcel
工具类。
本地开发环境,我确认过,那个类是有的。而且我这个功能是可以正常运行的,我都导出数据了。
但pre环境却报了类找不到。
我猜可能是有jar包版本不兼容。
于是,调整了一下pom文件中引入的jar包的版本,之后,重新部署pre环境。
还真是这个原因,这一次接口能正常访问,能够返回数据了。
我心里暗自窃喜。
后来,把运营所需要的excel文件及时发给他们了。
8. pre环境网络异常又过了两天,需求4有点调整。我把代码改了,还是那个hotfix分支,找人重新部署了pre环境。
打算用之前相同的方法导数据的。
但马上被啪啪打脸。
用postman请求该接口很久都不返回,我知道肯定是出了什么幺蛾子。
查了一下pre环境应用服务器的日志
,竟然没有查到请求该数据处理接口的记录。
接着,我查了一下pre环境应用网关层的日志
,竟然也没有记录。
不对呀。
然后又查了一下生产环境应用网关层的日志
,原来是请求到生产环境了。
不是配置了代理吗?
为什么会访问到生产环境?
我带着这两个问题咨询了一下公司的IT部门同事。他们追查了一下原因,发现原来网络带宽被打满,导致pre环境的代理出问题了。
经过一段时间之后,pre环境的代理恢复正常了。
其实,pre环境代理出问题后,我们也尝试了一下登录到远程服务器上,执行相关curl命令,直接调用服务器的本地接口。最后,发现用这种方式不太好下载文件。
9. 部署错分支了pre环境代理恢复之后,我满怀希望去用postman请求数据处理接口导数据。
但我发现导出的数据不对。
导出的excel文件根本打不开。
我打开excel文件看数据内容时,提示excel文件格式不对,或者已经被损坏了。
然后,我赶紧看应用服务器的日志,有请求记录,但是没有返回记录,从这个日志中看不出问题。
当时我灵机一动:既然保存成.xlsx
后缀的excel文件打不开,如果把文件后缀改成.csv
格式呢?
于是,我把导出的excel文件后缀改成了.csv
格式,果然可以打开文件。
文件内容中提示404
。
这时我就明白了,可能是pre环境的接口没发成功,被其他分支的代码冲掉了。
然后,跟部署代码的同学沟通之后,他当时操作失误,部署的master分支的代码,果然把hotfix的代码冲掉了。
后来,他重新部署了hotfix的代码,我顺利的把数据导给运营了。
至此,这4个需求顺利完成了。
总结这次给运营导数据,是一次比较难得的经历,遇到了很多问题,值得总结一下。
当然这其中有一部分是自己给自己挖的坑,也有一部分是被其他人坑了。
不要怕踩坑,其实踩坑,也是成长的机会,我通过这次经历也收获了不少宝贵的经验。
- 生产环境的表名或字段名,一定不能用中文的。不要抱侥幸心里,说不定哪天就出问题了。
- 生产环境创建的临时表,用完之后,一定要记得及时清理。
- 使用
canal
时,最好别全库监听。用到什么表,就监听什么表,避免出现一些意外事故。 - 版本不兼容,会导致类找不到问题。
- 如果使用了
代理
,要考虑代理出现问题的情况。 - 代码发版之后,一定要再三确认分支是否正确。
- 删除
meta.dat
文件,重新canal服务,可以解决canal的很多问题。 - postman真的非常强大,建议大家都好好用一下。
- 把多条insert语句合成一条执行,效率更高。可以使用
https://tool.lu/sql
,这里在线工具,压缩一下sql去掉多余的空格。 - excel导入和导出用阿里的
easyexcel
工具,真的非常方便。
还有挺多收获的,这里就不一一列举了。
希望你看了我的文章,自己也会有点收获,能从我的经历中,学到一点点东西,我就已经心满意足了。
如果这篇文章对你有点帮助的话,请记得给我点赞喔,谢谢你的支持。
我们下期再见。
最后说一句(求关注,别白嫖我)如果这篇文章对您有所帮助,或者有所启发的话,帮忙扫描下发二维码关注一下,您的支持是我坚持写作最大的动力。
求一键三连:点赞、转发、在看。
关注公众号:【苏三说技术】,在公众号中回复:面试、代码神器、开发手册、时间管理有超赞的粉丝福利,另外回复:加群,可以跟很多BAT大厂的前辈交流和学习。