13

次のようなテーブルがあります。

CREATE TABLE `Calls` (
  `calendar_id` int(11) NOT NULL,
  `db_date` timestamp NOT NULL,
  `cgn` varchar(32) DEFAULT NULL,
  `cpn` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`calendar_id`),
  KEY `db_date_idx` (`db_date`)
) 
 PARTITION BY RANGE (calendar_id)(
   PARTITION p20091024 VALUES LESS THAN (20091024) ,
   PARTITION p20091025 VALUES LESS THAN (20091025));

どういうわけか、mysqlスケジューラを使用して新しいパーティションを自動的に追加できますか(2日前)-毎日新しいパーティションを追加する例を探しています-次のように実行されます

alter table Calls add partition (partition p20091026 values less than(20091026));

p20091026/20091026 は、スケジュールされたタスクの実行時に構築され、現在 + 2 日から値を導き出します。(または、cron を使用してこれをスクリプト化する方がよいでしょうか?)

4

2 に答える 2

32

はい、できます。

スケジューラはデフォルトではアクティブではないことに注意してください (「イベント スケジューラの設定」を参照)。したがって、リスクのないオプションではありません。たとえば、運用チームがアプリを新しいサーバーに移行したものの、スケジューラを有効にするのを忘れた場合、アプリは失敗します。特別な権限も必要です。これも、新しいサーバーで設定する必要がある場合があります。

私のアドバイス: まず、定期的なパーティション メンテナンスを処理するストアド プロシージャ (以下のコード サンプルを参照) を作成します。しばらく実行しなくても、アプリは停止しません。

次に、そのストアド プロシージャへの呼び出しを重複してスケジュールします。MySQL スケジューラを使用したり、cron ジョブを使用したり、その他の方法を使用したりできます。1 つのスケジューラーが機能していない場合、もう 1 つのスケジューラーがスラックを拾うことができます。sproc を正しく設計すれば、何もする必要がない場合、no-op を実行する方が安上がりです。たとえば、長時間実行レポートを生成するときの最初のステートメントとして、または毎日の ETL プロセス (ある場合) の一部として、アプリから呼び出すこともできます。要点は、スケジュールされたタスクのアキレス腱は、スケジューラが実際に機能していることを確認することです。したがって、ここで冗長性について考えてください。

すべての通話を同時にスケジュールしないように注意してください。:-)

これは、メンテナンス プロシージャがどのようなものになるかを示すコード サンプルです。最初に古いパーティションを削除し、次に新しいパーティションを追加します。エラー チェックと複数の同時実行の防止については、読者の演習として残しました。

DELIMITER $$

DROP PROCEDURE IF EXISTS `test`.`UpdatePartitions` $$
CREATE PROCEDURE `test`.`UpdatePartitions` ()
BEGIN

  DECLARE maxpart_date date;
  DECLARE partition_count int;
  DECLARE minpart date;
  DECLARE droppart_sql date;
  DECLARE newpart_date date;
  DECLARE newpart_sql varchar(500);

  SELECT COUNT(*)
    INTO partition_count
    FROM INFORMATION_SCHEMA.PARTITIONS
    WHERE TABLE_NAME='Calls' AND TABLE_SCHEMA='test';

  -- first, deal with pruning old partitions
  -- TODO: set your desired # of partitions below, or make it parameterizable
  WHILE (partition_count > 1000)
  DO

    -- optionally, do something here to deal with the parition you're dropping, e.g.
    -- copy the data into an archive table

     SELECT MIN(PARTITION_DESCRIPTION)
       INTO minpart
       FROM INFORMATION_SCHEMA.PARTITIONS
       WHERE TABLE_NAME='Calls' AND TABLE_SCHEMA='test';

     SET @sql := CONCAT('ALTER TABLE Calls DROP PARTITION p'
                        , CAST((minpart+0) as char(8))
                        , ';');

     PREPARE stmt FROM @sql;
     EXECUTE stmt;
     DEALLOCATE PREPARE stmt;

    SELECT COUNT(*)
      INTO partition_count
      FROM INFORMATION_SCHEMA.PARTITIONS
      WHERE TABLE_NAME='Calls' AND TABLE_SCHEMA='test';


  END WHILE;

  SELECT MAX(PARTITION_DESCRIPTION)
    INTO maxpart_date
    FROM INFORMATION_SCHEMA.PARTITIONS
    WHERE TABLE_NAME='Calls' AND TABLE_SCHEMA='test';

  -- create enough partitions for at least the next week
  WHILE (maxpart_date < CURDATE() + INTERVAL 7 DAY)
  DO

    SET newpart_date := maxpart_date + INTERVAL 1 DAY;
    SET @sql := CONCAT('ALTER TABLE Calls ADD PARTITION (PARTITION p'
                        , CAST((newpart_date+0) as char(8))
                        , ' values less than('
                        , CAST((newpart_date+0) as char(8))
                        , '));');

    PREPARE stmt FROM @sql;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;

    SELECT MAX(PARTITION_DESCRIPTION)
      INTO maxpart_date
      FROM INFORMATION_SCHEMA.PARTITIONS
      WHERE TABLE_NAME='Calls' AND TABLE_SCHEMA='test';

  END WHILE;

