如何索引状态(标志)字段

Izo*_*i4a 0 mysql indexing query-performance

CREATE TABLE `products` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `brand` int(11) DEFAULT NULL,
  `shown` tinyint(4) DEFAULT '1',
  PRIMARY KEY (`id`),
  KEY `fk_products_brandId_idx` (`brand`),
  KEY `pk_products_shown` (`shown`),
  CONSTRAINT `fk_products_brandId` FOREIGN KEY (`brand`) REFERENCES `brands` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `fk_products_categoryId` FOREIGN KEY (`category`) REFERENCES `categories` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
Run Code Online (Sandbox Code Playgroud)

我有这个“显示”字段,用于标记已删除的记录(如果用户删除记录,则不会删除;这些字段设置为值 0)。

所以我所有的查询或多或少都是这样的:

"SELECT * FROM products WHERE brand = 1 AND shown = 1"
Run Code Online (Sandbox Code Playgroud)

但如果我解释一下,在 MySQL/oracle Workbench 中它说的是全表扫描。显然这是有道理的,因为我 99% 的数据库记录都会显示 = 1,但品牌也有一个索引。

这是工作台输出的解释

{
  "query_block": {
    "select_id": 1,
    "cost_info": {
      "query_cost": "2.40"
    },
    "table": {
      "table_name": "products",
      "access_type": "ALL",
      "possible_keys": [
        "fk_products_brandId_idx",
        "pk_products_shown"
      ],
      "rows_examined_per_scan": 7,
      "rows_produced_per_join": 2,
      "filtered": "40.82",
      "cost_info": {
        "read_cost": "1.83",
        "eval_cost": "0.57",
        "prefix_cost": "2.40",
        "data_read_per_join": "6K"
      },
      "used_columns": [
        "id",
        "name_bg",
        "name_en",
        "category",
        "weight",
        "width",
        "length",
        "height",
        "brand",
        "availability",
        "text_bg",
        "text_en",
        "sold",
        "price",
        "price_retail",
        "linked_products",
        "created_at",
        "created_by",
        "updated_at",
        "updated_by",
        "shown"
      ],
      "attached_condition": "((`whatever`.`products`.`shown` = 1) and (`whatever`.`products`.`brand` = 18))"
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

我的逻辑是否错误,或者我显示的索引已被修改,因此正常查询不会进行全表扫描?

mysql 5.7

O. *_*nes 6

在 上创建复合索引(brand, shown)brand = constant AND shown=1该索引将通过 on和 just 的过滤器加速查询brand=constant

ALTER TABLE products ADD INDEX brand_shown (brand, shown);
Run Code Online (Sandbox Code Playgroud)

该索引还加速了表单的查询

SELECT * FROM products WHERE brand >= 1 AND brand <=10 AND shown = 1;
Run Code Online (Sandbox Code Playgroud)

或者

SELECT * FROM products WHERE brand BETWEEN 1 AND 10 AND shown = 1;
Run Code Online (Sandbox Code Playgroud)

做好索引工作需要一些知识。阅读https://use-the-index-luke.com/是一种很好的学习方式。

MySQL 可能正在执行全表扫描,因为基数(不同值的数量)shown非常低。因此它猜测表扫描比尝试找出如何从索引获取数据要便宜。

如果您可以将查询修改为

SELECT name FROM products WHERE brand = constant AND shown = 1
Run Code Online (Sandbox Code Playgroud)

然后添加name到索引中,您将得到一个所谓的覆盖索引。这将允许 MySQL 仅通过对索引进行范围扫描来满足您的查询,并且速度相当快。

ALTER TABLE products ADD INDEX brand_shown_name (brand, shown, name);
Run Code Online (Sandbox Code Playgroud)

专家提示避免SELECT *在性能存在问题的查询中使用。相反,列出您需要的列。当您这样做时,MySQL 可能能够应用一些优化(例如覆盖索引优化)。

专业提示 2避免仅仅为了良好的衡量标准而创建索引。创建您实际需要的查询(或强制唯一性)。额外的索引会占用空间并减慢 INSERT 和 UPDATE 操作。