更新mysql大table挂得太及时
Update mysql big table hang too time
更新时的性能问题 MySql MyISAM big table 根据相同的索引对列进行升序排列 table
我的问题是服务器只有 4 GB 内存。
我必须像这样进行更新查询:previous asked question
我的是这样的:
set @orderid = 0;
update images im
set im.orderid = (select @orderid := @orderid + 1)
ORDER BY im.hotel_id, im.idImageType;
在 im.hotel_id, im.idImageType
我有一个升序索引。
在 im.orderid
我也有一个升序索引。
table 有 2100 万条记录 并且是一个 MyIsam table。
table是这样的:
CREATE TABLE `images` (
`photo_id` int(11) NOT NULL,
`idImageType` int(11) NOT NULL,
`hotel_id` int(11) NOT NULL,
`room_id` int(11) DEFAULT NULL,
`url_original` varchar(150) COLLATE utf8_unicode_ci NOT NULL,
`url_max300` varchar(150) COLLATE utf8_unicode_ci NOT NULL,
`url_square60` varchar(150) COLLATE utf8_unicode_ci NOT NULL,
`archive` int(11) NOT NULL DEFAULT '0',
`orderid` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`photo_id`),
KEY `idImageType` (`idImageType`),
KEY `hotel_id` (`hotel_id`),
KEY `hotel_id_idImageType` (`hotel_id`,`idImageType`),
KEY `archive` (`archive`),
KEY `room_id` (`room_id`),
KEY `orderid` (`orderid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
性能问题:挂了几分钟!
服务器磁盘也很忙。
我的问题是:有没有更好的方式达到同样的效果?
我是否需要对 table 进行分区或其他方式来提高性能?
我无法修改服务器硬件,但可以调整 MySql 应用程序数据库服务器设置。
此致
你可以使用其中的一些:
更新引擎到 InnoDB,它只阻塞一行,而不是所有更新时的 table。
使用 photo_id 和良好的 orderid 创建 #temp table,然后从该温度更新您的 table:
update images im, temp tp
set im.orderid = tp.orderid
where im.photo_id = tp.photo_id
这将是最快的方式,当你填充你的 tmp table - 你在主要 table 上没有块。
- 您可以在批量更新前删除索引。在您完成所有单次更新后,您需要重建索引并且需要很长时间。
KEY `hotel_id` (`hotel_id`),
KEY `hotel_id_idImageType` (`hotel_id`,`idImageType`),
放弃前者;后者负责满足任何需要。 (这不会加快原始查询的速度。)
"The problem is the performance: hang for several minutes!"有什么问题?
- 其他查询被阻塞几分钟? (InnoDB 应该有所帮助。)
- 你运行这个更新频繁而且很烦人? (为什么在世界上??)
- 还有别的吗?
这个索引在执行更新时开销很大:
KEY `orderid` (`orderid`)
删除它并重新创建它。 (不要打扰删除其余部分。)使用 InnoDB 的另一个原因是这些操作可以完成(在 5.6 中)而无需复制 table 。 (21M 行 == 很长时间,如果它必须复制 table!)
除了 photo_id 之外,为什么还要构建第二个唯一索引 (orderid),它已经是唯一索引了?我问这个是因为可能有另一种方法可以解决真正的问题,不涉及这个耗时的Update。
我有两个更具体的建议,但我想先在这里回答你。
编辑分页,按hotel_id, idImageType, photo_id
:
排序
可以按该三元组顺序读取记录。甚至 "paginate" 通过他们。
如果您在 ($hid, $type, $pid)
之后 "left off",这里将是 'next' 20 条记录:
WHERE hotel_id >= $hid
AND ( hotel_id > $hid
OR idImageType >= $type
AND ( idImageType > $type
OR photo_id > $pid
)
)
ORDER BY hotel_id, idImageType, photo_id
LIMIT 20
并且有
INDEX(hotel_id, idImageType, photo_id)
这避免了 orderid
及其耗时的更新。
一次分页一个 hotel_id
会更简单。那行得通吗?
编辑 2 -- 消除停机时间
由于您要定期重新加载整个 table,因此在重新加载时执行此操作:
CREATE TABLE New
与建议索引更改。
- 将数据加载到
New
。 (一定要避免 51 分钟的超时;我不知道是什么原因造成的。)
RENAME TABLE images TO old, New TO images;
DROP TABLE old;
这将避免阻塞 table 加载和架构更改。原子步骤 #3 将有一个非常短的块。
计划在每次重新加载数据时执行此过程。
另一个好处 -- 在步骤 #2 之后,您可以测试新数据以查看它是否正常。
感谢每一个人。你的回答对我帮助很大。我想现在我找到了更好的解决方案。
这个问题涉及两个关键问题:
- 高效分页大型 table
- 更新大table。
为了在大型 table 上进行高效分页,我找到了一个解决方案,方法是对 table 进行先前的更新,但是这样做我在更新所需的 51 分钟时间内遇到了问题以及随之而来的 java 基础架构超时(spring-批处理步骤)。
现在在你的帮助下,我找到了两种在大 table 上分页的解决方案,以及一种更新大 table.
的解决方案
为了达到这种性能,服务器需要内存。我在使用 32 GB 内存的开发服务器上尝试此解决方案。
常见的解决步骤
要按照我需要的方式对字段图普拉进行分页,我已经制作了一个索引:
KEY `hotel_id_idImageType` (`hotel_id`,`idImageType`)
为了实现新的解决方案,我们必须通过将主键部分添加到索引尾部来更改此索引 KEY hotel_id_idImageType (hotel_id,idImageType, primary key fields)
:
drop index hotel_id_idImageType on images;
create index hotelTypePhoto on images (hotel_id, idImageType, photo_id);
这是为了避免触摸 table 并且只使用索引文件...
假设我们想要第19000000条记录之后的10条记录。
这个答案里的小数点是这个,
解决方案 1
这个解决方案是非常实用的,不需要额外的字段orderid
,而且在分页之前你不需要做任何更新:
select * from images im inner join
(select photo_id from images
order by hotel_id, idImageType, photo_id
limit 19000000,10) k
on im.photo_id = k.photo_id;
要在我的 2100 万 table 条记录上生成 table k 只需要 1.5 秒因为它仅使用索引 hotelTypePhoto
中的三个字段,所以无法访问 table 文件并且只能处理索引文件。
订单与原来要求的一样 (hotel_id, idImageType) 因为包含在 (hotel_id, idImageType, photo_id): 相同的子集...
连接不需要时间,所以每次第一次在同一页面上执行分页只需要 1.5 秒,如果你必须在 3 个月内批量执行它,这是一个很好的时间。
在使用 4 GB 内存的生产服务器上 相同的查询需要 3.5 秒。
分区 table 无助于提高性能。
如果服务器将其放入缓存中,时间会减少,或者如果您执行 jdbc 参数声明,时间也会减少(我想)。
如果你必须经常使用它,它的优点是它不关心数据是否改变。
解决方案 2
这个解决方案需要额外的字段orderid
并且需要通过批量导入更新一次orderid并且数据在下一次批量导入之前不会改变。
然后您可以在 0,000 秒内在 table 上分页
set @orderid = 0;
update images im inner join (
select photo_id, (@orderid := @orderid + 1) as newOrder
from images order by hotel_id, idImageType, photo_id
) k
on im.photo_id = k.photo_id
set im.orderid = k.newOrder;
table k 几乎和第一个解决方案一样快。
所有更新只用了 150,551 秒,比 51 分钟好多了!!! (150 秒对 3060 秒)
批量更新后,您可以通过以下方式进行分页:
select * from images im where orderid between 19000000 and 19000010;
或更好
select * from images im where orderid >= 19000000 and orderid< 19000010;
这需要 0,000 秒来执行第一次和所有其他时间。
在 Rick 评论后编辑
解决方案 3
这个解决方案是为了避免额外的字段和偏移量的使用。但也需要像 this solution
中那样记忆最后一页
这是一个快速的解决方案,仅需 4GB 内存即可用于在线服务器生产
假设您需要读取 20000000 之后的最后十条记录。
有两种情况需要注意:
- 如果你像我一样需要全部,你可以从第一页开始读到 20000000,并更新一些变量以获取最后一页读取的内存。
- 你只需要阅读 20000000 之后的最后 10 个。
在第二种情况下,您必须进行预查询才能找到起始页:
select hotel_id, idImageType, photo_id
from images im
order by hotel_id, idImageType, photo_id limit 20000000,1
它给我:
+----------+-------------+----------+
| hotel_id | idImageType | photo_id |
+----------+-------------+----------+
| 1309878 | 4 | 43259857 |
+----------+-------------+----------+
这需要 6.73 秒。
所以您可以将这些值存储在变量中以备下次使用。
假设我们命名为 @hot=1309878, @type=4, @photo=43259857
然后你可以像这样在第二个查询中使用它:
select * from images im
where
hotel_id>@hot OR (
hotel_id=@hot and idImageType>@type OR (
idImageType=@type and photo_id>@photo
)
)
order by hotel_id, idImageType, photo_id limit 10;
第一个子句hotel_id>@hot
获取滚动索引上实际第一个字段之后的所有记录,但丢失了一些记录。要取它我们必须做OR子句,取第一个索引字段所有未读记录。
现在只需 0.10 秒。
但是这个查询可以优化(布尔分配):
select * from images im
where
hotel_id>@hot OR (
hotel_id=@hot and
(idImageType>@type or idImageType=@type)
and (idImageType>@type or photo_id>@photo
)
)
order by hotel_id, idImageType, photo_id limit 10;
变成:
select * from images im
where
hotel_id>@hot OR (
hotel_id=@hot and
idImageType>=@type
and (idImageType>@type or photo_id>@photo
)
)
order by hotel_id, idImageType, photo_id limit 10;
变成:
select * from images im
where
(hotel_id>@hot OR hotel_id=@hot) and
(hotel_id>@hot OR
(idImageType>=@type and (idImageType>@type or photo_id>@photo))
)
order by hotel_id, idImageType, photo_id limit 10;
变成:
select * from images im
where
hotel_id>=@hot and
(hotel_id>@hot OR
(idImageType>=@type and (idImageType>@type or photo_id>@photo))
)
order by hotel_id, idImageType, photo_id limit 10;
它们是否与我们可以通过限制获得的数据相同?
快速不详尽测试:
select im.* from images im inner join (
select photo_id from images order by hotel_id, idImageType, photo_id limit 20000000,10
) k
on im.photo_id=k.photo_id
order by im.hotel_id, im.idImageType, im.photo_id;
这需要 6.56 秒,数据与上面的查询相同。
所以测试呈阳性。
在此解决方案中,您只需在第一次需要在第一页上查找以阅读时花费 6.73 秒(但如果您需要所有内容,则不需要)。
要实现所有其他页面,您只需 0.10 秒即可获得非常好的结果。
感谢 rick 对基于存储最后一页阅读的解决方案的提示。
结论
在解决方案 1 上,您没有任何额外字段,每页花费 3.5 秒
在 解决方案 2 上,您有额外的字段并且需要在 150 秒内使用大内存服务器(已测试 32 GB)。但随后您在 0,000 秒内阅读了该页面。
在 解决方案 3 你没有任何额外的字段但必须存储最后一页读取指针,如果你没有从第一页开始阅读你第一页必须花费 6.73 秒。那么您在所有其他页面上只花费 0.10 秒。
此致
编辑 3
解决方案 3 正是 Rick 建议的。对不起,在我之前的 解决方案 3 中我犯了一个错误,当我编写正确的解决方案时,我应用了一些布尔规则,比如分配 属性 等等,毕竟我得到了相同的 Rich 解决方案!
问候
更新时的性能问题 MySql MyISAM big table 根据相同的索引对列进行升序排列 table
我的问题是服务器只有 4 GB 内存。
我必须像这样进行更新查询:previous asked question
我的是这样的:
set @orderid = 0;
update images im
set im.orderid = (select @orderid := @orderid + 1)
ORDER BY im.hotel_id, im.idImageType;
在 im.hotel_id, im.idImageType
我有一个升序索引。
在 im.orderid
我也有一个升序索引。
table 有 2100 万条记录 并且是一个 MyIsam table。
table是这样的:
CREATE TABLE `images` (
`photo_id` int(11) NOT NULL,
`idImageType` int(11) NOT NULL,
`hotel_id` int(11) NOT NULL,
`room_id` int(11) DEFAULT NULL,
`url_original` varchar(150) COLLATE utf8_unicode_ci NOT NULL,
`url_max300` varchar(150) COLLATE utf8_unicode_ci NOT NULL,
`url_square60` varchar(150) COLLATE utf8_unicode_ci NOT NULL,
`archive` int(11) NOT NULL DEFAULT '0',
`orderid` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`photo_id`),
KEY `idImageType` (`idImageType`),
KEY `hotel_id` (`hotel_id`),
KEY `hotel_id_idImageType` (`hotel_id`,`idImageType`),
KEY `archive` (`archive`),
KEY `room_id` (`room_id`),
KEY `orderid` (`orderid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
性能问题:挂了几分钟!
服务器磁盘也很忙。
我的问题是:有没有更好的方式达到同样的效果?
我是否需要对 table 进行分区或其他方式来提高性能?
我无法修改服务器硬件,但可以调整 MySql 应用程序数据库服务器设置。
此致
你可以使用其中的一些:
更新引擎到 InnoDB,它只阻塞一行,而不是所有更新时的 table。
使用 photo_id 和良好的 orderid 创建 #temp table,然后从该温度更新您的 table:
update images im, temp tp set im.orderid = tp.orderid where im.photo_id = tp.photo_id
这将是最快的方式,当你填充你的 tmp table - 你在主要 table 上没有块。
- 您可以在批量更新前删除索引。在您完成所有单次更新后,您需要重建索引并且需要很长时间。
KEY `hotel_id` (`hotel_id`),
KEY `hotel_id_idImageType` (`hotel_id`,`idImageType`),
放弃前者;后者负责满足任何需要。 (这不会加快原始查询的速度。)
"The problem is the performance: hang for several minutes!"有什么问题?
- 其他查询被阻塞几分钟? (InnoDB 应该有所帮助。)
- 你运行这个更新频繁而且很烦人? (为什么在世界上??)
- 还有别的吗?
这个索引在执行更新时开销很大:
KEY `orderid` (`orderid`)
删除它并重新创建它。 (不要打扰删除其余部分。)使用 InnoDB 的另一个原因是这些操作可以完成(在 5.6 中)而无需复制 table 。 (21M 行 == 很长时间,如果它必须复制 table!)
除了 photo_id 之外,为什么还要构建第二个唯一索引 (orderid),它已经是唯一索引了?我问这个是因为可能有另一种方法可以解决真正的问题,不涉及这个耗时的Update。
我有两个更具体的建议,但我想先在这里回答你。
编辑分页,按hotel_id, idImageType, photo_id
:
可以按该三元组顺序读取记录。甚至 "paginate" 通过他们。
如果您在 ($hid, $type, $pid)
之后 "left off",这里将是 'next' 20 条记录:
WHERE hotel_id >= $hid
AND ( hotel_id > $hid
OR idImageType >= $type
AND ( idImageType > $type
OR photo_id > $pid
)
)
ORDER BY hotel_id, idImageType, photo_id
LIMIT 20
并且有
INDEX(hotel_id, idImageType, photo_id)
这避免了 orderid
及其耗时的更新。
一次分页一个 hotel_id
会更简单。那行得通吗?
编辑 2 -- 消除停机时间
由于您要定期重新加载整个 table,因此在重新加载时执行此操作:
CREATE TABLE New
与建议索引更改。- 将数据加载到
New
。 (一定要避免 51 分钟的超时;我不知道是什么原因造成的。) RENAME TABLE images TO old, New TO images;
DROP TABLE old;
这将避免阻塞 table 加载和架构更改。原子步骤 #3 将有一个非常短的块。
计划在每次重新加载数据时执行此过程。
另一个好处 -- 在步骤 #2 之后,您可以测试新数据以查看它是否正常。
感谢每一个人。你的回答对我帮助很大。我想现在我找到了更好的解决方案。
这个问题涉及两个关键问题:
- 高效分页大型 table
- 更新大table。
为了在大型 table 上进行高效分页,我找到了一个解决方案,方法是对 table 进行先前的更新,但是这样做我在更新所需的 51 分钟时间内遇到了问题以及随之而来的 java 基础架构超时(spring-批处理步骤)。
现在在你的帮助下,我找到了两种在大 table 上分页的解决方案,以及一种更新大 table.
的解决方案
为了达到这种性能,服务器需要内存。我在使用 32 GB 内存的开发服务器上尝试此解决方案。
常见的解决步骤
要按照我需要的方式对字段图普拉进行分页,我已经制作了一个索引:
KEY `hotel_id_idImageType` (`hotel_id`,`idImageType`)
为了实现新的解决方案,我们必须通过将主键部分添加到索引尾部来更改此索引 KEY hotel_id_idImageType (hotel_id,idImageType, primary key fields)
:
drop index hotel_id_idImageType on images;
create index hotelTypePhoto on images (hotel_id, idImageType, photo_id);
这是为了避免触摸 table 并且只使用索引文件...
假设我们想要第19000000条记录之后的10条记录。
这个答案里的小数点是这个,
解决方案 1
这个解决方案是非常实用的,不需要额外的字段orderid
,而且在分页之前你不需要做任何更新:
select * from images im inner join
(select photo_id from images
order by hotel_id, idImageType, photo_id
limit 19000000,10) k
on im.photo_id = k.photo_id;
要在我的 2100 万 table 条记录上生成 table k 只需要 1.5 秒因为它仅使用索引 hotelTypePhoto
中的三个字段,所以无法访问 table 文件并且只能处理索引文件。
订单与原来要求的一样 (hotel_id, idImageType) 因为包含在 (hotel_id, idImageType, photo_id): 相同的子集...
连接不需要时间,所以每次第一次在同一页面上执行分页只需要 1.5 秒,如果你必须在 3 个月内批量执行它,这是一个很好的时间。
在使用 4 GB 内存的生产服务器上 相同的查询需要 3.5 秒。
分区 table 无助于提高性能。
如果服务器将其放入缓存中,时间会减少,或者如果您执行 jdbc 参数声明,时间也会减少(我想)。
如果你必须经常使用它,它的优点是它不关心数据是否改变。
解决方案 2
这个解决方案需要额外的字段orderid
并且需要通过批量导入更新一次orderid并且数据在下一次批量导入之前不会改变。
然后您可以在 0,000 秒内在 table 上分页
set @orderid = 0;
update images im inner join (
select photo_id, (@orderid := @orderid + 1) as newOrder
from images order by hotel_id, idImageType, photo_id
) k
on im.photo_id = k.photo_id
set im.orderid = k.newOrder;
table k 几乎和第一个解决方案一样快。
所有更新只用了 150,551 秒,比 51 分钟好多了!!! (150 秒对 3060 秒)
批量更新后,您可以通过以下方式进行分页:
select * from images im where orderid between 19000000 and 19000010;
或更好
select * from images im where orderid >= 19000000 and orderid< 19000010;
这需要 0,000 秒来执行第一次和所有其他时间。
在 Rick 评论后编辑
解决方案 3
这个解决方案是为了避免额外的字段和偏移量的使用。但也需要像 this solution
中那样记忆最后一页这是一个快速的解决方案,仅需 4GB 内存即可用于在线服务器生产
假设您需要读取 20000000 之后的最后十条记录。
有两种情况需要注意:
- 如果你像我一样需要全部,你可以从第一页开始读到 20000000,并更新一些变量以获取最后一页读取的内存。
- 你只需要阅读 20000000 之后的最后 10 个。
在第二种情况下,您必须进行预查询才能找到起始页:
select hotel_id, idImageType, photo_id
from images im
order by hotel_id, idImageType, photo_id limit 20000000,1
它给我:
+----------+-------------+----------+
| hotel_id | idImageType | photo_id |
+----------+-------------+----------+
| 1309878 | 4 | 43259857 |
+----------+-------------+----------+
这需要 6.73 秒。
所以您可以将这些值存储在变量中以备下次使用。
假设我们命名为 @hot=1309878, @type=4, @photo=43259857
然后你可以像这样在第二个查询中使用它:
select * from images im
where
hotel_id>@hot OR (
hotel_id=@hot and idImageType>@type OR (
idImageType=@type and photo_id>@photo
)
)
order by hotel_id, idImageType, photo_id limit 10;
第一个子句hotel_id>@hot
获取滚动索引上实际第一个字段之后的所有记录,但丢失了一些记录。要取它我们必须做OR子句,取第一个索引字段所有未读记录。
现在只需 0.10 秒。
但是这个查询可以优化(布尔分配):
select * from images im
where
hotel_id>@hot OR (
hotel_id=@hot and
(idImageType>@type or idImageType=@type)
and (idImageType>@type or photo_id>@photo
)
)
order by hotel_id, idImageType, photo_id limit 10;
变成:
select * from images im
where
hotel_id>@hot OR (
hotel_id=@hot and
idImageType>=@type
and (idImageType>@type or photo_id>@photo
)
)
order by hotel_id, idImageType, photo_id limit 10;
变成:
select * from images im
where
(hotel_id>@hot OR hotel_id=@hot) and
(hotel_id>@hot OR
(idImageType>=@type and (idImageType>@type or photo_id>@photo))
)
order by hotel_id, idImageType, photo_id limit 10;
变成:
select * from images im
where
hotel_id>=@hot and
(hotel_id>@hot OR
(idImageType>=@type and (idImageType>@type or photo_id>@photo))
)
order by hotel_id, idImageType, photo_id limit 10;
它们是否与我们可以通过限制获得的数据相同?
快速不详尽测试:
select im.* from images im inner join (
select photo_id from images order by hotel_id, idImageType, photo_id limit 20000000,10
) k
on im.photo_id=k.photo_id
order by im.hotel_id, im.idImageType, im.photo_id;
这需要 6.56 秒,数据与上面的查询相同。
所以测试呈阳性。
在此解决方案中,您只需在第一次需要在第一页上查找以阅读时花费 6.73 秒(但如果您需要所有内容,则不需要)。
要实现所有其他页面,您只需 0.10 秒即可获得非常好的结果。
感谢 rick 对基于存储最后一页阅读的解决方案的提示。
结论
在解决方案 1 上,您没有任何额外字段,每页花费 3.5 秒
在 解决方案 2 上,您有额外的字段并且需要在 150 秒内使用大内存服务器(已测试 32 GB)。但随后您在 0,000 秒内阅读了该页面。
在 解决方案 3 你没有任何额外的字段但必须存储最后一页读取指针,如果你没有从第一页开始阅读你第一页必须花费 6.73 秒。那么您在所有其他页面上只花费 0.10 秒。
此致
编辑 3
解决方案 3 正是 Rick 建议的。对不起,在我之前的 解决方案 3 中我犯了一个错误,当我编写正确的解决方案时,我应用了一些布尔规则,比如分配 属性 等等,毕竟我得到了相同的 Rich 解决方案! 问候