END $$

DELIMITER ;

ところで、パーティションのメンテナンス(新しいパーティションを事前に作成すること、古いパーティションをプルーニングすることなど)は、自動化するために非常に重要です。個人的には、大規模なエンタープライズ データ ウェアハウスが 1 日ダウンするのを見たことがあります。これは、最初に 1 年分のパーティションが作成されたが、翌年になってもさらにパーティションを作成することを覚えている人が誰もいなかったためです。したがって、ここで自動化について考えていることは非常に良いことです。それは、あなたが取り組んでいるプロジェクトにとって良い前兆です。:-)

于 2009-11-28T14:09:51.987 に答える
9

そこのジャスティンからの優れたソリューション。私は彼のコードを現在のプロジェクトの出発点として採用し、それを実装している間に思いついたことをいくつか述べたいと思います。

  1. これを実行するテーブルの既存のパーティション構造には、MAXVALUE タイプのパーティションを含めないでください。すべてのパーティションは、リテラルの日付で区切る必要があります。これは、SELECT MAX(PARTITION_DESCRIPTION) が「MAXVALUE」を返し、次のステップで日付に変換できないためです。プロシージャを呼び出したときに、次のような奇妙なメッセージが表示された場合:「<」の照合順序が不正に混在している場合、これが問題である可能性があります。

  2. INFORMATION_SCHEMA テーブルからパーティション名を選択するときに、「AND TABLE_SCHEMA = 'dbname'」を追加することをお勧めします。同じテーブルに対して同じ名前の複数のパーティションが (異なるデータベース内に) 存在する可能性がありますが、それらはすべてリストされているためです。 INFORMATION_SCHEMA テーブルで一緒に。TABLE_SCHEMA 仕様がなければ、例えば選択します。MAX(PARTITION_DESCRIPTION) は、すべてのデータベース内のその名前のテーブルの既存のすべてのパーティション間の最大パーティション名を提供します。

  3. Justinのソリューションにあるように、ALTER TABLE xxx ADD PARTITIONで問題が発生した途中のどこかで、パーティション名(yyyymmdd)と同じ形式が、yyyy-mm-ddを期待するパーティション区切り文字として使用されていたと思います(v5.6.2)。

  4. デフォルトの動作では、必要に応じて将来のみパーティションを追加します。過去のパーティションを作成する場合は、最初に、必要な最も古いパーティションよりも古い日付のパーティションを設定する必要があります。例えば。過去 30 日間のデータを保持している場合は、まずたとえば 35 日前のパーティションを追加してから、手順を実行します。確かに、これは空のテーブルでしか実行できないかもしれませんが、言及する価値があると思いました。

  5. 4. のように、過去/将来のパーティションの必要な範囲を作成するには、最初に手順を 2 回実行する必要があります。上記の 4. の例では、最初の実行で現在までの -35 日間のパーティションと、必要な将来のパーティションが作成されます。2 回目の実行では、-35 から -30 の間のパーティションがトリミングされます。

