优化数据库表

问题描述 投票:0回答:1

所以,我想制作一款有很多玩家的闲置网游。

目前我已经设计了5张桌子,然而我不知道它们的优化正常化程度如何。尤其是那张 user_equipment 充满外键的表。该 inventory 表也感觉会很快填满。

库存 - 这样会不会很快就变得人满为患?

item_id INTEGER NOT NULL,
user_id TEXT NOT NULL,
count INTEGER NOT NULL DEFAULT 1,
FOREIGN KEY(user_id) REFERENCES users(user_id),
FOREIGN KEY(item_id) REFERENCES items(item_id),
PRIMARY KEY(item_id, user_id)

项目 - 储存游戏中所有物品的信息

item_id INTEGER NOT NULL,
name TEXT NOT NULL,
attack INTEGER,
defense INTEGER,
perception INTEGER,
special TEXT DEFAULT "x",
description TEXT NOT NULL,
PRIMARY KEY(item_id)

用户设备 - 本表主要关注。存储装备物品的ID。

user_id TEXT NOT NULL,
head_id INTEGER,
body_id INTEGER,
hand_id INTEGER,
feet_id INTEGER,
main_hand_id INTEGER,
off_hand_id INTEGER,
FOREIGN KEY(user_id) REFERENCES users(user_id),
FOREIGN KEY(head_id) REFERENCES items(item_id),
FOREIGN KEY(body_id) REFERENCES items(item_id),
FOREIGN KEY(hand_id) REFERENCES items(item_id),
FOREIGN KEY(feet_id) REFERENCES items(item_id),
FOREIGN KEY(main_hand_id) REFERENCES items(item_id),
FOREIGN KEY(off_hand_id) REFERENCES items(item_id),
PRIMARY KEY(user_id)

用户统计 - 存储每个用户的统计信息。

user_id TEXT NOT NULL,
attack INTEGER DEFAULT 100,
defense INTEGER DEFAULT 100,
perception INTEGER DEFAULT 5,
FOREIGN KEY(user_id) REFERENCES users(user_id),
PRIMARY KEY(user_id)

用户 - 存储所有用户。

user_id TEXT NOT NULL,
spirit_stones INTEGER DEFAULT 0,
cultivation_val INTEGER DEFAULT 0,
last_checked DATE NOT NULL,
PRIMARY KEY(user_id)

inventoryuser_equipment 表细?有没有更好的设计和方法?如果有任何帮助和建议,我将感激不尽!

node.js sqlite optimization normalization
1个回答
1
投票

对于一个小型RPG来说,这一切看起来都是一个不错的开始。这里有一些建议。


users.user_id 应该是一个整数。名字会改变,但ID不应该。增加一个名字列。一般来说,数据表的主键(不同于连接表)应该是简单的整数。


可以考虑 user_equipment. 有一堆相同的列是一个危险的信号。

当你有固定数量的库存槽时,考虑一下你会如何搜索一个玩家是否装备了某个物品。

select 1
from user_equipment
where user_id = :user_id
  and :item_id in (head_id, body_id, hand_id, feet_id, main_hand_id, off_hand_id)

当你需要添加或删除一个新的插槽时,(越来越大的)表必须被改变,所有的查询也必须被改变。而且你需要六个索引。

取而代之的是,有一个表来存储槽的类型,以及其他任何你需要的信息。然后用一个表来存储每个用户的每个槽中的内容。这样可以简化索引和搜索。

create slots (
  -- You can also use an integer.
  -- I chose char(2) because it is easier to know what it is without a join.
  slot_id char(2) primary key,
  name text not null
);

insert into slots values
('H', 'Head'), ('B', 'Body'), ('MH', 'Main Hand'),
('OH', 'Off Hand'), ('F', 'Feet');

create user_slots (
  user_id integer not null references users(id),
  item_id integer not null references items(id),
  slot_id char(2) not null references slots(slot_id),

  -- Enforce only one item per user slot
  unique(user_id, slot_id)
)

现在查询一个用户是否有某项装备就很容易了。

select 1
from user_slots
where user_id = :user_id
  and item_id = :item_id

或者查询一个用户的所有装备,以及它在哪个槽位。

select slots.name, item_id
from user_slots us
join slots on us.slot_id = slots.slot_id
where user_id = :user_id

或者可以查到谁装备了一个物品,在什么槽位。

select user_id, item_id, slots.name
from user_slots us
join slots on us.slot_id = slots.slot_id
where item_id = :item_id

索引很重要,随着表的增长,要保持性能不变。例如,所有的外键都有索引。如果你用 user_iditem_id SQLite就不用搜索整个表了。但如果你通过 items.name没有索引,它将不得不搜索整个项目表。

索引是一个非常大的话题。你必须考虑到表将如何增长,如何搜索它们。比如说 users 会无限制地增长。items 可能只会变得这么大。user_equipped 将会随着用户的增长而增长,但一个单独的用户只会有这么多的物品;因为user_id和item_id是有索引的,所以你很可能会没事。

例如,检查一个用户是否装备了一个物品,会有很好的表现,因为所有的外键都是有索引的。

select user_id, item_id, slots.name
from user_slots us
join slots on us.slot_id = slots.slot_id
where item_id = :item_id

但如果要查找某个时间内没有被检查过的用户,就必须搜索整个表,除非user.last_checked被索引了。

select user_id
from users
where last_checked < :time

同样,索引也用于排序。这个查询的性能会很差,除非 users.last_checked 是索引的。

select user_id
from users
order by last_checked desc

(并考虑last_checked是否应该是一个


除非有充分的理由将其分开。user_stats 应该并入用户。

users

user_id TEXT NOT NULL,
attack INTEGER DEFAULT 100,
defense INTEGER DEFAULT 100,
perception INTEGER DEFAULT 5,
spirit_stones INTEGER DEFAULT 0,
cultivation_val INTEGER DEFAULT 0,
last_checked DATE NOT NULL,
PRIMARY KEY(user_id)

如果有充分的理由分开,就把所有的用户统计放在同一个地方。

user_stats

user_id TEXT NOT NULL,
attack INTEGER DEFAULT 100,
defense INTEGER DEFAULT 100,
perception INTEGER DEFAULT 5,
spirit_stones INTEGER DEFAULT 0,
cultivation_val INTEGER DEFAULT 0,
FOREIGN KEY(user_id) REFERENCES users(user_id),
PRIMARY KEY(user_id)

users - Stores all users.

user_id TEXT NOT NULL,
last_checked DATE NOT NULL,
PRIMARY KEY(user_id)

分开的一个理由是 user_stats 是你要经常更新统计资料。这可能涉及到行锁定。将统计信息放在一个单独的表中,可以避免锁定重要的统计信息。users 表:

© www.soinside.com 2019 - 2024. All rights reserved.