想问一个 mysql 高频实时交易的表设计

2023-11-23 10:36:12 +08:00
 poembre

游戏开发过程中遇到 游戏装备、游戏材料等物品道具的交易售卖功能实现;

如下是结合了 Google 的 bard 给我的 mysql 表设计


1. 物品表 物品表是整个交易系统的基础,用于存储游戏中所有物品的相关信息,包括物品 ID 、物品名称、物品类型、物品属性、物品等级、物品数量等。
CREATE TABLE `items` (
  `item_id` INT NOT NULL AUTO_INCREMENT,
  `item_name` VARCHAR(255) NOT NULL,
  `item_type` VARCHAR(255) NOT NULL,
  `item_attributes` JSON NOT NULL,
  `item_level` INT NOT NULL, 
  PRIMARY KEY (`item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


2. 用户持有哪些物品及数量表
CREATE TABLE `user_items` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `user_id` INT NOT NULL,  
  `item_id` INT NOT NULL,   // 物品 id 
  `item_count` INT NOT NULL, // 物品数量
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

3.交易信息表 交易信息表用于存储交易的相关信息,包括交易 ID 、物品 ID 、交易价格、交易时间、交易状态等。 让其他用户可以看到 当前正在售卖的道具物品。
CREATE TABLE `trade_info` (
  `trade_id` INT NOT NULL AUTO_INCREMENT,
  `user_id` INT NOT NULL,   // 是谁 在卖道具
  `item_id` INT NOT NULL,   // 卖的什么道具
  `item_count` INT NOT NULL, // 当前在售数量     重点关注 在售数量为 0  的记录要不要删除
  `price` DECIMAL(10,2) NOT NULL, // 价格
  `create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `status` TINYINT(1) NOT NULL DEFAULT 0,
  PRIMARY KEY (`trade_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


4.交易记录表 交易记录表用于存储交易的详细记录,包括交易 ID 、物品 ID 、交易价格、交易时间、交易状态、买家 ID 、卖家 ID 等。
CREATE TABLE `trade_records` (
  `trade_id` INT NOT NULL,
  `item_id` INT NOT NULL,
  `item_count` INT NOT NULL,
  `price` DECIMAL(10,2) NOT NULL,
  `create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `status` TINYINT(1) NOT NULL DEFAULT 0,
  `buyer_id` INT NOT NULL,
  `seller_id` INT NOT NULL,
  PRIMARY KEY (`trade_id`),
  FOREIGN KEY (`buyer_id`) REFERENCES `players` (`player_id`),
  FOREIGN KEY (`seller_id`) REFERENCES `players` (`player_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;




问题疑惑

2205 次点击
所在节点    MySQL
16 条回复
mourner
2023-11-23 11:08:33 +08:00
DNF 的交易系统使用体验简直一坨屎.

我觉得如果实现 DNF 的交易功能,一般来说可以使用 redis + mysql 的方式.
redis 提供基础的查询购买等需求, mysql 做持久化储存使用(方便统计图表数据记录使用).

首先是用户的浏览商品页面,这个页面需要作出快速响应,展示出所有物品,这一层仅需要分页展示商品列表,具体的交易信息最好还是再分层进入查询.道具的具体目录最多几万种道具,所以查询目录速度会非常快.(这一层可以模糊查询道具,然后给出列表)

然后是用户的购买页面,当用户进入道具售卖界面时,列出当前道具的售卖商品信息.

其他的业务同理, 最多在购买和上架时要处理业务逻辑判断保证购买的有效性.

思路就是像这样拆分业务,具体内容具体处理. 感觉你用四个表来操作拍卖行,那你会比 dnf 拍卖行还要卡.
kxct
2023-11-23 11:13:14 +08:00
交易信息表不用删,你觉得数据量太大可以分表或者定时删除多久之前的数据。
poembre
2023-11-23 11:49:14 +08:00
@mourner 大佬说的在理,改交互方式;



总结一下就是 :

dnf 的交易界面是直接搜索道具名称, 列表展示的字段是 :

卖家昵称 卖的物品道具名称 数量 价格
张三 小太刀 1000 $34000

--------------------------更改为 ------------------------------------------

改为 直接搜索道具名称 列表是道具总表 items 表:

道具 id 道具名称 类型 属性 等级
58 小太刀 武器 +30 攻击 1


然后点击 小太刀 这行后; 弹窗列表展示谁在售卖 通过 id 58 去 trade_info 表查询 列表展示的字段是 :

卖家昵称 卖的物品道具名称 数量 价格
张三 小太刀 1000 $34000




大佬的方案极好 解决了查询问题, 省去了一层 表的关联查询。
比如之前可能是 select * from items as A left join trade_info as B ON A.id = B.item_id WHERE A.item_name like "%小太刀%"


更改后是:
select * from items WHERE item_name like "%小太刀%"
select * from trade_info WHERE item_id = 58
更加方便加缓存了


另外我还是想问 :
关于 交易信息表(trade_info), 某物品道具已经被买空,库存字段为 0; 是否需要从交易信息表 删除; 否则 trade_info 表数据非常大
poembre
2023-11-23 11:56:10 +08:00
@kxct 但确实会越来越大, 比如张三 有 1000 把太刀 他可以每次上架 1 把 。 被人购买后 有 1W 条 库存为 0 的数据

分表也可以 分表条件 用什么字段, 这里最佳选择是 道具 ID 取余或者其他 ; 增加了点工作量 效果 应该还不错 ; 配合 @mourner 大佬的改交互方案
poembre
2023-11-23 11:56:54 +08:00
@poembre 但确实会越来越大, 比如张三 有 10000 把太刀 他可以每次上架 1 把 。 被人购买后 有 1W 条 库存为 0 的数据
21231sv
2023-11-23 11:59:10 +08:00
老哥,我想问个题外问题。做这块相关内容的算是游戏行业里的后端开发吗?主要是用什么语言实现呢?
poembre
2023-11-23 12:02:07 +08:00
@21231sv 目前是 golang
mourner
2023-11-23 13:01:56 +08:00
@poembre #3

在拍卖行这里需要使用 redis 进行数据的交互, mysql 仅纪录未来,或者当前设计需求中要满足的数据储存.

使用 Elasticsearch 储存交易纪录就好, 实际的交易内容对于整个拍卖系统来说并没有任何意义.

例如,张三有 10000 把属性不同的太刀 , 他以不同的价格上架到太刀分类中, 那么在拍卖行中你数据中储存的就是 10000 把太刀的交易信息, 但是随着被人购买,物品正常发往购买者的库存中, 那么交易信息的 redis 数据就可以直接删掉这件道具的售卖相关信息. 只需要把详细的交易纪录储存到 Elasticsearch 中方便数据查询就好.

在我的认知里, mysql 应该是不能完成这样的高频读写更新功能.(体量比较的话可以.)
poembre
2023-11-23 14:20:49 +08:00
@mourner 有个问题,因为涉及到玩家的道具物品数量,如果放入 redis 没法用事物 来保证一致性。 比如 mysql 操作成功 Redis 失败 等杠精问题 ... 。 然后 DNF 那个 将物品放入拍卖行 如果一直没人买, 到期会自动退回。 这个好像避免不了更新操作, 即使放入 Elasticsearch 也要被拎出来更新 。
iosyyy
2023-11-23 14:35:33 +08:00
@poembre #4 我觉得可以根据时间来分 每多少天一张表
iosyyy
2023-11-23 14:37:26 +08:00
@poembre #4 根据时间去分的好处就是如果要删除或者数据迁移可以直接从时间入手 对于筛选时间进行查找也更方便 业务逻辑更好(也许
xuanbg
2023-11-23 15:11:15 +08:00
其实这种不存在商品表。因为这本质上不是商城而是交易。交易么,就是财务那套东西,记账。根据货币/物资的借贷记录,计算出货币/物资的数量即可。也就是一次交易,双方各记两笔账:物资的增加数/减少数量,货币的增加/减少数量。只要记录没错,那玩家的钱和道具就不会错。

至于这个表的数量会不断增加的问题,其实很好解决。停服维护的时候,把历史交易记录归档,然后只留下一条汇总记录即可,这就和财务的期初余额是一个道理。
bianhui
2023-11-23 15:28:31 +08:00
两点:1 ,功能可以挂一个 redis 做库存加减扣除,定期同步到 mysql 。方案网上很多不赘述
2.就这点数据不需要删。你又不是做赌博游戏的,仅仅你交易能交易多少记录。你日活达到多少才能把你数据库搞卡。如果真的有那一天,你根据用户分表就可以了。如果真的到这种程度,你也不止这张表要优化,可能其他的表都得优化。可能也不是 mysql 了。
poembre
2023-11-23 18:20:53 +08:00
@iosyyy 时间会涉及到 跨月 跨年 等问题. 刚好这里又是列表形式
poembre
2023-11-23 18:24:03 +08:00
@xuanbg 赞同 或者写双份。 一段时间后替换掉大表 。
poembre
2023-11-23 18:26:43 +08:00
@bianhui 大佬说的对. 什么时候能讨论一下 “真的有一天 真的到了这种程度” 的方案

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/994424

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX