数据库中有一大 table 还是许多小的?
One large table or many small ones in database?
假设我想使用像 postgresql 这样的数据库创建一个典型的 todo-webApp。用户应该能够创建待办事项列表。在此列表上,他应该能够输入实际的待办事项。
我将待办事项列表视为具有不同属性(如所有者、名称等)的对象,当然还有实际的待办事项条目,它们具有自己的属性,如内容、优先级、日期...。
我的想法是为所有用户的所有待办事项列表创建一个 table。在此 table 中,我将存储每个列表的所有属性。但是出现的问题是如何存储待办事项本身?当然在额外的 table 中,但我应该:
1.为所有条目创建一个大 table 并有一个字段存储它们所属的待办事项列表的 ID,如下所示:
todo-list: id, owner, ...
todo-entries: list.id, content, ...
总共需要 2 tables。待办事项 table 可能会变得非常大。虽然我们知道条目会过期,因此 table 只会随着使用量的增加而增长,但不会随着时间的推移而增长。然后我们会写类似 SELECT * FROM todo-entries WHERE todo-list-id=id
的东西,其中 id
是我们试图检索的列表。
或
2。为每个用户创建待办事项 table.
todo-list: id, owner, ...
todo-entries-owner: list.id, content,. ..
条目数table 取决于系统中的用户数。像 SELECT * FROM todo-entries-owner
这样的东西。中型 table 取决于用户总共输入的条目数。
或
3。创建一个 todo-entries-table for each todo-list,然后将生成的 table 名称存储在 [=70] 的字段中=].例如,我们可以在 table 名称中使用 todos-list 唯一 ID,例如:
todo-list: id, owner, entries-list-name, ...
todo-entries-id: content, ... //the id part is the id from the todo-list id field.
在第三种情况下,我们可能会有相当多的 table。用户可能会创建许多 'short' 个待办事项列表。要检索列表,我们只需按照 SELECT * FROM todo-entries-id
行,其中 todo-entries-id
应该是待办事项列表中的一个字段,或者可以通过将 'todo-entries' 与待办事项连接来隐式完成-列出唯一 ID。顺便说一句:我该怎么做,应该在 js
中完成还是可以直接在 PostgreSQL 中完成?与此非常相关:在 SELECT * FROM <tablename>
语句中,是否可以将其他 table 的某些字段的值设为 <tablename>
?像 SELECT * FROM todo-list(id).entries-list-name
左右。
这三种可能性从少数大到许多小table。我个人的感觉是第二种或第三种方案更好。我认为他们可能会扩展得更好。但我不太确定,我想知道 'typical' 方法是什么。
我可以更深入地了解我对每种方法的看法,但要直奔我的问题:
- 我应该选择三种可能性中的哪一种? (或其他任何东西,这与规范化有关吗?)
跟进:
- (PostgreSQL) 语句会是什么样子?
唯一可行的选择是第一个。它更容易管理,而且很可能比其他选项更快。
假设您有 100 万用户,每个用户平均有 3 个待办事项列表,每个列表平均有 5 个条目。
场景 1
在第一个场景中,您有三个 table:
todo_users
: 100 万条记录
todo_lists
: 300 万条记录
todo_entries
: 1500 万条记录
这样的 table 大小对于 PostgreSQL 来说没有问题,使用正确的索引,您将能够在不到一秒的时间内检索任何数据(意味着只是简单的查询;如果您的查询变得更复杂(例如:给我 todo_entries 最长 todo_list 的 todo_users 前 15% 的人,他们在 3 个月内取得了不到 3 todo_list 的最高 todo_entries entered) 显然会慢一些(和其他场景一样),查询很直接:
-- Find user data based on username entered in the web site
-- An index on 'username' is essential here
SELECT * FROM todo_users WHERE username = ?;
-- Find to-do lists from a user whose userid has been retrieved with previous query
SELECT * FROM todo_lists WHERE userid = ?;
-- Find entries for a to-do list based on its todoid
SELECT * FROM todo_entries WHERE listid = ?;
您也可以将三个查询合二为一:
SELECT u.*, l.*, e.* -- or select appropriate columns from the three tables
FROM todo_users u
LEFT JOIN todo_lists l ON l.userid = u.id
LEFT JOIN todo_entries e ON e.listid = l.id
WHERE u.username = ?;
使用 LEFT JOIN
s 意味着您还将获得没有列表的用户或没有条目的列表的数据(但列值将为 NULL
)。
插入、更新和删除记录可以用非常相似的语句完成,而且速度也同样快。
PostgreSQL 将数据存储在 "pages" 上(通常大小为 4kB)并且大多数页面将被填满,这是一件好事,因为读一个写一个页面 非常与其他操作相比慢。
场景 2
在这种情况下,每个用户只需要两个 table(todo_lists
和 todo_entries
),但是您需要一些机制来识别要查询的 table。
- 100 万
todo_lists
table 秒,每个都有几条记录
- 100 万
todo_entries
table 秒,每个有几十条记录
唯一可行的解决方案是从与用户名相关的 "basename" 或您网站上的一些其他永久身份验证数据构建完整的 table 名称。所以像这样:
username = 'Jerry';
todo_list = username + '_lists';
todo_entries = username + '_entries';
然后您使用那些 table 名称进行查询。无论如何,您更有可能需要 todo_users
table 来存储 100 万用户的个人数据、用户名和密码。
在大多数情况下,tables 会非常小并且 PostgreSQL 不会使用任何索引(也不必)。不过,找到合适的 table 会比较麻烦,而且您很可能会在代码中构建查询,然后将它们提供给 PostgreSQL,这意味着它无法优化查询计划。一个更大的问题是为新用户(todo_list 和 todo_entries)创建 table 或删除过时的列表或用户。这通常需要您在前面的场景中避免的幕后管理。最大的性能损失将是大多数页面只有很少的内容,因此您浪费磁盘 space 和 很多 的时间来读取和写入那些部分填充的页面。
场景 3
这个场景比场景 2 更糟糕。不要这样做,这太疯狂了。
- 300万table秒
todo_entries
每个都有几条记录
所以...
坚持选项 1。这是你唯一真正的选择。
假设我想使用像 postgresql 这样的数据库创建一个典型的 todo-webApp。用户应该能够创建待办事项列表。在此列表上,他应该能够输入实际的待办事项。
我将待办事项列表视为具有不同属性(如所有者、名称等)的对象,当然还有实际的待办事项条目,它们具有自己的属性,如内容、优先级、日期...。
我的想法是为所有用户的所有待办事项列表创建一个 table。在此 table 中,我将存储每个列表的所有属性。但是出现的问题是如何存储待办事项本身?当然在额外的 table 中,但我应该:
1.为所有条目创建一个大 table 并有一个字段存储它们所属的待办事项列表的 ID,如下所示:
todo-list: id, owner, ...
todo-entries: list.id, content, ...
总共需要 2 tables。待办事项 table 可能会变得非常大。虽然我们知道条目会过期,因此 table 只会随着使用量的增加而增长,但不会随着时间的推移而增长。然后我们会写类似 SELECT * FROM todo-entries WHERE todo-list-id=id
的东西,其中 id
是我们试图检索的列表。
或
2。为每个用户创建待办事项 table.
todo-list: id, owner, ...
todo-entries-owner: list.id, content,. ..
条目数table 取决于系统中的用户数。像 SELECT * FROM todo-entries-owner
这样的东西。中型 table 取决于用户总共输入的条目数。
或
3。创建一个 todo-entries-table for each todo-list,然后将生成的 table 名称存储在 [=70] 的字段中=].例如,我们可以在 table 名称中使用 todos-list 唯一 ID,例如:
todo-list: id, owner, entries-list-name, ...
todo-entries-id: content, ... //the id part is the id from the todo-list id field.
在第三种情况下,我们可能会有相当多的 table。用户可能会创建许多 'short' 个待办事项列表。要检索列表,我们只需按照 SELECT * FROM todo-entries-id
行,其中 todo-entries-id
应该是待办事项列表中的一个字段,或者可以通过将 'todo-entries' 与待办事项连接来隐式完成-列出唯一 ID。顺便说一句:我该怎么做,应该在 js
中完成还是可以直接在 PostgreSQL 中完成?与此非常相关:在 SELECT * FROM <tablename>
语句中,是否可以将其他 table 的某些字段的值设为 <tablename>
?像 SELECT * FROM todo-list(id).entries-list-name
左右。
这三种可能性从少数大到许多小table。我个人的感觉是第二种或第三种方案更好。我认为他们可能会扩展得更好。但我不太确定,我想知道 'typical' 方法是什么。
我可以更深入地了解我对每种方法的看法,但要直奔我的问题:
- 我应该选择三种可能性中的哪一种? (或其他任何东西,这与规范化有关吗?)
跟进:
- (PostgreSQL) 语句会是什么样子?
唯一可行的选择是第一个。它更容易管理,而且很可能比其他选项更快。
假设您有 100 万用户,每个用户平均有 3 个待办事项列表,每个列表平均有 5 个条目。
场景 1
在第一个场景中,您有三个 table:
todo_users
: 100 万条记录todo_lists
: 300 万条记录todo_entries
: 1500 万条记录
这样的 table 大小对于 PostgreSQL 来说没有问题,使用正确的索引,您将能够在不到一秒的时间内检索任何数据(意味着只是简单的查询;如果您的查询变得更复杂(例如:给我 todo_entries 最长 todo_list 的 todo_users 前 15% 的人,他们在 3 个月内取得了不到 3 todo_list 的最高 todo_entries entered) 显然会慢一些(和其他场景一样),查询很直接:
-- Find user data based on username entered in the web site
-- An index on 'username' is essential here
SELECT * FROM todo_users WHERE username = ?;
-- Find to-do lists from a user whose userid has been retrieved with previous query
SELECT * FROM todo_lists WHERE userid = ?;
-- Find entries for a to-do list based on its todoid
SELECT * FROM todo_entries WHERE listid = ?;
您也可以将三个查询合二为一:
SELECT u.*, l.*, e.* -- or select appropriate columns from the three tables
FROM todo_users u
LEFT JOIN todo_lists l ON l.userid = u.id
LEFT JOIN todo_entries e ON e.listid = l.id
WHERE u.username = ?;
使用 LEFT JOIN
s 意味着您还将获得没有列表的用户或没有条目的列表的数据(但列值将为 NULL
)。
插入、更新和删除记录可以用非常相似的语句完成,而且速度也同样快。
PostgreSQL 将数据存储在 "pages" 上(通常大小为 4kB)并且大多数页面将被填满,这是一件好事,因为读一个写一个页面 非常与其他操作相比慢。
场景 2
在这种情况下,每个用户只需要两个 table(todo_lists
和 todo_entries
),但是您需要一些机制来识别要查询的 table。
- 100 万
todo_lists
table 秒,每个都有几条记录 - 100 万
todo_entries
table 秒,每个有几十条记录
唯一可行的解决方案是从与用户名相关的 "basename" 或您网站上的一些其他永久身份验证数据构建完整的 table 名称。所以像这样:
username = 'Jerry';
todo_list = username + '_lists';
todo_entries = username + '_entries';
然后您使用那些 table 名称进行查询。无论如何,您更有可能需要 todo_users
table 来存储 100 万用户的个人数据、用户名和密码。
在大多数情况下,tables 会非常小并且 PostgreSQL 不会使用任何索引(也不必)。不过,找到合适的 table 会比较麻烦,而且您很可能会在代码中构建查询,然后将它们提供给 PostgreSQL,这意味着它无法优化查询计划。一个更大的问题是为新用户(todo_list 和 todo_entries)创建 table 或删除过时的列表或用户。这通常需要您在前面的场景中避免的幕后管理。最大的性能损失将是大多数页面只有很少的内容,因此您浪费磁盘 space 和 很多 的时间来读取和写入那些部分填充的页面。
场景 3
这个场景比场景 2 更糟糕。不要这样做,这太疯狂了。
- 300万table秒
todo_entries
每个都有几条记录
所以...
坚持选项 1。这是你唯一真正的选择。