在这种情况下,MyISAM 在 mysql 中比 InnoDB 快得多

In this case MyISAM is dramatically faster than InnoDB in mysql

我一直在写 InnoDB table 中计算客户之间距离的算法的结果。例如,如果我的客户是 A、B、C 和 D,则数据库中的 table 看起来像这样,在其他列中:

From | To    | Distance
  A     B        344
  A     C        274
  A     D        182
  B     C        338

等等...排的很多我觉得我会打到5000万

其他列是 product_type 和值。这些告诉我客户 B(列中的 customer_to)购买了多少 product_type。这意味着我根据客户 B 购买的 product_type 数量对每双进行多次。

我需要查询以将每个客户与他的邻居购买的产品和价值分组。查询如下所示:

select customer_from, product_type, avg(value) as opportunity
from customer_distances
where distance < 500
group by customer_from, product_type
order by opportunity desc; 

innodb table 无法回答我的问题。尽管我将 net_read_timeout 更改为 28800,但 mysql 连接在查询期间丢失了。

我认为它与用于事务处理而不是用于密集查询的 innodb 构建有关。所以我用 MyIsam 作为引擎创建了一个新的 table 并插入-select 来自 innodb table.

的所有记录

正如预期的那样,select 非常快(70 段),所有其他 selects 都像 count(distinct customer_from),几乎是瞬时的。

出于好奇,我尝试继续在 myisam table 中插入距离的过程。当程序开始 运行 比它在 innodb table -for INSERTS!

上工作时至少快 100 倍时,我感到很惊讶

对于每个客户,程序插入大约 3000 行(每个 product_type 每个邻居一个。大约 300 个邻居和每个客户 10 product_types)。使用 innodb table 插入单个客户需要 40 到 60 秒(大约 3000 行)。使用 myisam table,插入 3 个客户需要 1 秒(大约 9000 行)。

一些额外信息:

所以总结起来问题是: 为什么 MyISAM 使用插入语句那么快? 你怎么看?

编辑 1:我正在为 tables、innodb 和 myisam 添加创建语句。 编辑 2:我删除了一些无用的信息,并在这里和那里进行了一些格式化。

/* INNODB TABLE */
CREATE TABLE `customer_distances` (
  `customer_from` varchar(50) NOT NULL,
  `customer_from_type` varchar(50) DEFAULT NULL,
  `customer_from_segment` varchar(50) DEFAULT NULL,
  `customer_from_district` int(11) DEFAULT NULL,
  `customer_from_zone` int(11) DEFAULT NULL,
  `customer_from_longitud` decimal(15,6) DEFAULT NULL,
  `customer_from_latitud` decimal(15,6) DEFAULT NULL,
  `customer_to` varchar(50) NOT NULL,
  `customer_to_type` varchar(50) DEFAULT NULL,
  `customer_to_segment` varchar(50) DEFAULT NULL,
  `customer_to_district` int(11) DEFAULT NULL,
  `customer_to_zone` int(11) DEFAULT NULL,
  `customer_to_longitud` decimal(15,6) DEFAULT NULL,
  `customer_to_latitud` decimal(15,6) DEFAULT NULL,
  `distance` decimal(10,2) DEFAULT NULL,
  `product_business_line` varchar(50) DEFAULT NULL,
  `product_type` varchar(50) NOT NULL,
  `customer_from_liters` decimal(10,2) DEFAULT NULL,
  `customer_from_dollars` decimal(10,2) DEFAULT NULL,
  `customer_from_units` decimal(10,2) DEFAULT NULL,
  `customer_to_liters` decimal(10,2) DEFAULT NULL,
  `customer_to_dollars` decimal(10,2) DEFAULT NULL,
  `customer_to_units` decimal(10,2) DEFAULT NULL,
  `liters_opportunity` decimal(10,2) DEFAULT NULL,
  `dollars_opportunity` decimal(10,2) DEFAULT NULL,
  `units_oportunity` decimal(10,2) DEFAULT NULL,
  PRIMARY KEY (`cliente_desde`,`cliente_hasta`,`grupo`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

/* MYISAM TABLE */
CREATE TABLE `customer_distances` (
  `customer_from` varchar(50) NOT NULL,
  `customer_from_type` varchar(50) DEFAULT NULL,
  `customer_from_segment` varchar(50) DEFAULT NULL,
  `customer_from_district` int(11) DEFAULT NULL,
  `customer_from_zone` int(11) DEFAULT NULL,
  `customer_from_longitud` decimal(15,6) DEFAULT NULL,
  `customer_from_latitud` decimal(15,6) DEFAULT NULL,
  `customer_to` varchar(50) NOT NULL,
  `customer_to_type` varchar(50) DEFAULT NULL,
  `customer_to_segment` varchar(50) DEFAULT NULL,
  `customer_to_district` int(11) DEFAULT NULL,
  `customer_to_zone` int(11) DEFAULT NULL,
  `customer_to_longitud` decimal(15,6) DEFAULT NULL,
  `customer_to_latitud` decimal(15,6) DEFAULT NULL,
  `distance` decimal(10,2) DEFAULT NULL,
  `product_business_line` varchar(50) DEFAULT NULL,
  `product_type` varchar(50) NOT NULL,
  `customer_from_liters` decimal(10,2) DEFAULT NULL,
  `customer_from_dollars` decimal(10,2) DEFAULT NULL,
  `customer_from_units` decimal(10,2) DEFAULT NULL,
  `customer_to_liters` decimal(10,2) DEFAULT NULL,
  `customer_to_dollars` decimal(10,2) DEFAULT NULL,
  `customer_to_units` decimal(10,2) DEFAULT NULL,
  `liters_opportunity` decimal(10,2) DEFAULT NULL,
  `dollars_opportunity` decimal(10,2) DEFAULT NULL,
  `units_oportunity` decimal(10,2) DEFAULT NULL,
  PRIMARY KEY (`cliente_desde`,`cliente_hasta`,`grupo`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

插入

  • InnoDB,默认情况下,"commits"每个INSERT立即。这可以通过一次聚集 100-1000 行来解决。
  • 批处理插入将加速 MyISAM 和 InnoDB - 可能提高 10 倍。
  • 了解 autocommitBEGIN..COMMIT

Select

  • InnoDB 消耗的磁盘 space 比 MyISAM 多——通常是 2x-3x;这会影响 table 扫描,您可能
  • 对于该查询,(customer_from、product_type、距离)上的复合索引可能对两个引擎都有帮助。

调整

  • 当运行宁只是 MyISAM时,设置key_buffer_size为RAM的20%和innodb_buffer_pool_size=0
  • 当运行宁只是 InnoDB时,设置key_buffer_size到只有10M和innodb_buffer_pool_size到RAM的70%。

规范化和保存space

  • 更小 --> 更可缓存 --> 更少 I/O --> 更快(在任一引擎中)
  • DECIMAL(10,2) 在大多数情况下并不是最好的。考虑 FLOAT 表示非货币(例如 distance)。考虑更少的数字;最多可处理 99,999,999.99,占用 5 个字节。
  • 复制列通常不是一个好主意,例如 customer_fromcustomer_to 的 10 列。有一个 Customers table,两者都在里面。
  • 你的纬度和经度都是7个字节,并且有不必要的分辨率。建议 latidud DECIMAL(6,4)longitud (7,4),总计 为 7 个字节。 (这些分辨率为 16m/52ft。)

结果

在这些建议之后,5000 万行 table 会小很​​多,并且 运行 在两个引擎中都快得多。然后运行再次比较