これが私が現時点で使用しているものです。呼び出し元の観点から、もう少し柔軟にするために、いくつかのパラメーターを追加しました。データベース、テーブル、現在の日付、および過去と将来の両方で保持するパーティションの数を指定できます。

p20110527 という名前のパーティションが、2011年 5 月 27 日00:00 で終わる日ではなく、その日から始まる日を表すように、パーティションの名前も変更しました。

エラーチェックや同時実行の防止はまだありません:-)

DELIMITER $$

DROP PROCEDURE IF EXISTS UpdatePartitions $$

-- Procedure to delete old partitions and create new ones based on a given date.
-- partitions older than (today_date - days_past) will be dropped
-- enough new partitions will be made to cover until (today_date + days_future)
CREATE PROCEDURE UpdatePartitions (dbname TEXT, tblname TEXT, today_date DATE, days_past INT, days_future INT)
BEGIN

DECLARE maxpart_date date;
DECLARE partition_count int;
DECLARE minpart date;
DECLARE droppart_sql date;
DECLARE newpart_date date;
DECLARE newpart_sql varchar(500); 

SELECT COUNT(*)
INTO partition_count
FROM INFORMATION_SCHEMA.PARTITIONS
WHERE TABLE_NAME=tblname
AND TABLE_SCHEMA=dbname;

-- SELECT partition_count;

-- first, deal with pruning old partitions
WHILE (partition_count > days_past + days_future)
DO
-- optionally, do something here to deal with the parition you're dropping, e.g.
-- copy the data into an archive table

 SELECT STR_TO_DATE(MIN(PARTITION_DESCRIPTION), '''%Y-%m-%d''')
   INTO minpart
   FROM INFORMATION_SCHEMA.PARTITIONS
   WHERE TABLE_NAME=tblname
   AND TABLE_SCHEMA=dbname;

-- SELECT minpart;

 SET @sql := CONCAT('ALTER TABLE '
                    , tblname
                    , ' DROP PARTITION p'
                    , CAST(((minpart - INTERVAL 1 DAY)+0) as char(8))
                    , ';');

 -- SELECT @sql;
 PREPARE stmt FROM @sql;
 EXECUTE stmt;
 DEALLOCATE PREPARE stmt;

SELECT COUNT(*)
  INTO partition_count
  FROM INFORMATION_SCHEMA.PARTITIONS
  WHERE TABLE_NAME=tblname
  AND TABLE_SCHEMA=dbname;

-- SELECT partition_count;

END WHILE;

SELECT STR_TO_DATE(MAX(PARTITION_DESCRIPTION), '''%Y-%m-%d''')
INTO maxpart_date
FROM INFORMATION_SCHEMA.PARTITIONS
WHERE TABLE_NAME=tblname
AND TABLE_SCHEMA=dbname;

-- select maxpart_date;
-- create enough partitions for at least the next days_future days
WHILE (maxpart_date < today_date + INTERVAL days_future DAY)
DO

-- select 'here1';
SET newpart_date := maxpart_date + INTERVAL 1 DAY;
SET @sql := CONCAT('ALTER TABLE '
                    , tblname
                    , ' ADD PARTITION (PARTITION p'
                    , CAST(((newpart_date - INTERVAL 1 DAY)+0) as char(8))
                    , ' VALUES LESS THAN ('''
                    , newpart_date
                    , '''));');

-- SELECT @sql;
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

SELECT STR_TO_DATE(MAX(PARTITION_DESCRIPTION), '''%Y-%m-%d''')
  INTO maxpart_date
  FROM INFORMATION_SCHEMA.PARTITIONS
  WHERE TABLE_NAME=tblname
  AND TABLE_SCHEMA=dbname;

SET maxpart_date := newpart_date;

END WHILE;

END $$

DELIMITER ;
于 2011-05-27T03:31:35.120 に答える