Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/68.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
提高MySQL查询的性能_Mysql_Query Optimization_Database Indexes_Laravel Datatables_Covering Index - Fatal编程技术网

提高MySQL查询的性能

提高MySQL查询的性能,mysql,query-optimization,database-indexes,laravel-datatables,covering-index,Mysql,Query Optimization,Database Indexes,Laravel Datatables,Covering Index,我有两个表,users和points。当前用户有84263行,而点有1636119行。每个用户可以有0个或多个点,我需要提取最后创建的点 show create table users CREATE TABLE `users` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `email` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `password` varchar(255

我有两个表,
users
points
。当前
用户
有84263行,而
有1636119行。每个用户可以有0个或多个点,我需要提取最后创建的点

show create table users
CREATE TABLE `users` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `email` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `password` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `remember_token` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
  `role` varchar(15) COLLATE utf8_unicode_ci DEFAULT 'consument',
  `created_at` timestamp NOT NULL DEFAULT current_timestamp(),
  `updated_at` timestamp NOT NULL DEFAULT current_timestamp(),
  `deleted_at` timestamp NULL DEFAULT NULL,
  `email_verified_at` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `email_verify_token` text COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `users_email_unique` (`email`)
) ENGINE=InnoDB AUTO_INCREMENT=84345 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

show create table points
CREATE TABLE `points` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` int(10) unsigned NOT NULL,
  `tablet_id` int(10) unsigned DEFAULT NULL,
  `parent_company` int(10) unsigned NOT NULL,
  `company_id` int(10) unsigned NOT NULL,
  `points` int(10) unsigned NOT NULL,
  `mutation_type` tinyint(3) unsigned NOT NULL,
  `created_at` timestamp NOT NULL DEFAULT current_timestamp(),
  `updated_at` timestamp NOT NULL DEFAULT current_timestamp(),
  PRIMARY KEY (`id`),
  KEY `points_user_id_foreign` (`user_id`),
  KEY `points_company_id_foreign` (`company_id`),
  KEY `points_parent_company_index` (`parent_company`),
  KEY `points_tablet_id_index` (`tablet_id`),
  KEY `points_mutation_type_company_id_created_at_index` (`mutation_type`,`company_id`,`created_at`),
  KEY `created_at_user_id` (`created_at`,`user_id`),
  CONSTRAINT `points_company_id_foreign` FOREIGN KEY (`company_id`) REFERENCES `companies` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `points_parent_company_foreign` FOREIGN KEY (`parent_company`) REFERENCES `parent_company` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `points_tablet_id_foreign` FOREIGN KEY (`tablet_id`) REFERENCES `tablets` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
  CONSTRAINT `points_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1798627 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
我尝试了一些查询,但时间太长(我们讨论的是几分钟,而不是几秒钟):

为什么我不限制结果,一次只返回100个?因为我对Laravel使用Yajra DataTables,并且在限制结果时,它只返回有限的结果,并且不知道还有更多的结果。因此,我只得到100行,而不是84263行。

基本上,您的“用户”表有一个“角色”列。它没有索引。所以,您的查询正在“users”表上执行完整的表扫描,该表有84263行。优化它的一种方法是在“角色”列上有一个索引。但是我可以看到“consument”是默认值&您正在根据该值进行查询。现在假设95%的用户扮演“消费者”角色。那么,即使在“角色”上添加索引也不会有多大帮助。您必须添加更多的条件来过滤查询&为该条件创建索引

第一个查询更好,因为它可以避免第二个查询不必要的内部查询

如果需要返回84263行,那么这是一个速度问题。不知何故,您必须引入分页。您必须将查询拆分为多个查询。假设在每次调用中返回500个用户数据。您可以按id对其进行排序。在随后的调用中,您可以请求下一个500,其中id大于上一次查询中返回的最后一个id(对于第一次调用,最后一个id值将为0)。然后查询可以使用“id”作为索引

您可以使用“explain”关键字检查查询计划,以便更好地理解。

Edit

