拖放列表,从mysql中的元素中保存顺序的最佳性能

Posted

技术标签:

【中文标题】拖放列表,从mysql中的元素中保存顺序的最佳性能【英文标题】:Drag and drop list, best performance to save the order from the elements in mysql 【发布时间】:2021-08-09 21:16:19 【问题描述】:

我正在处理一个可拖动的列表,我们需要保存元素的顺序或优先级,但我对性能有问题。

我们有三种类型的元素,按钮、图像和视频,用户可以添加元素和重新排序。

我的问题是,我怎样才能获得最佳性能,想象一下未来我们可以拥有 100 000 个页面,每个页面在图像、视频和按钮之间有 15 个元素,并且每次移动都应该使用 ajax 更新数据库?

第一个选项:创建 4 个表格、图像、视频、按钮和优先级,并在它们之间建立关系及其优先级。 First option

第二个选项:只创建一个名为“elements”的表,其中包含一个名为priority 的属性和三个属性“is_video”、“is_image”、“is_button”。 Second option

【问题讨论】:

您有没有试过检查当前是否有问题? “最佳性能”可能有许多活动部分,例如:您的应用程序本身运行速度快吗?您是否可能仅在应用程序中更新数据并向数据库发送一堆UPDATE 查询,因为可能涉及其他部分? 您是否在每个页面上重新排序“少数”项目?或者您重新排序 100K 页?还是 100K*15 项? @NicoHaase 应用程序应该在 laravel livewire 中开发,我需要更新拖放中的每个动作。 @RickJames 只有我需要更新属于一个页面的项目,但是几个用户可以同时修改,所以每个用户都有自己的页面,只有每个用户可以修改自己的项目,它们的间隔从 2 项到 40 项。 【参考方案1】:

最好有一个包含项目顺序的表,因为您在一个表上进行所有重新排序操作。这样的东西可以很好地工作:

SORTED_ITEMS
--------------
PAGE      VARCHAR   -- Identifier of the page
ID_ITEM   VARCHAR   -- Identifier of the item
POSITION  INT       -- the position of the item inside the page

您可以拥有一个ITEMS 表来保存在重新排序期间可能不会更改的所有数据。

ITEMS
-----------------
ID_ITEM   VARCHAR PK - THe primary key of the item
TYPE      VARCHAR    - The type of the item (video, image, button...)
...                  - All other fields related to an item

此解决方案执行良好,因为排序操作不会影响大表,并且使您能够在将来添加新类型的项目而无需更改排序代码。

如果每页的项目数限制在合理的数量内,您还可以为 PAGES 创建一个表,如下所示

 PAGES
 ------
 ID_PAGE    VARCHAR
 ITEMS      VARCHAR   

ITEMS 以您的代码可以轻松处理的格式保存项目列表,例如在 JSON 中,如 ["ITEM1", "ITEM2", "ITEM3", ...] 这使您可以通过对数据库的单个更新进行重新排序,但需要在软件上进行额外的工作以生成 ITEMS 字符串。您还可以将其更改为其他格式,例如 ITEM1,ITEM2,ITEM3ITEM1|ITEM2|ITEM3

这种格式还可以帮助您查找包含特定项目的页面,因为您可以执行简单的SELECT ID_PAGE FROM PAGES WHERE ITEMS LIKE '%<ID_ITEM>%'

【讨论】:

谢谢@Davide,如果我使用第一种方法并且订单表中有 100 000 页 * 15 项 = 1 500 000 条记录,我需要在每次用户移动时修改 15 条记录,这是会有效率吗?以及如何将类型存储在项目表中?使用数字,例如 1、2、3 或直接使用名称,例如图像、视频和按钮,或在其他称为类型和关系的表中? 我相信从 150 万行中更新大约 15 行,虽然可能,但会很笨拙而且有点慢。 @RickJames 在包含 150 万条记录的表中仅更新 15 行并没有什么特别之处。更新必须通过主键完成,所以速度非常快。最终,如果速度不够,可以分区表,但我建议只有在遇到性能低时才这样做 @DavideLorenzoMARINO - 有时获得 15 个 PK 是昂贵的部分。最佳方法是让 PK WHERE 所需的列开始,以便查找和更新 15 行。 (在模式确定之前,很难更具体。)【参考方案2】:

Items 表包含一个 AUTO_INCREMENT 编号,该编号对于页面上的每个可排序项目都是唯一的。

Pages 表包含一个 VARCHAR,其中包含项目 id 的 commalist。

UI 会获取该 commalist 以及有关项目的其他信息以供用户重新排列。每次重新排列后,id 列表都会被发送回服务器(通过 AJAX)。还发回了page_id。那么就是一个简单的

UPDATE Pages
    SET items = ?
    WHERE page_id = ?

请注意,items 既是项目列表,也是这些项目的排序。当“构建”一个页面时,split()explode(),或其他)将 id 分开以便进一步获取。或者直接输入IN(...) 列表。

这将获取一页的项目,并按当前顺序交付:

SELECT *
    FROM Items
    WHERE item_id IN ($item_list)
    ORDER BY FIND_IN_SET(item_id, ?)

(警告:在大多数 API 中,IN 列表不能通过 ? 绑定,您必须在验证列表未被黑客入侵后构造 SQL。使用正则表达式检查 /^[,0-9]+$/ 就足够了。)

我的Items 强制您使用单个表来存放物品,至少是因为您有办法拥有唯一的item_ids。您可能还需要 3 个其他表 - 取决于架构详细信息。

仅重新排列?

如果用户只允许重新排列一页上的项目,...

假设页面有 51、4、7 项。这来自用于构建 UI 的 SELECT

如果用户只是将它们重新排列为,比如说,7,51,4,取每个字符串,explode它,按数字排序,然后implode它。比较它们。由于两者都表示 4、7、51,因此对 UPDATEitems 列有效。 (注意:应该从表中重新获取“旧”items,以确保 API 调用中没有被黑客入侵。)

黑客可能会尝试 99,33。但这将排序为 33,99,与 4,7,51 不匹配。 (对于试图将项目移出或移入页面的合法用户也是如此。)

【讨论】:

谢谢Rick,如果用户在前端修改了项目的字符串怎么办?即,如果我有原始字符串,ID:“4,7,9”,但用户将此字符串修改为“4,16,92”,我如何确保项目属于此页面而不是另一个页面?我是否需要进行另一个查询来检查项目表中具有页面 ID 的列表? @CarlosValdesWeb - 我的补充包括“只允许在单个页面上重新排列”。 谢谢 Rick,您如何考虑将位置存储在 items 字段中,并且只存储位置 1000、2000、3000、4000 以便在每个项目之间有更多的间隙,然后只存储例如 1500,在 1250 等之后,或者可能在字段位置存储一个字符串,如 AAA、BBB、CCC、DDD,如果我需要重新排序,我可以添加 AAB 或 AAC 等?会比您的解决方案更高效吗? @CarlosValdesWeb - 在极端情况下,您将在 10 次插入后用完数字。 (日志 1024 基数 2 = 10)。我的方法避免使用数字进行排序;相反,它使用字符串中的位置。

以上是关于拖放列表,从mysql中的元素中保存顺序的最佳性能的主要内容,如果未能解决你的问题,请参考以下文章

闪亮的小部件,以更改向量中的元素的顺序

从一个列表中拖放 jQuery UI 可拖动元素并将其放入另一个列表的问题

Java在列表中拖放图像

Angular CDK拖放,保持顺序直到下降

拖放时获取元素在列表中的位置(ui.sortable)

谁能建议将 React 拖放列表与来自 Redux 的动态值集成的最佳方法?