高效的计费数据库设计
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" 字段。您可以在应用程序逻辑中或使用数据库触发器来执行此操作。应用程序逻辑意味着处理金钱的每一段代码都需要正确执行;只是一个错误可能意味着您放弃了免费的图像旋转。数据库触发器难以测试、难以维护,并且会产生自己的性能问题。
我的需求很简单,但设计经验不多
每个客户请求旋转一张照片将花费 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" 字段。您可以在应用程序逻辑中或使用数据库触发器来执行此操作。应用程序逻辑意味着处理金钱的每一段代码都需要正确执行;只是一个错误可能意味着您放弃了免费的图像旋转。数据库触发器难以测试、难以维护,并且会产生自己的性能问题。