该楼层疑似违规已被系统折叠 隐藏此楼查看此楼

财务数据相比于普通的互联网应用数据,对数据的一致性有更高的要求。因为涉及到用户金钱的流动,出现问题就意味金钱和声誉上的损失。在用 Mysql 处理财务数据时,我认为应该遵循以下原则:

使用 DECIMAL 数据类型存储金额 。因为浮点数精度是有限的,并且无法精确的表示一些数字。应用程序也应该使用 Decimal 函数库来进行金额的加减乘除的运算,比如 Python 的 decimal 模块,C++ 的 boost Multiprecision 库。

使用事务来更新数据库 。涉及到数据库多个记录更新时,事务能够做到要么全部成功,要么全部失败,这保证了数据的一致性。Mysql 使用事务需要 InnoDB 引擎。

更新数据库时使用悲观锁 。更新数据前使用 SELECT … FOR UPDATE; 来查询,这样防止并发的请求读到脏数据,导致数据错乱。虽然加锁会影响性能,但为了数据的一致性也是值得的。

记录资金变化的流水日志 。不能简单的只记录用户的金额,还要记录每笔资金的来龙去脉,包括变化的大小、时间、业务、更新前金额、更新后金额、备注等,另外还有记录业务ID防止重复更新金额。这样在对账的时候才能有理有据。

下面以一个完整的示例来说明如何设计一个完备的记账系统用来记录人民币余额,这里精度只需要2位小数就可以了。

假设已经有一个用户表,每个用户有一个唯一ID。我们需要创建两张表,一张余额表,一张流水表:

