Mysql 如何使以下查询在具有多种条件的查找中更有效

Mysql 如何使以下查询在具有多种条件的查找中更有效,mysql,sql,performance,optimization,database-design,Mysql,Sql,Performance,Optimization,Database Design,首先,我需要一个分页功能,通过使用耦合where条件限制以下查询查找中的结果 SELECT SQL_CALC_FOUND_ROWS a.uid, b.NAME FROM `profiles` AS a FORCE INDEX(profiles_country_city_gender_index) JOIN `users` AS b ON b.id = a.uid AND a.country = 'INDONESIA' AND a.gender = 0 JOIN ( SEL

首先,我需要一个分页功能,通过使用耦合where条件限制以下查询查找中的结果

SELECT SQL_CALC_FOUND_ROWS
    a.uid, b.NAME
FROM
    `profiles` AS a FORCE INDEX(profiles_country_city_gender_index)
JOIN `users` AS b
ON b.id = a.uid
AND a.country = 'INDONESIA'
AND a.gender = 0
JOIN (
    SELECT
        a.uid
    FROM
       profile_details AS a
    JOIN profile_details AS kids ON kids.uid = a.uid
    AND kids.kids_pref = 1
    JOIN profile_details AS current ON current.uid = a.uid
    AND current.current_relationship = 1
    JOIN profile_details AS smoking ON smoking.uid = a.uid
    AND smoking.smoking_pref = 1
    ) AS e ON e.uid = a.uid
AND ( TIMESTAMPDIFF( YEAR, a.birth_date, NOW()) BETWEEN 25 AND 35 )
LIMIT 33;
这里的所有表都是与表用户的一对一关系

  • 轮廓
  • 档案室详情
在用户中使用id列作为主键,在其他表中使用uid作为外键。 开始时,我对上面的查询/设计没有问题,直到其中的表增长到300K行,运行查询需要
OK,时间:0.726000s
才能获取结果,这对我来说太慢了

我尝试使用count(*)根据上述条件计算行数,并得到大致相同的结果, 我需要有更快的方法从查找条件中获取行数,以使分页系统按预期工作,并减少等待时间

正如您在查询中看到的,我使用的是:

FORCE INDEX(profiles_country_city_gender_index)
我认为,由于使用了以下方法,范围中的行数变大了,因此没有多大帮助:

AND a.country = 'INDONESIA' 
AND a.gender = 0
结果(148801行范围限制,按国家/地区,性别等于0),如果我与城市配对,这不是问题,查询时间相当长,因为行的结果要小得多,但当某一天有较大的行时,仍然会有问题

对于可能要求查询解释的任何人:

Explain SELECT SQL_CALC_FOUND_ROWS
        a.uid, 
        b.NAME ...

Results:

| select_type | table   | type   | possible_keys                      | key                                | key_len | ref              | rows   | filtered  | Extra                              |
+-------------+---------+--------+------------------------------------+------------------------------------+---------+------------------+--------+-----------+------------------------------------+
| SIMPLE      | a       | ref    | profiles_country_city_gender_index | profiles_country_city_gender_index | 242     | const            | 148801 | 10.00     | Using index condition; Using where |
| SIMPLE      | a       | ref    | profile_details_uid_foreign        | profile_details_uid_foreign        | 3       | restfulapi.a.uid | 1      | 100.00.00 | Using index                        |
| SIMPLE      | kids    | ref    | profile_details_uid_foreign        | profile_details_uid_foreign        | 3       | restfulapi.a.uid | 1      | 10.00     | Using where                        |
| SIMPLE      | current | ref    | profile_details_uid_foreign        | profile_details_uid_foreign        | 3       | restfulapi.a.uid | 1      | 10.00     | Using where                        |
| SIMPLE      | smoking | ref    | profile_details_uid_foreign        | profile_details_uid_foreign        | 3       | restfulapi.a.uid | 1      | 10.00     | Using where                        |
| SIMPLE      | b       | eq_ref | PRIMARY                            | PRIMARY                            | 3       | restfulapi.a.uid | 1      | 100.00.00 |                                    |
正如您在解释结果中所看到的,并没有表扫描或使用临时或使用范围,只有索引条件。 我想,如果表按国家范围至少有100万行返回,只需将时间与缩放行数乘以300K,就糟了:(

下表定义有助于分析问题:

CREATE TABLE `profile_details` (
  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `uid` mediumint(8) unsigned NOT NULL,
  `intents` tinyint(4) NOT NULL DEFAULT '3',
  `height` smallint(6) DEFAULT NULL,
  `body_type` tinyint(4) NOT NULL DEFAULT '5',
  `kids_pref` tinyint(4) NOT NULL DEFAULT '1',
  `drinking_pref` tinyint(4) NOT NULL DEFAULT '2',
  `living_with` tinyint(4) NOT NULL DEFAULT '0',
  `current_relationship` tinyint(4) NOT NULL DEFAULT '1',
  `sexual_pref` tinyint(4) NOT NULL DEFAULT '1',
  `smoking_pref` tinyint(4) NOT NULL DEFAULT '0',
  `status_online` tinyint(4) NOT NULL DEFAULT '0',
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `profile_details_uid_foreign` (`uid`),
  KEY `idx_multipart` (`intents`,`body_type`,`kids_pref`,`drinking_pref`,`living_with`,`current_relationship`,`sexual_pref`,`smoking_pref`),
  CONSTRAINT `profile_details_uid_foreign` FOREIGN KEY (`uid`) REFERENCES `users` (`id`)
)

CREATE TABLE `profiles` (
  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `uid` mediumint(8) unsigned NOT NULL,
  `birth_date` date NOT NULL,
  `gender` tinyint(4) NOT NULL DEFAULT '0',
  `country` varchar(60) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'ID',
  `city` varchar(60) COLLATE utf8mb4_unicode_ci DEFAULT 'Makassar',
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  `latitude` double NOT NULL DEFAULT '0',
  `longitude` double NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `profiles_uid_foreign` (`uid`),
  KEY `profiles_birth_date_index` (`birth_date`),
  KEY `profiles_latitude_longitude_index` (`latitude`,`longitude`),
  KEY `profiles_country_city_gender_index` (`country`,`city`,`gender`),
  KEY `idx_country_gender_birthdate` (`country`,`gender`,`birth_date`),
  KEY `idx_country_city_gender_birthdate` (`country`,`city`,`gender`,`birth_date`),
  CONSTRAINT `profiles_uid_foreign` FOREIGN KEY (`uid`) REFERENCES `users` (`id`)
)
我如何才能找到解决方案,我是否需要重新设计表格以获得理想的系统?也许这是最后一个选项

编辑

我正在尝试您先前的建议,首先我在三列中添加了一个索引:

CREATE INDEX profiles_country_gender_birth_date_index on `profiles`(country,gender,birth_date);
我尝试选择Count(*)而不与profile_detail连接:

SELECT
    count(*)


FROM
    `profiles` AS a 
    FORCE INDEX ( profiles_country_gender_birth_date_index )
    JOIN `users` AS b ON b.id = a.uid 
and 
a.country = 'INDONESIA' 

    AND a.gender =1 
    AND a.birth_date BETWEEN NOW()- INTERVAL 35 YEAR 
    AND NOW()- INTERVAL 25 YEAR 
结果计时在0.7秒到0.35秒之间不稳定,我不知道为什么会这样。 下面是Json格式的解释查询计划,以帮助找出罪魁祸首

{
  "query_block": {
    "select_id": 1,
    "cost_info": {
      "query_cost": "114747.38"
    },
    "nested_loop": [
      {
        "table": {
          "table_name": "a",
          "access_type": "range",
          "possible_keys": [
            "profiles_country_gender_birth_date_index"
          ],
          "key": "profiles_country_gender_birth_date_index",
          "used_key_parts": [
            "country",
            "gender",
            "birth_date"
          ],
          "key_length": "246",
          "rows_examined_per_scan": 94066,
          "rows_produced_per_join": 32961,
          "filtered": "100.00",
          "index_condition": "((`restfulapi`.`a`.`gender` = 1) and (`restfulapi`.`a`.`country` = 'INDONESIA') and (`restfulapi`.`a`.`birth_date` between <cache>((now() - interval 35 year)) and <cache>((now() - interval 25 year))))",
          "cost_info": {
            "read_cost": "15858.00",
            "eval_cost": "6592.23",
            "prefix_cost": "75194.00",
            "data_read_per_join": "16M"
          },
          "used_columns": [
            "uid",
            "birth_date",
            "gender",
            "country"
          ]
        }
      },
      {
        "table": {
          "table_name": "b",
          "access_type": "eq_ref",
          "possible_keys": [
            "PRIMARY"
          ],
          "key": "PRIMARY",
          "used_key_parts": [
            "id"
          ],
          "key_length": "3",
          "ref": [
            "restfulapi.a.uid"
          ],
          "rows_examined_per_scan": 1,
          "rows_produced_per_join": 32961,
          "filtered": "100.00",
          "using_index": true,
          "cost_info": {
            "read_cost": "32961.15",
            "eval_cost": "6592.23",
            "prefix_cost": "114747.38",
            "data_read_per_join": "89M"
          },
          "used_columns": [
            "id"
          ]
        }
      }
    ]
  }
}
{
“查询块”:{
“选择id”:1,
“成本信息”:{
“查询成本”:“114747.38”
},
“嵌套的_循环”:[
{
“表格”:{
“表格名称”:“a”,
“访问类型”:“范围”,
“可能的_键”:[
“个人资料、国家、性别、出生日期、索引”
],
“关键”:“概况、国家、性别、出生日期、索引”,
“使用过的关键部件”:[
“国家”,
“性别”,
“出生日期”
],
“密钥长度”:“246”,
“每次扫描检查的行数”:94066,
“每联接产生的行数”:32961,
“过滤”:“100.00”,
“索引条件”:“(`restfulapi`.`a`.`gender`=1)和(`restfulapi`.`a`.`country`='INDONESIA')和(`restfulapi`.`a`.`出生日期`介于((现在()-间隔35年))和((现在()-间隔25年))之间”),
“成本信息”:{
“阅读成本”:“15858.00”,
“评估成本”:“6592.23”,
“前缀成本”:“75194.00”,
“每个联接的数据读取”:“16M”
},
“已用列”:[
“uid”,
“出生日期”,
“性别”,
“国家”
]
}
},
{
“表格”:{
“表格名称”:“b”,
“访问类型”:“eq\u ref”,
“可能的_键”:[
“主要”
],
“密钥”:“主要”,
“使用过的关键部件”:[
“id”
],
“密钥长度”:“3”,
“参考”:[
“restfulapi.a.uid”
],
“每次扫描检查的行数”:1,
“每联接产生的行数”:32961,
“过滤”:“100.00”,
“使用指数”:正确,
“成本信息”:{
“阅读成本”:“32961.15”,
“评估成本”:“6592.23”,
“前缀成本”:“114747.38”,
“每个联接的数据读取”:“89M”
},
“已用列”:[
“id”
]
}
}
]
}
}
并将
出生日期的用法更改为“sargeable”:

这样优化器就可以使用
出生日期

LIMIT 33
——您关心哪33行吗?也许您需要一个
订单人

当计划
加入配置文件详细信息…
有效时,不要执行
加入(选择…配置文件详细信息…

SQL\u CALC\u FOUND\u ROWS
需要花费一些钱。删除它以查看速度,然后决定是否值得保留

我认为您不需要多次加入profile\u details
,特别是因为它与
profiles
是1:1

我的意思是:

而不是加入(选择…)

JOIN  profile_details AS d  USING(uid)
然后将以下内容添加到WHERE子句中:

AND  d.kids_pref = 1
AND  d.current_relationship = 1
AND  d.smoking_pref = 1 
避免文件排序

INDEX(country, gender,   -- Tested with '='
      birth_date,        -- Tested as a "range"
      uid)               -- For the ORDER BY -- Useless!
构建索引时,请按以下顺序包括列

  • 所有列测试为“列=常量”
  • 一个范围(如之间的
    )。如果这与
    顺序相同,则可能避免使用“文件排序”
  • 如果
    中没有“范围”,则

  • 所有列测试为“列=常量”
  • orderby
    列——假设它们都是
    DESC
    或all
    ASC
    (或者,在MySQL 8.0中,与
    索引定义匹配)。这可能会避免“filesort”
  • 但是索引不能同时处理“范围”和不同的“顺序”。请考虑下面的内容。您有一个姓和名的人的列表。

    SELECT ...
        WHERE last_name LIKE 'Ja%'   -- a "range"
        ORDER BY first_name;
    
    索引(姓、名)
    将有助于处理
    中的
    ,但会混淆名。反之亦然


    (这是一种简化,有关更多详细信息,请参见。)

    请按列设置该表的格式
    AND  d.kids_pref = 1
    AND  d.current_relationship = 1
    AND  d.smoking_pref = 1 
    
    INDEX(country, gender,   -- Tested with '='
          birth_date,        -- Tested as a "range"
          uid)               -- For the ORDER BY -- Useless!
    
    SELECT ...
        WHERE last_name LIKE 'Ja%'   -- a "range"
        ORDER BY first_name;