我尝试在
role
上添加索引,在
users
表中添加1000个用户和50000个点,您的第一次查询耗时约4秒,这太长了

因此,我尝试了这个查询,耗时约0.5秒,但仍然太长:

select
       `users`.`id`,
       `users`.`email`,
       `users`.`role`,
       `users`.`created_at`,
       `users`.`updated_at`,
       pt.created_at as `last_transaction`
from `users`
left join points pt on pt.id = (select pt2.id from points pt2 WHERE pt2.user_id = users.id ORDER BY pt2.created_at DESC limit 1)
where `users`.`role` = 'consument' and `users`.`deleted_at` is null

因此,我在
点上添加了一个索引。在
处创建,现在查询耗时0.05秒,这更容易接受看起来您需要一个结果集,其中包含
用户
表中的一些列,以及在
表中为每个用户创建的最新

所谓的复合覆盖索引通常有助于加快这类查询的速度。那么,让我们从您需要的
点开始。这个子查询得到它

               SELECT user_id, MAX(created_at) last_transaction
                 FROM points
                GROUP BY user_id
这将为您提供一个虚拟表,其中包含每个
用户id
以及所需的
创建值。下列索引

CREATE INDEX points_maxcreated ON points (user_id, created_at DESCENDING);
CREATE INDEX users_del_role_etc 
    ON users 
      (deleted_at, role, id, email, created_at, updated_at);
将让MySQL以近乎奇迹般的速度满足子查询

然后,让我们考虑其余的查询。

select
       `users`.`id`,
       `users`.`email`,
       `users`.`role`,
       `users`.`created_at`,
       `users`.`updated_at`
from `users`
where `users`.`role` = 'consument' and `users`.`deleted_at` is null
为此,您需要以下索引

CREATE INDEX points_maxcreated ON points (user_id, created_at DESCENDING);
CREATE INDEX users_del_role_etc 
    ON users 
      (deleted_at, role, id, email, created_at, updated_at);
MySQL可以直接从这个索引满足您的查询。可以认为这些索引是按顺序存储的。MySQL随机访问第一个符合条件的行的索引(null
deleted_at
role
='consucent'),然后逐行读取索引,而不是表,以获取所需的数据

把这一切放在一起,你就会明白

选择
`用户`.`id`,
`用户`.`email`,
`用户`.`role`,
`用户`.`created_位于`,
`用户`.`updated_在`,
`子查询`.`上次\u事务`
来自`用户`
左连接(
选择用户id,最大值(在上次交易时创建)
从点
按用户id分组
)users.id上的子查询=subquery.user\u id
其中'users`.'role`='consumert'和'users`.'deleted_at'为空
对于您向我们提出的问题,这应该是相当迅速的。尽管如此,一个需要返回数万行的查询也需要一些时间。没有什么魔法可以让SQL快速处理非常大的结果集。它的设计目的是从大量的表中快速检索小的结果集


恕我直言,您对如何对结果集中的行进行分页的理解并不完全正确。很难相信您的用户会实际检查成千上万行。在查询中,如果不使用
orderby
操作,
LIMIT
是一种非常便宜的操作。如果您需要
订购方式。。。限制
要对结果进行分页,请问另一个问题,因为这种性能也是可以管理的。

分页的问题是,查询是在Laravel内部使用雄辩的ORM生成的,然后被Yajra/DataTables使用。我确实提到,当我尝试按
limit
对结果进行分页时,我只返回有限的结果和该限制的
totalRecords
,而不是84263行。该代码执行整个查询,然后复制请求的行数。因此,我已经放弃了这个解决方案,转而采用其他方法,但速度仍然是个问题。我确实为
用户添加了索引。角色
点。在
创建了\u,您提出的查询执行时间为184.037秒。奇怪的是,如果我删除上面提到的索引,查询需要180.148秒,所以不确定发生了什么,因为我知道索引应该加快查询的执行。我也知道MySQL可以处理数百万行,所以不知道为什么它在这里要处理一个简单的数据连接和160万行。