CREATE TABLE `balance` ( `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY, `user_id` int NOT NULL, `item` varchar(10) NOT NULL, `balance` decimal(20,2) NOT NULL) ENGINE=InnoDB;CREATE TABLE `history` ( `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY, `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `user_id` int NOT NULL, `item` varchar(10) NOT NULL, `amount` decimal(20,2) NOT NULL, `befor` decimal(20,2) NOT NULL, `after` decimal(20,2) NOT NULL, `business` varchar(30) NOT NULL, `business_id` varchar(100) NOT NULL, `detail` text) ENGINE=InnoDB;

为了加快查询速度,另外为了有效利用 InnoDB 的行级锁,我们需要给两张表加上联合索引。另外,我们需要保证流水记录中 user_id, item, business, business_id 的组合是唯一的,避免重复更新数据。

ALTER TABLE balance ADD INDEX `user_item_idx` (`user_id`, `item`);ALTER TABLE history ADD INDEX `user_item_idx` (`user_id`, `item`);ALTER TABLE history ADD UNIQUE update_unique (user_id, item, business, business_id);

假设这时候用户 ID 为 1 的用户充值了 100 元人民币,我们需要把用户的人民币余额加上 100 元,我们要如何处理呢?

这里我们还需要一个充值表用来保存用户的充值记录,同时,我们再创建一个提现表来表示用户提现记录,如下所示:

CREATE TABLE `deposit` ( `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY, `user_id` int NOT NULL, `item` varchar(10) NOT NULL, `amount` decimal(20,2) NOT NULL) ENGINE=InnoDB;CREATE TABLE `withdraw` ( `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY, `user_id` int NOT NULL, `item` varchar(10) NOT NULL, `amount` decimal(20,2) NOT NULL) ENGINE=InnoDB;

首先,开始一个事务,创建充值记录:

START TRANSACTION;INSERT INTO deposit VALUES (NULL, 1, 'CNY', '100')

插入成功后,我们可以使用 MYSQL 的 API 获取到上次插入后生成的自增 ID 的值,假设这里也为 1. 然后我们需要从数据库查询当前余额。查询的时候注意要使用 FOR UPDATE 来锁住该记录,避免其它并发的请求读到脏数据。即使当前该记录不存在,在事务提交之前,其它读请求仍然被阻塞读不到数据的。

SELECT id, balance FROM balance where user_id = 1 and item = 'CNY' FOR UPDATE;

由于用户是新注册的,查询到的数据为空,所以我们创建新的记录:

INSERT INTO balance VALUES (NULL, 1, 'CNY', '100');

然后我们要在流水表中记录下这次的变更操作:

INSERT INTO history VALUES (NULL, NULL, 1, 'CNY', '100', '0', '100', 'deposit', '1', '');

最后提交事务:

COMMIT;

事务提交后,所有的数据都写入到数据库中了,中间如果有异常发生,则执行

ROLLBACK;

来放弃所有的变更。

假设过了一段时间用户要求提现 50 元,操作流程如下:

START TRANSACTION;SELECT id, balance FROM balance where user_id = 1 and item = 'CNY' FOR UPDATE;

假设读出的 id 也为 1. 由于用户之前充值了 100 元还没有使用,所以余额是 100,大于 50 满足提现条件。如果不满足的话需要执行 ROLLBACK. 然后:

INSERT INTO withdraw VALUES (NULL, 1, 'CNY', '50');UPDATE balance set balance = '50' WHERE id = 1;INSERT INTO history VALUES (NULL, NULL, 1, 'CNY', '-50', '100', '50', 'withdraw', '1', '');COMMIT;

这就完整的实现了整个充值和提现的流程。

当然,这里示例的充值和提现是最简化的流程,实际业务中,充值和提现往往涉及到多种状态的流转。并且在提现中,用户发起提现和实际进行转账不可能是同时进行的,中间可能会取消操作,直接扣除资金不太妥当,更妥当的做法是把待提现的资金冻结起来,冻结操作其实就是创建一个新的 item: CNY_FREEZE, 在 CNY 上扣减,然后在 CNY_FREEZE 上增加来实现。转账后再从 CNY_FREEZE 中扣除。如果取消操作则取消冻结,从 CNY_FREEZE 扣减,增加到 CNY 上面,这样整个流程在流水日志上都有体现。

原地址:http://aoyel.com/archives/view?id=4c654b78834dc292a27af2942db9fa6b

mysql 事务处理null_如何使用Mysql正确的处理财务数据相关推荐

  1. mysql事务处理 java_Java实现的mysql事务处理操作示例

    本文实例讲述了Java实现的mysql事务处理操作.分享给大家供大家参考,具体如下: 1.事务是一组要么同时执行成功,要么同时执行失败的sql语句.是数据库操作的一个执行单元. 2.事物的四大特点: ...

  2. mysql count null_一个不可思议的MySQL慢查分析与解决

    点击上方蓝色字体,选择"标星公众号" 优质文章,第一时间送达 责任编辑:岚总链接:http://www.fordba.com/optimize-an-amazing-mysql-s ...

  3. mysql事务处理用法与实例详解

    MySQL的事务支持不是绑定在MySQL服务器本身,而是与存储引擎相关1.MyISAM:不支持事务,用于只读程序提高性能 2.InnoDB:支持ACID事务.行级锁.并发 3.Berkeley DB: ...

  4. MySQL事务处理与事务隔离(锁机制)

    转载:http://blog.csdn.net/qq_26525215/article/details/52146529 MySQL 事务处理 简单介绍事务处理: MySQL 事务主要用于处理操作量大 ...

  5. 解读mysql的索引和事务的正确姿势

    一.索引是做什么的? 很多时候,当你的应用程序进行SQL查询速度很慢时,应该想想是否可以建索引. 大多数MySQL索引(PRIMARY KEY.UNIQUE.INDEX和FULLTEXT)在B树中存储 ...

  6. 解读mysql的索引和事务的正确姿势 1

    一.索引是做什么的? 很多时候,当你的应用程序进行SQL查询速度很慢时,应该想想是否可以建索引. 大多数MySQL索引(PRIMARY KEY.UNIQUE.INDEX和FULLTEXT)在B树中存储 ...

  7. mysql 数据索引使用_mysql数据库正确建立索引及使用

    普通mysql运行,数据量和访问量不大的话,是足够快的,但是当数据量和访问量剧增的时候,那么就会明显发现MySQL很慢,甚至down掉,那么就要考虑优化我们的mysql了.其中优化mysql的一个重要 ...

  8. mysql事务处理 begin_mysql 事务处理

    mysql 事务处理(表的引擎必须是 innodb / BDB) 主要是两种两法:推荐用第一种 1.用 begin,rollback,commit 来实现 begin 开始一个事务 rollback ...

  9. PHP mysql 事务处理实例

    事务是必须满足4个条件(ACID):原子性(Autmic).一致性(Consistency).隔离性(Isolation).持久性(Durability) 原子性(Autmic):事务在执行性,要做到 ...

最新文章

  1. TNF诱导的关节破坏由IL-1介导
  2. copy_{to, from}_user()的思考
  3. sudo: Cannot execute /usr/local/bin/zsh: No such file or directory 问题
  4. 【笔记】多态之Override
  5. pytorch之expand和repeat
  6. apache 支持.htaccess 解决Internal Server Error The server 错误
  7. 西门子 STEP7 HMI生成步骤
  8. php源码安装教程宝塔,宝塔版-源码安装教程
  9. Lumion 9.0 建筑日景快速渲染实例教程
  10. 用python做系统的感悟_《Python机器学习经典实例》学习感悟
  11. 深度学习之TensorFlow(一)
  12. ipad2利用crappstore安装破解软件成功-还是写一下我安装的过程吧,大家可以参考一下...
  13. 推荐5款edge浏览器插件
  14. dout java_java之 ------ 文件的输入、输出(一)
  15. vmware启动报错Entering emergency mode
  16. 和隆优化 matlab,misc.m · XJTU_STEI/matlabTools - Gitee.com
  17. phpstudy端口被占用
  18. uniapp组件-uni-fav收藏按钮
  19. 类似于 qq发说说动态 基于原生servlet 后台方案
  20. 数据库系统概论第五版_第四章:数据库安全性

热门文章

  1. Android 应用启动速度优化
  2. VMbox复制虚拟机后网卡问题-bring up interface eth0:Device eth0 does not seem to be present
  3. Java读写二进制文件示例
  4. Microsoft Office 2010 Service Pack 2
  5. android 如何开发出一款知名应用:构思篇
  6. 华为机试HJ33:整数与IP地址间的转换
  7. linux bsd命令,科学网—Linux/BSD下join命令使数据以tab为分隔符的方法 - 李雷廷的博文...
  8. 计算机基础win7桌面操作,windows7基本操作方法(零基础的人教学)-win7教程
  9. java面试会问到的问题吗_[转载]java面试中经常会被问到的一些算法的问题
  10. 剑指offer面试题[4]-空格替换