[PHP]基于MySQL简单库存量流程实现笔记

来源:互联网 发布:java已被安全设置阻止 编辑:程序博客网 时间:2024/05/14 15:37
构想记录见:
20130323_简单的库存量流程处理接口设计.txt(http://blog.csdn.net/gevolution90/article/details/8710334)


在对设计进行实际编码后,现在回过头来看当初的设计,认为设计算是失败的。
手动使用数据表(MyISAM)模拟事务给编码带来了复杂性,以及代码写起来非常之麻烦。
所以,教训是,只要能用数据库的事务功能都去用数据库的事务功能。在没有的情况下,也不要用这么麻烦的模拟(下面会写更简单的方法)。


先分析用于模拟事务的数据表结构设计:只有一个num字段用于保存变动的数量。当时设计时这个字段对应的是库存表(store)的sell(发售库存量),所以在扣除发售库存量时逻辑上可以实现模拟事务功能。
但库存表还有一个 deliver 字段表示发货库存(另一种库存量),当在编写扣除发货库存功能时发现模拟事务的数据表(store_asyn_num)没有相关字段可以保存变动数据-_-!!!
因为回滚是另一个程序去做的,而num字段中的数据已经隐性地绑定为回滚到发售库存中。
为了避免去改数据表结构,在扣除发货库存量时放弃了模拟事务。
所以,教训是,如果使用这种方式模拟事务的回滚,那么要回滚哪一个表的变动数据,模拟事务数据表就要有相同数量的字段去保存需要变动数据(写得有点复杂),简单说就是有两个库存量字段模拟事务表就要有对应的两个库存量字段。
但是,不再建议使用数据表来模拟回滚了,下面有一个更简单的方法。


当时设计时,倾向于把库存量设计成一个相对独立的功能,所以只提供一个库存ID(store_id),让库存ID绑定到其它数据表的记录中。
但是,因为库存量是要加入现在已经存在的系统中(即iwebshop),而iwebshop把库存量放在了两个位置:
如果商品不分规格,则放在goods表中,否则放在products表中并且goods表中的库存量是所有规则库存量的和。
结果在编码时,想只通过库存ID绑定非常麻烦,因为要回滚,回滚就需要根据库存类型的不同回滚到goods表或products表中。而因为要尽可能与原本的程序兼容,所以库存量修改后同样要同步回goods表或products表原本的库存量字段,而这样同样需要知道一个库存ID实际上是属于goods的还是products的。
所以,后来还是在库存表中加入了 goods_id 和 product_id 两个字段,如果 goods_id 字段不为空,表示属于 goods 表,如果 product_id 字段不为空,则表示属于 products 表。
加入这两个额外的字段后,编码反而变得简单了。
所以,教训是,如果新加入的功能(或代码)是要与原来的功能整合(或代替原本的部份功能),新功能设计向“独立,分离”的方向设计实现起来反而更麻烦。还不如将就着原本的结构加些字段来得简单。


当时设计时,是打算好完全替换掉原本的库存字段:即最后的效果是把 goods 表中和 products 表中的库存字段删去,只从库存表中取库存量数据。
这会涉及到调整所有对goods表和products表库存量的读和写部份的程序,比如下订单,取消订单,判断库存量等。
但经过了两天的编码后(约24小时),预见到想实现这个效果工作量是很大的,而且还修改完后还需要完善的测试才能放心,现在估计起来要达到这个效果估计都要一个人做2~4星期左右(每天算12小时)。
所以后来改变为把库存数据同步回 goods 或 products 表库存字段中,让程序兼容。
在编写这个功能的时候发现一种值得以后参考的方法:
凡是这种变动,都采用优先兼容原本流程的做法,不幻想一次就可以完全替换掉原本的结构。
使用当新结构数据发生改变时,同步回旧结构对应的数据字段中的方法实现兼容。
接管所有旧结构的写操作,全改为先写到新结构中,再同步回旧结构中的流程。
若读数据时发现旧结构未有对应的新结构,可自动根据旧结构数据自动初始化到新结构中。
这样,只要接管了写操作,就基本兼容了。
简单来说就是:
1. 接管写操作,写入新的结构。
2. 修改新的结构中的数据后,同步回旧结构中
3. 在读操作部份,能自动初始化成新数据。
功能上线后,等就可以了,因为数据会自动初始化(未初始化的旧数据同样可以读,兼容)。并且测试工作量大大减少。但关键是一定要接管所有写操作。


在最后编写扣发货库存量功能时,突然发现有一种更简单的方式在没数据库事务支持时达到一定的事务性,不需要使用数据表模拟事务的回滚:
之所以模拟事务,是怕在没有事务支持时,完成了扣除库存量的操作但未修改订单状态这个时间点上,意外中断。这样被扣除那些库存量就相当于丢失了。下次这一张订单会再扣一次库存量。
所以,只要记录集哪些数据已扣除库存,就不会重复扣除,已扣除的库存量也不会丢失了:
订单会有一个记录每个订单有哪些商品的表,在表中拿一个字段可以表示本条记录是否已扣发货库存(没有就增加),扣发货库存时主要就是更新这个表和库存量所在的表。
更新使用连表更新:update t1 join t2 on ... set t1.t=1, t2.t=0 where ... ,让数据库处理锁问题。
每次处理时只处理未扣除库存量的记录即可,当所有记录都扣除库存量,就更新订单状态。因为每一条都已进行了记录,并且可能出现数据丢失的部份已交给数据库内部的锁解决掉了,就算是回滚,也只需要把已扣库存量的记录用同样的回滚就可以了。
完全不需要原先设计的那么复杂啊!!!


最后,原先设计中的异步支付功能算是实现了,但基于以上的分析,现在认为实际上可以更简单地实现:
异步支付功能与订单强关联,只使用原先的 store_asyn_num 表:
anid int 自增主键
order_id int 订单ID
num smallint 库存变动数量
order_goods_id int 外键,保存每个订单各有什么商品那个表中的主键ID
goods_id int 不为空表示库存量属于goods表。
product_id int 不为空表示库存量属于product表。
timeout int 过期时间


使用上面的方法防止重复扣库存,支付成功后再检查 store_asyn_num 表,把相关记录的timeout标记为一个特殊值,然后再删除 store_asyn_num 表中的数据即可。
完全不需要原先设计的那么复杂啊!!!


所以总的来说,原先的设计是一个失败的设计。
原创粉丝点击