高效的计费数据库设计

Efficient database design for billing

我的需求很简单,但设计经验不多

每个客户请求旋转一张照片将花费 1 美分。为了历史目的,我需要在 table 中添加每个请求:

CREATE TABLE Rotate_Photo
(
license VARCHAR(35),
reqtype INT NOT NULL, --each will cost 1 cent
updated TIMESTAMP,
FOREIGN KEY (license) 
        REFERENCES customer(license)
        ON DELETE CASCADE
        ON UPDATE CASCADE
)ENGINE=InnoDB;

现在另一个付款 table 将保留每个客户的贷方余额。

CREATE TABLE Payments (
license VARCHAR(35),
Amount FLOAT(5,0) NOT NULL,
receive_date DATE NOT NULL,
updated TIMESTAMP,
FOREIGN KEY (license) 
        REFERENCES customer(license)
        ON DELETE CASCADE
        ON UPDATE CASCADE
) ENGINE=InnoDB;

我如何知道客户何时 运行 失去平衡?鉴于服务器每秒都会收到很多请求。

如果收到请求,那么我可以对 Rotate_Photo 中的所有记录求和,以了解收到了多少请求,然后减去客户信用。但是这样会不会很低效?

有什么更好的方法?

选项 1

如你所说

Payments table will hold credit balance

因此,当有请求的时候,需要查询客户端的余额,确认是正数。

SELECT Amount from Payments where license = :Rotate_Photo.licence;

您可以使用 count() 函数以高效的方式从 Rotate_Photo 中获取记录数。

SELECT count(*) AS rotation_count from Payments where license = :Rotate_Photo.licence;

选项 2

更有效的设计可能是根据许可证记录保持平衡。

然后您将获得 table 许可证:

CREATE TABLE Payments (
license VARCHAR(35),
Amount FLOAT(5,0) NOT NULL,  -- current balance
purchased_amount FLOAT(5,0),  -- amount purchased so far
receive_date DATE NOT NULL,
updated TIMESTAMP,
FOREIGN KEY (license) 
        REFERENCES customer(license)
        ON DELETE CASCADE
        ON UPDATE CASCADE
) ENGINE=InnoDB;

显然,每次购买时您都必须管理金额和 purchased_amount 字段(减少金额并增加 purchased_amount)。

这种方法的缺点是您无法获取特定时间段(例如一个月)的统计信息,但您可以获得帐户的快照。

优点是您将需要一次读取和更新来管理数量。

"Better" 是主观的——但我强烈建议首先构建一个规范化的解决方案(这意味着即时计算余额),只有在遇到无法解决的性能问题时才去规范化更好的查询、归档策略或更大的硬件。

在现代系统中,这可能意味着数千万或数亿条记录。

如果您真的很担心这一点,请构建一个测试工具,其记录数量是您预期在生产环境中拥有的记录数量的两倍,然后查看您的应用程序的执行情况。

对于您担心的性能问题,有多种解决方案。

最常见的是 "denormalization" - 每次用户付款或使用该服务时,您都会更新他们个人资料中的 "currentBalance" 字段。您可以在应用程序逻辑中或使用数据库触发器来执行此操作。应用程序逻辑意味着处理金钱的每一段代码都需要正确执行;只是一个错误可能意味着您放弃了免费的图像旋转。数据库触发器难以测试、难以维护,并且会产生自己的性能问题。