Ad Widget

Collapse

Data Migration from Partitioned to Partitioned MySQ

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • RicOkteo
    Junior Member
    • Mar 2022
    • 17

    #1

    Data Migration from Partitioned to Partitioned MySQ

    Hello,

    I am in the process of migrating from a zabbix 3.0 to a 6.0

    I would like to change the table partitioning method but I don't know how to do it

    Currently, my scores are in pYYYY-MM
    CREATE TABLE `trends` (
    `itemid` bigint(20) unsigned NOT NULL,
    `clock` int(11) NOT NULL DEFAULT 0,
    `num` int(11) NOT NULL DEFAULT 0,
    `value_min` double NOT NULL DEFAULT 0,
    `value_avg` double NOT NULL DEFAULT 0,
    `value_max` double NOT NULL DEFAULT 0,
    PRIMARY KEY (`itemid`,`clock`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
    PARTITION BY RANGE (`clock`)
    (PARTITION `p2021_04` VALUES LESS THAN (1619820000) ENGINE=InnoDB,
    PARTITION `p2021_05` VALUES LESS THAN (1622498400) ENGINE=InnoDB,
    PARTITION `p2021_06` VALUES LESS THAN (1625090400) ENGINE=InnoDB,
    PARTITION `p2021_07` VALUES LESS THAN (1627768800) ENGINE=InnoDB,
    PARTITION `p2021_08` VALUES LESS THAN (1630447200) ENGINE=InnoDB,
    PARTITION `p2021_09` VALUES LESS THAN (1633039200) ENGINE=InnoDB,
    PARTITION `p2021_10` VALUES LESS THAN (1635721200) ENGINE=InnoDB,
    PARTITION `p2021_11` VALUES LESS THAN (1638313200) ENGINE=InnoDB,
    PARTITION `p2021_12` VALUES LESS THAN (1640991600) ENGINE=InnoDB,
    PARTITION `p2022_01` VALUES LESS THAN (1643670000) ENGINE=InnoDB,
    PARTITION `p2022_02` VALUES LESS THAN (1646089200) ENGINE=InnoDB,
    PARTITION `p2022_03` VALUES LESS THAN (1648764000) ENGINE=InnoDB,
    PARTITION `p2022_04` VALUES LESS THAN (1651356000) ENGINE=InnoDB,
    PARTITION `p2022_05` VALUES LESS THAN (1654034400) ENGINE=InnoDB,
    PARTITION `p2022_06` VALUES LESS THAN (1656626400) ENGINE=InnoDB,
    PARTITION `p2022_07` VALUES LESS THAN (1659304800) ENGINE=InnoDB,
    PARTITION `p2022_08` VALUES LESS THAN (1661983200) ENGINE=InnoDB,
    PARTITION `p2022_09` VALUES LESS THAN (1664575200) ENGINE=InnoDB,
    PARTITION `p2022_10` VALUES LESS THAN (1667257200) ENGINE=InnoDB,
    PARTITION `p2022_11` VALUES LESS THAN (1669849200) ENGINE=InnoDB,
    PARTITION `p2022_12` VALUES LESS THAN (1672527600) ENGINE=InnoDB,
    PARTITION `p2023_01` VALUES LESS THAN (1675206000) ENGINE=InnoDB,
    PARTITION `p2023_02` VALUES LESS THAN (1677625200) ENGINE=InnoDB,
    PARTITION `p2023_03` VALUES LESS THAN (1680300000) ENGINE=InnoDB,
    PARTITION `p2023_04` VALUES LESS THAN (1682892000) ENGINE=InnoDB,
    PARTITION `p2023_05` VALUES LESS THAN (1685570400) ENGINE=InnoDB,
    PARTITION `p2023_06` VALUES LESS THAN (1688162400) ENGINE=InnoDB,
    PARTITION `p2023_07` VALUES LESS THAN (1690840800) ENGINE=InnoDB,
    PARTITION `p2023_08` VALUES LESS THAN (1693519200) ENGINE=InnoDB)

    I would like to recover all of my data and switch to partitions like this:
    p202206180000

    More precisely, I would like to derive on this kind of script:


    How can I recover all my values ​​and transfer them to the new tables in the correct format?
  • RicOkteo
    Junior Member
    • Mar 2022
    • 17

    #2
    If it can be useful to you, here is the script that was used so far

    #!/usr/bin/perl

    use strict;
    use Data:umper;
    use DBI;
    use Sys::Syslog qw(:standard :macros);
    use DateTime;
    use POSIX qw(strftime);

    openlog("mysql_zbx_part", "ndelay,pid", LOG_LOCAL0);

    my $db_schema = 'zabbix';
    my $dsn = 'DBI:mysql:'.$db_schema.':mysql_socket=/var/lib/mysql/mysql.sock';
    my $db_user_name = 'zabbix';
    my $db_password = 'password';
    my $tables = { 'history' => { 'period' => 'day', 'keep_history' => '14'},
    'history_log' => { 'period' => 'day', 'keep_history' => '14'},
    'history_str' => { 'period' => 'day', 'keep_history' => '14'},
    'history_text' => { 'period' => 'day', 'keep_history' => '14'},
    'history_uint' => { 'period' => 'day', 'keep_history' => '14'},
    'trends' => { 'period' => 'day', 'keep_history' => '500'},
    'trends_uint' => { 'period' => 'day', 'keep_history' => '500'},

    };
    my $amount_partitions = 15;

    my $curr_tz = 'Europe/Paris';

    my $part_tables;

    my $dbh = DBI->connect($dsn, $db_user_name, $db_password);

    unless ( check_have_partition() ) {
    print "Your installation of MySQL does not support table partitioning.\n";
    syslog(LOG_CRIT, 'Your installation of MySQL does not support table partitioning.');
    exit 1;
    }

    my $sth = $dbh->prepare(qq{SELECT table_name, partition_name, lower(partition_method) as partition_method,
    rtrim(ltrim(partition_expression)) as partition_expression,
    partition_description, table_rows
    FROM information_schema.partitions
    WHERE partition_name IS NOT NULL AND table_schema = ?});
    $sth->execute($db_schema);

    while (my $row = $sth->fetchrow_hashref()) {
    $part_tables->{$row->{'table_name'}}->{$row->{'partition_name'}} = $row;
    }

    $sth->finish();

    foreach my $key (sort keys %{$tables}) {
    unless (defined($part_tables->{$key})) {
    syslog(LOG_ERR, 'Partitioning for "'.$key.'" is not found! The table might be not partitioned.');
    next;
    }

    create_next_partition($key, $part_tables->{$key}, $tables->{$key}->{'period'});
    remove_old_partitions($key, $part_tables->{$key}, $tables->{$key}->{'period'}, $tables->{$key}->{'keep_history'})
    }

    delete_old_data();

    $dbh->disconnect();

    sub check_have_partition {
    my $result = 0;
    # MySQL 5.5
    my $sth = $dbh->prepare(qq{SELECT variable_value FROM information_schema.global_variables WHERE variable_name = 'have_partitioning'});
    # MySQL 5.6
    #my $sth = $dbh->prepare(qq{SELECT plugin_status FROM information_schema.plugins WHERE plugin_name = 'partition'});

    $sth->execute();

    my $row = $sth->fetchrow_array();

    $sth->finish();

    # MySQL 5.5
    return 1 if $row eq 'YES';
    # MySQL 5.6
    [HASHTAG="t4295"]return[/HASHTAG] 1 if $row eq 'ACTIVE';
    }

    sub create_next_partition {
    my $table_name = shift;
    my $table_part = shift;
    my $period = shift;

    for (my $curr_part = 0; $curr_part < $amount_partitions; $curr_part++) {
    my $next_name = name_next_part($tables->{$table_name}->{'period'}, $curr_part);
    my $found = 0;

    foreach my $partition (sort keys %{$table_part}) {
    if ($next_name eq $partition) {
    syslog(LOG_INFO, "Next partition for $table_name table has already been created. It is $next_name");
    $found = 1;
    }
    }

    if ( $found == 0 ) {
    syslog(LOG_INFO, "Creating a partition for $table_name table ($next_name)");
    my $query = 'ALTER TABLE '."$db_schema.$table_name".' ADD PARTITION (PARTITION '.$next_name.
    ' VALUES less than (UNIX_TIMESTAMP("'.date_next_part($tables->{$table_name}->{'period'}, $curr_part).'") div 1))';
    syslog(LOG_DEBUG, $query);
    $dbh->do($query);
    }
    }
    }

    sub remove_old_partitions {
    my $table_name = shift;
    my $table_part = shift;
    my $period = shift;
    my $keep_history = shift;

    my $curr_date = DateTime->now;
    $curr_date->set_time_zone( $curr_tz );

    if ( $period eq 'day' ) {
    $curr_date->add(days => -$keep_history);
    $curr_date->add(hours => -$curr_date->strftime('%H'));
    $curr_date->add(minutes => -$curr_date->strftime('%M'));
    $curr_date->add(seconds => -$curr_date->strftime('%S'));
    }
    elsif ( $period eq 'week' ) {
    }
    elsif ( $period eq 'month' ) {
    $curr_date->add(months => -$keep_history);

    $curr_date->add(days => -$curr_date->strftime('%d')+1);
    $curr_date->add(hours => -$curr_date->strftime('%H'));
    $curr_date->add(minutes => -$curr_date->strftime('%M'));
    $curr_date->add(seconds => -$curr_date->strftime('%S'));
    }

    foreach my $partition (sort keys %{$table_part}) {
    if ($table_part->{$partition}->{'partition_description'} <= $curr_date->epoch) {
    syslog(LOG_INFO, "Removing old $partition partition from $table_name table");

    my $query = "ALTER TABLE $db_schema.$table_name DROP PARTITION $partition";

    syslog(LOG_DEBUG, $query);
    $dbh->do($query);
    }
    }
    }

    sub name_next_part {
    my $period = shift;
    my $curr_part = shift;

    my $name_template;

    my $curr_date = DateTime->now;
    $curr_date->set_time_zone( $curr_tz );

    if ( $period eq 'day' ) {
    my $curr_date = $curr_date->truncate( to => 'day' );
    $curr_date->add(days => 1 + $curr_part);

    $name_template = $curr_date->strftime('p%Y_%m_%d');
    }
    elsif ($period eq 'week') {
    my $curr_date = $curr_date->truncate( to => 'week' );
    $curr_date->add(days => 7 * $curr_part);

    $name_template = $curr_date->strftime('p%Y_%m_w%W');
    }
    elsif ($period eq 'month') {
    my $curr_date = $curr_date->truncate( to => 'month' );
    $curr_date->add(months => 1 + $curr_part);

    $name_template = $curr_date->strftime('p%Y_%m');
    }

    return $name_template;
    }

    sub date_next_part {
    my $period = shift;
    my $curr_part = shift;

    my $period_date;

    my $curr_date = DateTime->now;
    $curr_date->set_time_zone( $curr_tz );

    if ( $period eq 'day' ) {
    my $curr_date = $curr_date->truncate( to => 'day' );
    $curr_date->add(days => 2 + $curr_part);
    $period_date = $curr_date->strftime('%Y-%m-%d');
    }
    elsif ($period eq 'week') {
    my $curr_date = $curr_date->truncate( to => 'week' );
    $curr_date->add(days => 7 * $curr_part + 1);
    $period_date = $curr_date->strftime('%Y-%m-%d');
    }
    elsif ($period eq 'month') {
    my $curr_date = $curr_date->truncate( to => 'month' );
    $curr_date->add(months => 2 + $curr_part);

    $period_date = $curr_date->strftime('%Y-%m-%d');
    }

    return $period_date;
    }

    sub delete_old_data {
    $dbh->do("DELETE FROM sessions WHERE lastaccess < UNIX_TIMESTAMP(NOW() - INTERVAL 1 MONTH)");
    $dbh->do("TRUNCATE housekeeper");
    $dbh->do("DELETE FROM auditlog_details WHERE NOT EXISTS (SELECT NULL FROM auditlog WHERE auditlog.auditid = auditlog_details.auditid)");
    }

    Comment

    • Markku
      Senior Member
      Zabbix Certified SpecialistZabbix Certified ProfessionalZabbix Certified Expert
      • Sep 2018
      • 1781

      #3
      If I had to do table partitioning change, I would do it like this, idea based on https://www.zabbix.com/documentation...ary_keys#mysql:

      - rename old table to *_old
      - create new table with the correct partitioning
      - export the data from the old table to csv file
      - import the csv file data to the new table
      - check that everything works
      - delete the *_old table

      NOW, maybe it is possible to just ALTER TABLE to change the partitioning scheme, without export/import? I guess someone will comment here.

      Note: I'm not a database expert, I just know enough by experience to be able to run the Zabbix installations.

      Markku

      Comment

      • Markku
        Senior Member
        Zabbix Certified SpecialistZabbix Certified ProfessionalZabbix Certified Expert
        • Sep 2018
        • 1781

        #4
        Now that I think about it... Just add whatever new partitioning scheme you want, and adjust the code that adds the new partitions accordingly. Just note that the same code cannot remove the old partitions (unless you code it to understand the old and new partitioning syntax), so you maybe need to drop the old partitions by hand for some time, or build some additional script to remove those.

        So I don't think you need to actually migrate the data at all, just make sure you add new partitions correctly, and eventually drop the old partitions somehow.

        Markku

        Comment

        Working...