[英]MySQL query design for booking database with limited amount of goods and heavy traffic
我们正在运行一个预订鲑鱼捕捞许可证的网站。 该站点一年 364 天处理流量都没有问题。 第 365 天是许可证销售开始的时间,这就是问题发生的地方。 由于流量增加,服务器每年都在挣扎,我们必须进一步优化我们的预订查询。
许可证分为许多不同的类型( tbl_license_types
),每种许可证类型都连接到一个或多个钓鱼区( tbl_zones
)。
每个许可证类型都可以有一个季节性配额,这是一个设置为 tbl_license_types 中的tbl_license_types
字段的单个值。
每个区域可以有每日配额、每周配额和季节性配额。 每日配额所有天都一样,季节性配额当然是单个值。 因此,每日和季节性是 tbl_zones 中的tbl_zones
字段。 然而,每周配额因周而异,因此在单独的tbl_weekly_quotas
中指定。
预订可以是一个或多个连续日期,但仅在tbl_shopping_cart
(和tbl_bookings
)中表示为From_date
和To_date
。 对于用户进行的每次预订尝试,必须根据tbl_shopping_cart
和tbl_bookings
中已允许的预订来检查配额。
为了能够按日期计数/分组,我们使用一个名为view_season_calendar
的视图,其中包含当前季节的所有日期的单列。
一开始我们使用了一个事务,我们首先进行查询来检查配额,如果配额允许,我们将使用第二个查询将预订插入tbl_bookings
。
然而,在相对中等的流量下会产生很多死锁,因此我们将其重新设计为单个查询(伪代码):
INSERT INTO tbl_bookings (_booking_)
WHERE _lowest_found_quota_ >= _requested_number_of_licenses_
其中_lowest_found_quota_
是约 330 行长的 SELECT ,其中包含多个子查询并且相关表被多次连接以检查所有配额。
示例:用户想要从 2020 年 5 月 19 日到 2020 年 5 月 25 日为区域 5 和 6 预订许可证类型 A。 系统需要
如果所有都在配额之内,请插入预订。
正如我所说,这在早些时候运行良好,但由于更高的流量负载,我们现在需要进一步优化此查询。 我对如何做到这一点有一些想法;
READ UNCOMMITTED
,直到请求的区域/许可证类型的配额几乎已满,然后回退到默认的REPEATABLE READ
。 只要配额还剩很多,计数就不需要 100% 正确。 这将大大减少锁等待和死锁,对吧?READ UNCOMMITTED
。 如果视图报告相关配额接近满,请取消插入并使用我们今天使用的设计开始一个新的。 (希望流量水平在配额满之前下降)对于如何尽可能高效地完成查询,我将不胜感激。
看看你是否可以在赛季开始前一天开始升级 AWS,然后在高峰期之后降级。 为可能带来很大的性能提升付出的代价很小。
而不是用于计数的长而复杂的查询,而是在您 go 时减少一些计数器。 (这可能有帮助,也可能没有帮助,所以请尝试一下。)
您的 web 服务器对其将处理的连接数有一些限制; 限制这一点,而不是让 2K 用户进入 MySQL 并相互绊倒。 想想当过道如此拥挤以至于没有人完成时,杂货店是什么样的!
一定要使用“事务”,但不要让它们过于宽泛。 如果它们包含太多东西,流量将退化为单个文件(和/或事务将因死锁而中止)。
在事务之外尽可能多地做——例如收集和检查用户名/地址等。如果您在颁发许可证后这样做,请准备好在出现问题时撤消许可证。 (这应该在代码中完成,而不是通过ROLLBACK
。
(更多的)
VIEWs
是语法糖; 它们不提供任何性能或隔离。 OTOH,如果您制作“物化”视图,可能会有一些有用的东西。
长的“历史列表”是一个潜在的性能问题(尤其是 CPU)。 当大量连接同时处于事务中间时,可能会发生这种情况——每个连接都需要挂在数据集的“快照”上。
尽可能尽快终止交易——即使你转身开始新的交易。 数据仓库中的一个示例是在开始主事务之前进行“规范化”。 (可能此示例不适用于您的应用。)
考虑有一个计算配额的后台任务。 希望通过不在事务中进行计算,常规任务可以运行得更快。
预订行业中使用的一种技术:(这听起来有点像您的第 1 项。)以最少的锁定向前推进。 在最后一刻,以最短的交易时间进行预订并确认房间(飞机座位等)仍然可用。
如果整个任务可以分为(1)读取一些东西,然后(2)写入(并重新读取以验证该东西仍然可用),那么......如果读取步骤比写入步骤重,则添加更多奴隶('副本')并将它们用于读取步骤。 为写入步骤保留 Master。 请注意,复制品很容易(且便宜)添加并在短时间内折腾。
每秒速率 = RPS
为您的 AWS 参数组考虑的建议
innodb_lru_scan_depth=100 # from 1024 to conserve 90% of CPU cycles used for function every second
innodb_flush_neighbors=2 # from 1 to reduce time required to lower innodb_buffer_pool_pages_dirty count when busy
read_rnd_buffer_size=128K # from 512K to reduce handler_read_rnd_next RPS of 3,227
innodb_io_capacity=1900 # from 200 to use more of SSD IOPS
log_slow_verbosity=query_plan,explain # to make slow query log more useful
have_symlink=NO # from YES for some protection from ransomware
您会发现这些更改将导致交易更快地完成处理。 如需其他帮助,请查看配置文件、网络配置文件以获取联系信息和免费下载的实用程序脚本以帮助进行性能调整。 在我们的常见问题页面上,您会找到“问。如何找到不使用索引的 JOINS 或 QUERIES?” 帮助减少 1,173 的 select_scan RPhr。 Com_rollback 平均每 2,700 秒 1 次,通常可以通过维护查询中的一致读取顺序进行纠正。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.