MYSQL 4000 万条记录的大量更新 table 使用 128GB ram 专用服务器需要很长时间
MYSQL lot's of update on 40 million record table wih 128GB ram dedicated server take long time
我们遇到了单个 table 更新需要很长时间的问题。 table 包含约 3000 万行。
作业每天运行,截断 table 并在 table 中插入来自其他来源的新数据。
这里是 table:
CREATE TABLE tempportfolio1 (
SR_NO int(4) NOT NULL AUTO_INCREMENT,
TR_DATE date DEFAULT NULL,
TRAN_CODE decimal(18,0) DEFAULT NULL,
TRAN_TYPE varchar(20) DEFAULT NULL,
SCH_CODE bigint(8) DEFAULT NULL,
Nature varchar(25) DEFAULT NULL,
UNITS decimal(19,4) DEFAULT NULL,
BAL_UNITS decimal(19,4) DEFAULT NULL,
DIVD_RECD double DEFAULT '0',
FOLIO_NO varchar(50) DEFAULT NULL,
FLAG varchar(5) DEFAULT NULL,
MBALANCE double DEFAULT NULL,
PBALANCE double DEFAULT NULL,
MTotalBalance double DEFAULT NULL,
PL_NOTIONAL decimal(19,4) DEFAULT NULL,
PL_BOOKED decimal(19,4) DEFAULT NULL,
AGE int(4) DEFAULT NULL,
RET_ABS decimal(19,4) DEFAULT NULL,
RET_CAGR decimal(19,4) DEFAULT NULL,
INDEX_AMT decimal(19,4) DEFAULT NULL,
RET_INDEX_ABS decimal(19,4) DEFAULT NULL,
Ret_Index_CAGR decimal(19,4) DEFAULT NULL,
CURRENT_AMT decimal(19,4) DEFAULT NULL,
GAIN_LOSS_LT decimal(19,4) DEFAULT NULL,
GAIN_LOSS_ST decimal(19,4) DEFAULT NULL,
UNITS_FOR_DIVID decimal(19,4) DEFAULT NULL,
factor double DEFAULT NULL,
LatestNav double DEFAULT '10',
NavDate date DEFAULT NULL,
IType int(4) DEFAULT NULL,
Rate double DEFAULT NULL,
CurrAmt double DEFAULT NULL,
IndexVal double DEFAULT NULL,
LatestIndexVal double DEFAULT NULL,
Field int(4) DEFAULT NULL,
Client_Code int(4) DEFAULT NULL,
Branch_Code int(4) DEFAULT NULL,
Rm_Code int(4) DEFAULT NULL,
Group_Name varchar(100) DEFAULT NULL,
Type1 varchar(20) DEFAULT NULL,
Type2 varchar(20) DEFAULT NULL,
IsOnline tinyint(3) unsigned DEFAULT NULL,
SFactor double DEFAULT NULL,
OSch_Code int(4) DEFAULT NULL,
PRIMARY KEY (SR_NO),
KEY SCH_Code (SCH_CODE),
KEY OSch_Code (OSch_Code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
注意:有这个索引的原因是我们在 SP 中有很多选择和更新,这将减少 table 扫描。
UPDATE TempPortFolio1
INNER JOIN Clients
ON Clients.ClientId = TempPortFolio1.Client_Code
SET IType = InvCode;
UPDATE TempPortFolio1
INNER JOIN SchDate ON TempPortFolio1.Sch_Code = SchDate.Sch_Code
SET LatestNav = NavRs, NavDate = LDate;
UPDATE TempPortFolio1
SET RATE = 0
WHERE TRAN_TYPE = 'BONUS';
UPDATE TempPortFolio1
SET LatestNav = 10
WHERE LatestNav = 0 OR LatestNav IS NULL;
UPDATE TempPortFolio1
SET NavDate = Tr_date
WHERE NavDate < Tr_date AND Tran_Type <> 'Reinvestment';
UPDATE TempPortFolio1
SET Age = DATEDIFF(NAVDATE, TR_DATE),
CurrAmt = (LatestNav * Units),
PL_Notional = (UNITS * (LatestNav - Rate)),
Divd_Recd = 0;
UPDATE TempPortFolio1 TP INNER JOIN snature_new SM ON SM.CLASSCODE = TP.Type2
SET GAIN_LOSS_ST = (CASE WHEN (Age < 365) THEN PL_Notional ELSE NULL END),
GAIN_LOSS_LT = (CASE WHEN (Age >= 365) THEN PL_Notional ELSE NULL END)
WHERE SM.Indexation = 0;
UPDATE TempPortFolio1 TP INNER JOIN snature_new SM ON SM.CLASSCODE = TP.Type2
SET GAIN_LOSS_ST =
(CASE
WHEN (TIMESTAMPDIFF(MONTH, TR_DATE, NAVDATE) < 36)
THEN
PL_Notional
ELSE
NULL
END),
GAIN_LOSS_LT =
(CASE
WHEN (TIMESTAMPDIFF(MONTH, TR_DATE, NAVDATE) >= 36)
THEN
PL_Notional
ELSE
NULL
END)
WHERE SM.Indexation = 1;
UPDATE TempPortFolio1
SET RET_INDEX_ABS = ((LatestIndexVal - IndexVal) / IndexVal) * 100;
UPDATE TempPortFolio1
SET Ret_Index_CAGR =
CASE
WHEN Age <= 365
THEN
((CONVERT(RET_INDEX_ABS, decimal) / age) * 365)
ELSE
( POWER((((LatestIndexVal)) / (IndexVal)),
(365 / CONVERT(IFNULL(AGE, 1), decimal)))
- 1)
* 100
END
WHERE age <> 0
AND LatestIndexVal <> 0
AND IndexVal <> 0
AND AGE IS NOT NULL;
UPDATE TempPortFolio1
SET ret_abs =
( ((((UNITS * LATESTNAV) + DIVD_RECD)) - (UNITS * RATE))
/ (UNITS * RATE))
* 100
WHERE UNITS <> 0 AND rate <> 0;
UPDATE TempPortFolio1
SET RET_CAGR =
CASE
WHEN Age <= 365
THEN
((ret_abs / age) * 365)
ELSE
( POWER(
((((UNITS * LATESTNAV) + DIVD_RECD)) / (UNITS * RATE)),
(365 / CONVERT(IFNULL(AGE, 1), DECIMAL)))
- 1)
* 100
END
WHERE age <> 0 AND UNITS <> 0 AND rate <> 0 AND AGE IS NOT NULL;
UPDATE TempPortFolio1
SET Age = 0, LatestNav = 10
WHERE Age IS NULL;
UPDATE TempPortFolio1
SET Factor = (UNITS * RATE * AGE);
UPDATE TempPortFolio1
SET SFactor = (UNITS * RATE * IndexVal * AGE);
它们之间有很多更新,但花费的时间更少。
之所以只有两个索引,是因为以上所有查询都更新了整个 table(4000 万条记录)。所以我认为不需要索引。
每次更新大约需要 25 minutes.server 有足够的 Ram 进行所有操作。
我试过临时 table,但性能没有改善,因为整个 table 更新没有分区逻辑会帮助我这样想。?
我在运行这个查询windows 10.有什么办法可以提高UPDATE查询的速度吗?任何与配置相关的更改都会有帮助吗?
请帮忙
-- 编辑
这里是对多个连接的解释table这里是查询更新 2 的解释计划
1 SIMPLE SchDate index PRIMARY,Sch_Code,IDX_1 Sch_Code 4 39064 100 Using index
1 SIMPLE TempPortFolio1 ref SCH_Code SCH_Code 9 SchDate.Sch_Code 1 100 Using index condition.
因为其他更新很简单 table,所以我认为不需要解释。
将更新拆分为块,例如使用 LIMIT 1000(限制适用于 UPDATE 查询)。
例如:
UPDATE TempPortFolio1
SET Age = DATEDIFF(NAVDATE, TR_DATE),
CurrAmt = (LatestNav * Units),
PL_Notional = (UNITS * (LatestNav - Rate)),
Divd_Recd = 0 LIMIT 1000;
使用 PRIMARY KEY
遍历 table。一次检查 1000 行。详细讨论here
UPDATE
必须保存旧行以防崩溃。这就是您的 UPDATE
如此缓慢的原因之一。而且,由于日志的大小,更新超过一定数量的行会变得更慢,因为需要额外的努力来保存它们。
不要使用 OFFSET
和 LIMIT
-- 只会越来越慢。
您的某些 UPDATEs
可能 可以使用索引:
UPDATE TempPortFolio1
SET RATE = 0
WHERE TRAN_TYPE = 'BONUS';
可以使用 INDEX(TRAN_TYPE)
.
但是那些没有 WHERE
子句的将不得不检查所有 40M 行。即使 table 可能 适合 buffer_pool,它仍然需要很长时间。
table 可能比需要的更胖。
decimal(19,4)
占用 9 个字节,允许的值最大为 999999999999999.9999;你真的有那么大的价值观吗?
AGE int(4)
-- 除非你说的是人秒中的 'age',我建议你使用 1 字节的 TINYINT UNSIGNED
而不是 4 字节的 INT SIGNED
. (与此同时,(4)
没有任何意义。)哦,我看到 AGE
可能在 "days" 中,所以可能是 2 字节 SMALLINT UNSIGNED
(范围 0..64K)可能是合适的。
DOUBLE
占用 8 个字节,由于在二进制和十进制之间切换,存在额外舍入的风险。
通常进行大量更新是架构设计不佳的标志,因为这意味着 'value' 不是保存在一个位置,而是保存在数百万个位置。冗余在数据库中是禁忌。
回慢UPDATE
。关注什么:
- 只是查询时间长? (在 elapsed 时间内,分块将花费更长的时间。)
- 它正在阻止其他东西? (分块避免这种情况。)
- 您现在需要更改吗? (太糟糕了。)
- 您需要将所有相关行更改为相同 'instant',即使那一刻距您开始查询只有几分钟? (这就是单曲,慢速
UPDATE
。)
这个答案给想知道的人。
因此,作为数据 truncate/insert 每天和作业 运行 每天。
我们制作了一个 SP,它根据行数(我们计算计数 (*))使用动态范围分区删除并重新创建 table。
我们制作了第二个 SP,它具有所有更新(大约 30 个),这些更新是动态的,并且在执行时必须应用分区
than we Created script file which execute every day and do following task
1 call 1'st SP
2 create number of dynamic(replace event name and partition number ) event (after interval one minute) as number of partition using file .
3 each event will call Second SP with different Partition Paralleled .
这个过程每天重复,只用了(30 分钟)4000 万行来执行所有更新。
我们遇到了单个 table 更新需要很长时间的问题。 table 包含约 3000 万行。
作业每天运行,截断 table 并在 table 中插入来自其他来源的新数据。
这里是 table:
CREATE TABLE tempportfolio1 (
SR_NO int(4) NOT NULL AUTO_INCREMENT,
TR_DATE date DEFAULT NULL,
TRAN_CODE decimal(18,0) DEFAULT NULL,
TRAN_TYPE varchar(20) DEFAULT NULL,
SCH_CODE bigint(8) DEFAULT NULL,
Nature varchar(25) DEFAULT NULL,
UNITS decimal(19,4) DEFAULT NULL,
BAL_UNITS decimal(19,4) DEFAULT NULL,
DIVD_RECD double DEFAULT '0',
FOLIO_NO varchar(50) DEFAULT NULL,
FLAG varchar(5) DEFAULT NULL,
MBALANCE double DEFAULT NULL,
PBALANCE double DEFAULT NULL,
MTotalBalance double DEFAULT NULL,
PL_NOTIONAL decimal(19,4) DEFAULT NULL,
PL_BOOKED decimal(19,4) DEFAULT NULL,
AGE int(4) DEFAULT NULL,
RET_ABS decimal(19,4) DEFAULT NULL,
RET_CAGR decimal(19,4) DEFAULT NULL,
INDEX_AMT decimal(19,4) DEFAULT NULL,
RET_INDEX_ABS decimal(19,4) DEFAULT NULL,
Ret_Index_CAGR decimal(19,4) DEFAULT NULL,
CURRENT_AMT decimal(19,4) DEFAULT NULL,
GAIN_LOSS_LT decimal(19,4) DEFAULT NULL,
GAIN_LOSS_ST decimal(19,4) DEFAULT NULL,
UNITS_FOR_DIVID decimal(19,4) DEFAULT NULL,
factor double DEFAULT NULL,
LatestNav double DEFAULT '10',
NavDate date DEFAULT NULL,
IType int(4) DEFAULT NULL,
Rate double DEFAULT NULL,
CurrAmt double DEFAULT NULL,
IndexVal double DEFAULT NULL,
LatestIndexVal double DEFAULT NULL,
Field int(4) DEFAULT NULL,
Client_Code int(4) DEFAULT NULL,
Branch_Code int(4) DEFAULT NULL,
Rm_Code int(4) DEFAULT NULL,
Group_Name varchar(100) DEFAULT NULL,
Type1 varchar(20) DEFAULT NULL,
Type2 varchar(20) DEFAULT NULL,
IsOnline tinyint(3) unsigned DEFAULT NULL,
SFactor double DEFAULT NULL,
OSch_Code int(4) DEFAULT NULL,
PRIMARY KEY (SR_NO),
KEY SCH_Code (SCH_CODE),
KEY OSch_Code (OSch_Code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
注意:有这个索引的原因是我们在 SP 中有很多选择和更新,这将减少 table 扫描。
UPDATE TempPortFolio1
INNER JOIN Clients
ON Clients.ClientId = TempPortFolio1.Client_Code
SET IType = InvCode;
UPDATE TempPortFolio1
INNER JOIN SchDate ON TempPortFolio1.Sch_Code = SchDate.Sch_Code
SET LatestNav = NavRs, NavDate = LDate;
UPDATE TempPortFolio1
SET RATE = 0
WHERE TRAN_TYPE = 'BONUS';
UPDATE TempPortFolio1
SET LatestNav = 10
WHERE LatestNav = 0 OR LatestNav IS NULL;
UPDATE TempPortFolio1
SET NavDate = Tr_date
WHERE NavDate < Tr_date AND Tran_Type <> 'Reinvestment';
UPDATE TempPortFolio1
SET Age = DATEDIFF(NAVDATE, TR_DATE),
CurrAmt = (LatestNav * Units),
PL_Notional = (UNITS * (LatestNav - Rate)),
Divd_Recd = 0;
UPDATE TempPortFolio1 TP INNER JOIN snature_new SM ON SM.CLASSCODE = TP.Type2
SET GAIN_LOSS_ST = (CASE WHEN (Age < 365) THEN PL_Notional ELSE NULL END),
GAIN_LOSS_LT = (CASE WHEN (Age >= 365) THEN PL_Notional ELSE NULL END)
WHERE SM.Indexation = 0;
UPDATE TempPortFolio1 TP INNER JOIN snature_new SM ON SM.CLASSCODE = TP.Type2
SET GAIN_LOSS_ST =
(CASE
WHEN (TIMESTAMPDIFF(MONTH, TR_DATE, NAVDATE) < 36)
THEN
PL_Notional
ELSE
NULL
END),
GAIN_LOSS_LT =
(CASE
WHEN (TIMESTAMPDIFF(MONTH, TR_DATE, NAVDATE) >= 36)
THEN
PL_Notional
ELSE
NULL
END)
WHERE SM.Indexation = 1;
UPDATE TempPortFolio1
SET RET_INDEX_ABS = ((LatestIndexVal - IndexVal) / IndexVal) * 100;
UPDATE TempPortFolio1
SET Ret_Index_CAGR =
CASE
WHEN Age <= 365
THEN
((CONVERT(RET_INDEX_ABS, decimal) / age) * 365)
ELSE
( POWER((((LatestIndexVal)) / (IndexVal)),
(365 / CONVERT(IFNULL(AGE, 1), decimal)))
- 1)
* 100
END
WHERE age <> 0
AND LatestIndexVal <> 0
AND IndexVal <> 0
AND AGE IS NOT NULL;
UPDATE TempPortFolio1
SET ret_abs =
( ((((UNITS * LATESTNAV) + DIVD_RECD)) - (UNITS * RATE))
/ (UNITS * RATE))
* 100
WHERE UNITS <> 0 AND rate <> 0;
UPDATE TempPortFolio1
SET RET_CAGR =
CASE
WHEN Age <= 365
THEN
((ret_abs / age) * 365)
ELSE
( POWER(
((((UNITS * LATESTNAV) + DIVD_RECD)) / (UNITS * RATE)),
(365 / CONVERT(IFNULL(AGE, 1), DECIMAL)))
- 1)
* 100
END
WHERE age <> 0 AND UNITS <> 0 AND rate <> 0 AND AGE IS NOT NULL;
UPDATE TempPortFolio1
SET Age = 0, LatestNav = 10
WHERE Age IS NULL;
UPDATE TempPortFolio1
SET Factor = (UNITS * RATE * AGE);
UPDATE TempPortFolio1
SET SFactor = (UNITS * RATE * IndexVal * AGE);
它们之间有很多更新,但花费的时间更少。 之所以只有两个索引,是因为以上所有查询都更新了整个 table(4000 万条记录)。所以我认为不需要索引。
每次更新大约需要 25 minutes.server 有足够的 Ram 进行所有操作。 我试过临时 table,但性能没有改善,因为整个 table 更新没有分区逻辑会帮助我这样想。?
我在运行这个查询windows 10.有什么办法可以提高UPDATE查询的速度吗?任何与配置相关的更改都会有帮助吗?
请帮忙
-- 编辑
这里是对多个连接的解释table这里是查询更新 2 的解释计划
1 SIMPLE SchDate index PRIMARY,Sch_Code,IDX_1 Sch_Code 4 39064 100 Using index
1 SIMPLE TempPortFolio1 ref SCH_Code SCH_Code 9 SchDate.Sch_Code 1 100 Using index condition.
因为其他更新很简单 table,所以我认为不需要解释。
将更新拆分为块,例如使用 LIMIT 1000(限制适用于 UPDATE 查询)。
例如:
UPDATE TempPortFolio1
SET Age = DATEDIFF(NAVDATE, TR_DATE),
CurrAmt = (LatestNav * Units),
PL_Notional = (UNITS * (LatestNav - Rate)),
Divd_Recd = 0 LIMIT 1000;
使用 PRIMARY KEY
遍历 table。一次检查 1000 行。详细讨论here
UPDATE
必须保存旧行以防崩溃。这就是您的 UPDATE
如此缓慢的原因之一。而且,由于日志的大小,更新超过一定数量的行会变得更慢,因为需要额外的努力来保存它们。
不要使用 OFFSET
和 LIMIT
-- 只会越来越慢。
您的某些 UPDATEs
可能 可以使用索引:
UPDATE TempPortFolio1
SET RATE = 0
WHERE TRAN_TYPE = 'BONUS';
可以使用 INDEX(TRAN_TYPE)
.
但是那些没有 WHERE
子句的将不得不检查所有 40M 行。即使 table 可能 适合 buffer_pool,它仍然需要很长时间。
table 可能比需要的更胖。
decimal(19,4)
占用 9 个字节,允许的值最大为 999999999999999.9999;你真的有那么大的价值观吗?AGE int(4)
-- 除非你说的是人秒中的 'age',我建议你使用 1 字节的TINYINT UNSIGNED
而不是 4 字节的INT SIGNED
. (与此同时,(4)
没有任何意义。)哦,我看到AGE
可能在 "days" 中,所以可能是 2 字节SMALLINT UNSIGNED
(范围 0..64K)可能是合适的。DOUBLE
占用 8 个字节,由于在二进制和十进制之间切换,存在额外舍入的风险。
通常进行大量更新是架构设计不佳的标志,因为这意味着 'value' 不是保存在一个位置,而是保存在数百万个位置。冗余在数据库中是禁忌。
回慢UPDATE
。关注什么:
- 只是查询时间长? (在 elapsed 时间内,分块将花费更长的时间。)
- 它正在阻止其他东西? (分块避免这种情况。)
- 您现在需要更改吗? (太糟糕了。)
- 您需要将所有相关行更改为相同 'instant',即使那一刻距您开始查询只有几分钟? (这就是单曲,慢速
UPDATE
。)
这个答案给想知道的人。
因此,作为数据 truncate/insert 每天和作业 运行 每天。
我们制作了一个 SP,它根据行数(我们计算计数 (*))使用动态范围分区删除并重新创建 table。
我们制作了第二个 SP,它具有所有更新(大约 30 个),这些更新是动态的,并且在执行时必须应用分区
than we Created script file which execute every day and do following task
1 call 1'st SP
2 create number of dynamic(replace event name and partition number ) event (after interval one minute) as number of partition using file .
3 each event will call Second SP with different Partition Paralleled .
这个过程每天重复,只用了(30 分钟)4000 万行来执行所有更新。