MYSQL在4000万台记录表上的大量更新与128GB ram专用服务器需要很长时间

问题描述 投票:0回答:3

我们在单个表上花费很长时间的更新时遇到问题。该表包含约3000万行。

该作业每天运行,截断表并从该表中的其他其他源插入新数据。

这是表格:

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中有许多选择和更新将减少表扫描。

   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);

这些之间有很多更新,但需要花费更少的时间。原因只有两个索引因为以上所有查询都更新整个表(4000万条记录)。所以我认为没有必要的索引。

每次更新大约需要25分钟。服务器有足够的Ram用于所有操作。我已经尝试过临时表,但性能没有任何改进,因为整个表更新没有分区逻辑会帮助我这么认为。

我在Windows 10上运行此查询。有没有办法提高UPDATE查询的速度?任何配置相关的更改都会有帮助吗?

请帮忙

- 编辑

这里解释一下多个连接表查询这里是更新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.

对于其他更新很简单,一个表,所以我认为不需要解释。

mysql performance optimization query-performance insert-update
3个回答
0
投票

例如,使用LIMIT 1000将更新分割为块(限制适用于UPDATE查询)。

例如:

 UPDATE TempPortFolio1
    SET Age = DATEDIFF(NAVDATE, TR_DATE),
        CurrAmt = (LatestNav * Units),
        PL_Notional = (UNITS * (LatestNav - Rate)),
        Divd_Recd = 0 LIMIT 1000;

0
投票

使用PRIMARY KEY走过桌子。一次检查1000行。详情讨论here

如果发生碰撞,UPDATE必须保存旧行。这就是你的UPDATE如此缓慢的一个原因。而且,由于日志的大小,更新超过一定数量的行会变得更慢,因为它需要额外的努力来保存它们。

不要使用OFFSETLIMIT - 只是越来越慢。

你的一些UPDATEs可能没有索引:

UPDATE TempPortFolio1
SET RATE = 0
WHERE TRAN_TYPE = 'BONUS';

可以使用INDEX(TRAN_TYPE)

但那些没有WHERE子句的人将不得不检查所有40M行。即使表可能适合buffer_pool,它仍然需要很长时间。

该表可能比它需要的更胖。

  • decimal(19,4)需要9个字节,允许值最大为999999999999999.9999;你真的有那么大的价值吗?
  • AGE int(4) - 除非你在人类的几秒钟内谈论'年龄',我建议你使用1字节的TINYINT UNSIGNED而不是4字节的INT SIGNED。 (同时,(4)没有任何意义。)哦,我看到AGE可能在“天”,所以也许2字节的SMALLINT UNSIGNED(范围0..64K)可能是合适的。
  • 由于在二进制和十进制之间切换,DOUBLE需要8个字节并且存在额外舍入的风险。

通常进行大规模更新是架构设计不佳的标志,因为它意味着“价值”不是一个地方,而是数百万个地方。冗余在数据库中是禁忌。

回到慢UPDATE。有什么问题:

  • 只是查询需要很长时间? (Chunking将花费更长的时间。)
  • 它阻止了其他事情? (Chunking避免这种情况。)
  • 你现在需要改变吗? (太糟糕了。)
  • 您需要将所有相关行更改为相同的“即时”,即使该时刻是从您开始查询的几分钟后? (这是你用单一,慢,UPDATE得到的。)

0
投票

这个答案适合谁想知道。

因此,每天数据截断/插入和每天都在运行。

我们制作了一个SP,它根据行数(我们计算count(*))来删除并重新创建具有动态范围分区的表。

我们制作了第二个具有所有更新(大约30个)的SP,这些更新是动态的,在执行时必须应用分区

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万行来执行所有更新。

© www.soinside.com 2019 - 2024. All rights reserved.