2014년 9월 22일 월요일

MySQL status variables for InnoDB online ddl progressing and rowlog buffer usage

MySQL 5.6 and MariaDB 10.0's online DDL does not tell us that how long does it takes to alter table. Also take a look at "Problem of MySQL Online DDL".
Even worse, row log buffer is finite and we have to complete online ddl before row log buffer become full (But MySQL 5.6 and MariaDB 10.0 does not tell me how much buffer is used either).
Sometimes, we have to wait for online ddl statement to be done, but it could be failed because of insuffcient row log buffer. We don't know how many hours we have to wait.

I think row log buffer usage and the progress of inplace alter is good metric to determine to continue altering or not.
So I added 3 global status variables. Actually I wanted to print these metric to client console - like MariaDB's copy style alter progressing. But it's not so easy, becuase copy altering is processed by MySQL handler, but inplace altering is processed by storage engine. So I choose the simple way - just adding status variables. Becuase it's global status variables, it's useless if there's two or more concurrent inplace alter session.


  • Innodb_onlineddl_rowlog_rowsShows how many rows are stored in row log buffer.
  • Innodb_onlineddl_rowlog_pct_usedShows row log buffer usage in percent ( *100%, it's 4-digit. 10000 means 100.00% ). 
  • Innodb_onlineddl_pct_progressShows the progress of inplace alter table. It might be not so accurate becuase inplace alter is highly depend on disk and buffer pool status. But still it is useful and better than nothing.


Innodb_onlineddl_pct_progress is based on estimation, but Innodb_onlineddl_rowlog_rows and Innodb_onlineddl_rowlog_pct_used is accurate value.

The percent reported by Innodb_onlineddl_pct_progress is not so accurate. Becuase inplace alter table is highly depend on InnoDB buffer pool warming-up status and disk throughput and some other things.
This features doesn't take into all these factor to calculate progressing. Now just take into account each index's page count and some hunches.

Online DDL is consist of two big(time consuming) task.

  • 1. Read all rows and store it to buffer from old table
  • 2. Rebuild each index


The first task need only once per ddl statement, but second is needed for each index which need to be rebuilt.
And simply, I assign weight 1.0 to above two main task. And second task, weight 1.0 is splited into fixed weight(0.5) and dynamic weight(0.5).
So all index's weight is 0.5 at minimum. and each index get a weight of dynamic ratio of total dynamic weight of all indexes based on their page count.

And second task is consist of two sub-task.

  • 2-1. Sort & merge buffer
  • 2-2. Insert sort-merged buffer to real index tree


Each sub-task is also highly depend on the disk throughput and buffer pool warming up status. So I assign 40% for first sub-task and 60% for second sub-task my own hunch (It's not based on some math or Big-O things.. ).
I have to assign some weight and percent for each task and sub-task because Online DDL is separated with independent functions.

For example
Let's think about add new column to existing table which have two index including primary key.
And let's assume primary key's total page count is 100 and ix_fd2 index's total page count is 50.

CREATE TABLE tb_test(
  fdpk int,
  fd1  varchar(10),
  fd2  bigint,
  primary key (fdpk),
  index ix_fd2(fd2)
) ENGINE=InnoDB;

ALTER TABLE tb_test ADD fd3 DATETIME, LOCK=NONE, ALGORITHM=INPLACE;

According to above weighting, total weight would be 3 (Task 1 and Task 2 for two indexes) and total dynamic weight would be 1(0.5 for each index).
Primary key will get 1.1667[= 0.5(fixed weight) and 0.6667(dynamic weight, 1.0 * 100/(100+50)) ] and second index(ix_fd2) will get 0.8333[= 0.5(fixed weight) and 0.3333(dynamic weight, 1.0 * 50/(100+50)) ].
So all task's weight would be assigned like below.

[weight:1.0000] 1. Read all rows and store it to buffer from old table
[weight:1.1667] 2. Rebuild primary key
  [weight:40% of 1.1667] 2-1. Sort & merge buffer
  [weight:60% of 1.1667] 2-2. Insert sort-merged buffer to real index tree
[weight:0.8333] 3. Rebuild secondary index (ix_fd2)
  [weight:40% of 0.8333] 3-1. Sort & merge buffer
  [weight:60% of 0.8333] 3-2. Insert sort-merged buffer to real index tree

Finally, we can calculate percent of consuming time for each task and sub-task.

[Time:33.33%] 1. Read all rows and store it to buffer from old table
[Time:38.89%] 2. Rebuild primary key
  [Time:15.56%] 2-1. Sort & merge buffer
  [Time:23.33%] 2-2. Insert sort-merged buffer to real index tree
[Time:27.78%] 3. Rebuild secondary index (ix_fd2)
  [Time:11.11%] 3-1. Sort & merge buffer
  [Time:16.67%] 3-2. Insert sort-merged buffer to real index tree

So, if inplace alter is completed to 2.2 than current progress is 72.22%.

And I also added some message which tell you what task is running and calculated weight for each task and sub-task.

140921 13:58:44 [Warning] Online DDL : Start

140921 13:58:44 [Warning] Online DDL : Start reading clustered index of the table and create temporary files
140921 14:01:08 [Warning] Online DDL : End of reading clustered index of the table and create temporary files

140921 14:01:08 [Warning] Online DDL : Start merge-sorting index PRIMARY (1 / 2), estimated cost : 15.5547%
140921 14:03:24 [Warning] Online DDL : End of merge-sorting index PRIMARY (1/ 2)
140921 14:03:24 [Warning] Online DDL : Start building index PRIMARY (1 / 2), estimated cost : 23.3321%
140921 14:07:21 [Warning] Online DDL : End of building index PRIMARY (1 / 2)
140921 14:07:21 [Warning] Online DDL : Completed

140921 14:07:21 [Warning] Online DDL : Start merge-sorting index ix1 (2 / 2), estimated cost : 11.1119%
140921 14:09:44 [Warning] Online DDL : End of merge-sorting index ix1 (2 / 2)
140921 14:09:44 [Warning] Online DDL : Start building index ix1 (2 / 2), estimated cost : 16.6679%
140921 14:13:12 [Warning] Online DDL : End of building index ix1 (2 / 2)
140921 14:13:12 [Warning] Online DDL : Completed


I ran the some test for checking the accuracy of estimating inplace alter progress.

-- // ---------------------------
-- // total rows : 141,577,818
-- // data size : 13GB
-- // index size : 7GB
-- // ---------------------------
CREATE TABLE tb_onlineddl1 (
  pk1 int(11) NOT NULL,
  pk2 bigint(20) NOT NULL,
  fd1 bigint(20) DEFAULT NULL,
  fd2 bigint(20) DEFAULT NULL,
  fd3 datetime DEFAULT NULL,
  fd4 text,
  fd5 varchar(50) DEFAULT NULL,
  fd6 bigint(20) DEFAULT NULL,
  fd7 bigint(20) DEFAULT NULL,
  PRIMARY KEY (pk1, pk2),
  UNIQUE KEY ux1 (pk2, pk1),
  KEY ix1 (fd6, fd7)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- // ---------------------------
-- // total rows : 141,577,818
-- // data size : 4GB
-- // index size : 2.5GB
-- // ---------------------------
CREATE TABLE tb_onlineddl2 (
  pk1 int(11) NOT NULL,
  pk2 bigint(20) NOT NULL,
  PRIMARY KEY (pk1, pk2),
  KEY ix1 (pk2, pk1)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

ALTER TABLE tb_onlinedd1 ADD x VARCHAR(5), LOCK=NONE, ALGORITHM=INPLACE;
ALTER TABLE tb_onlinedd2 ADD x VARCHAR(5), LOCK=NONE, ALGORITHM=INPLACE;

And I make two chart for the change of "Innodb_onlineddl_pct_progress" status variable during alter.


The first part of the chart, there's some inaccurate angle, but the other part is looks good. ^^

You can found the source cod change from Kakao Github . 

2014년 7월 17일 목요일

Defragment InnoDB table on MariaDB 10.0


We ported defragmentation feature of Facebook MySQL 5.6 to MariaDB 10.0.
(This feature only support XtraDB of MariaDB 10.0, InnoDB not yet support.)

Facebook patch

Timer support

https://github.com/facebook/mysql-5.6/commit/c75a413edeb96eb99bf11d7269bdfea06f96d6b6

Defragmentation feature

https://github.com/facebook/mysql-5.6/commit/a2d3a747426735c3a5f5feca8af8607f8acdc5a6
https://github.com/facebook/mysql-5.6/commit/def96c83fead34107d4122ec77364010d0edfa00
https://github.com/facebook/mysql-5.6/commit/9c67c5db5de056672df27e0cc995af652ad353da
https://github.com/facebook/mysql-5.6/commit/921a81b707673bc5bec324e17eb597b12e2232a6
https://github.com/facebook/mysql-5.6/commit/aa519bd44c16d8081d1ace194b383c1983794dd7
https://github.com/facebook/mysql-5.6/commit/fea7d13ca457ea7bbb38a2b36ae418c57c110139
https://github.com/facebook/mysql-5.6/commit/09b29d305cf380ecb241f163e1fbd68749688572
https://github.com/facebook/mysql-5.6/commit/9284abb38d6f968582dbd83a67d137ce7a7bd9c7
https://github.com/facebook/mysql-5.6/commit/dbd623df3a6b25fd8eb0216d469444d111e1de3c
https://github.com/facebook/mysql-5.6/commit/aed55dc4385d0a7c92eaca4d8db2f12c916dc4ab
https://github.com/facebook/mysql-5.6/commit/aad5c82ff8a5d40c84df5018365230da90e892a8

You can reference more detailed information about this feature from above facebook github sites.

Expectations

1) Increasing page fill factor and scan efficiency
2) Make free pages and recycling it without allocating new spaces

System variables (All system variables are DYNAMIC and GLOBAL)


  • innodb_defragment (ON | OFF), DEFAULT : ONControl whether using innodb defragmentation feature or not.Changing innodb_defragment=OFF will pause any ongoing defragmentation task. And paused defragmentation task will be proceeded when innodb_defragment is set as ON.ALTER TABLE .. DEFRAGMENT .. statement will fail when innodb_defragment=OFF.



  • innodb_defragment_fill_factor (0.7 ~ 1.0), DEFAULT : 0.9
  • innodb_defragment_fill_factor_n_recs (1 ~ 100), DEFAULT : 20Both system variables control how many rows would be stored in one page (It's controlling fill factor of data page).Also controlling how much space will be reserved for future usage.
    reserved_space = min(page_size * (1 - innodb_defragment_fill_factor), average_record_size * innodb_defragment_fill_factor_n_recs);

    In other words, you can control fill factor of pages with both row count and percentage of page size.



  • innodb_defragment_frequency (1 ~ 1000), DEFAULT : 40 (Facebook default is 100, But it's too high on commodity server)Control how fast defragmentation thread processing merge task. Innodb defragment thread will do merge this many times in a second.If innodb_defragment_frequency=100 and innodb_defragment_n_pages=7 then server have to process 700 disk reads (without considering innodb buffer pool).You should increase this system variable(it's dynamic so you can change it whenever you want) upto 1000 when data is stored in fast ssd. 



  • innodb_defragment_n_pages (2 ~ 32), DEFAULT : 7Defragment thread will merge all records from this many contigous pages to target page.



  • innodb_defragment_stats_accuracy (0 ~ ), DEFAULT : 0
    This patch introduce some columns(n_page_split, n_pages_freed, n_leaf_pages_defrag, n_leaf_pages_reserved) on mysql.innodb_index_stats table.
    This informations will be used for defragmentation efficiency and making decision whether defragment is needed or not.
    And this columns' value is not exact value. it's approximate.
    innodb_defragment_stats_accuracy control how often this statistics will be refreshed.



    • n_leaf_pages_defrag
      n_leaf_pages column of mysql.innodb_index_stats table is refreshed when over 10% of rows are changed.
      But n_leaf_pages_defrag is updated when other defragmentation statistics are updated (So it's more accurate than n_leaf_pages column).



    • n_pages_freed
      How many pages are freed by last defragmentation task. It's only updated defragmentation task is done.



    • n_page_split
      The number of page split is stored based on innodb_defragment_stats_accuracy system variable.
      And this value will be reset to 0 after defragmentation.



    • n_leaf_pages_reserved
      This column store how many free pages are there.
      You can make decision whether defragmentation is need or not with this column and n_leaf_pages.




Status variables


  • Innodb_defragment_count
    The number of btr_defragment_n_pages call for defragmentation.


  • Innodb_defragment_failures
    The number of btr_defragment_n_pages could not make free page.


  • Innodb_defragment_compression_failures
    The number of compression fail caused by defragmentation.


Defragmentation efficienty = (Innodb_defragment_count - Innodb_defragment_failures) * 100 / Innodb_defragment_count



Defragmentation syntax

-- // Defragment whole table (basic)
ALTER TABLE tb_t10 DEFRAGMENT;

-- // Defragment single index (Primary key name is 'PRIMARY')
ALTER TABLE tb_t10 DEFRAGMENT INDEX PRIMARY;
ALTER TABLE tb_t10 DEFRAGMENT INDEX ix_secondary_index;

-- // Run defragment as async commit mode (ASYNC_COMMIT)
ALTER TABLE tb_t10 DEFRAGMENT ASYNC_COMMIT;
ALTER TABLE tb_t10 DEFRAGMENT INDEX ix_secondary_index ASYNC_COMMIT;

Defragmentation task does not need any long time exclusive lock. so it could be used on online-serviced MariaDB (if there's free system resource like disk or cpu)


Download

https://github.com/kakao/mariadb-10.0/commit/3c22ca303dbe7168a52c55e0a7362e6a6982c3f4

2014년 4월 22일 화요일

Linux Kernel Parameters and MySQL

Overview

This writing explains relationships between Linux kernel parameter and MySQL server. Especially, It deal with Linux kernel parameter related to frequency of flushing data to disk.

Definition

Kernel parameters related to virtual memory exist in '/proc/sys/vm'.
Explain some of them which are related to frequency of flushing data to disk.

Kernel Parameter
description
Default
dirty_background_ratio
Contains, as a percentage of total available memory that contains free pages and reclaimable pages,
the number of pages at which the background kernel flusher threads will start writing out dirty data.
The total available memory is not equal to total system memory
10(%)
dirty_ratio
Contains, as a percentage of total available memory that contains free pages and reclaimable pages,
the number of pages at which a process which is generating disk writes will itself start writing out dirty data.
The total available memory is not equal to total system memory.
40(%)
dirty_background_bytes
Contains the amount of dirty memory at which the background kernel flusher threads will start writeback.
Note: dirty_background_bytes is the counterpart of dirty_background_ratio.
Only one of them may be specified at a time. When one sysctl is written it is immediately taken into account
to evaluate the dirty memory limits and the other appears as 0 when read.
0
dirty_bytes
Contains the amount of dirty memory at which a process generating disk writes will itself start writeback.
Note: dirty_bytes is the counterpart of dirty_ratio. Only one of them may be specified at a time.
When one sysctl is written it is immediately taken into account to evaluate the dirty memory limits
and the other appears as 0 when read.
Note: the minimum value allowed for dirty_bytes is two pages (in bytes);
any value lower than this limit will be ignored and the old configuration will be retained.
0
dirty_writeback_centisecs
The kernel flusher threads will periodically wake up and write 'old' data out to disk.
This tunable expresses the interval between those wakeups, in 100'ths of a second.
Setting this to zero disables periodic writeback altogether.
500(1/100sec)
dirty_expire_centisecs
This tunable is used to define when dirty data is old enough to be eligible for writeout by the kernel flusher threads.
It is expressed in 100'ths of a second. Data which has been dirty in-memory for longer than this interval will be written out next time a flusher thread wakes up.
3000(1/100sec)

Process

Pdflush daemon wakes up and checks whether dirty page will be written or not every 'dirty_writeback_centisecs'.
Namely, That daemon checks whether a percentage of dirty page in total avaiable memory is more than 'dirty_background_ratio' or not.
If the percentage of dirty page in total avaiable memory is more than 'dirty_background_ratio', Pdflush daemon writes dirty page to disk.


Opinion

If you set a large value into dirty_background_ratio, dirty page can exist more in the filesystem cache.
But if so, Pldflush daemon writes more dirty page into the disk and also Disk I/O would take too long time.
When Disk I/O is performed, Cpu is used. So If Disk I/O would take too long time, Whole system will become slow.
In addition, The data to be changed so there are a lot of cache, The data will be lost after a server failure.
So, I think that reducing the dirty_background_ratio is an efficient.

If you set a large value into dirty_writeback_centisecs, cycle of checking will be longer and there will be a lot of page which is handled at once.
This situation will give a load to server. so Reduce this value until an appropriate value for decreasing page handled at once.

The dirty_expire_centisecs is default 30 seconds. Until this time has passed, pdflush daemon do not actually write on disk. 
Dirty data will be still in filesystem cache for 30 seconds. so This value should be reduced. 
but If you reduce the value too much, that daemon attempts to write too frequently.
so I think It should be reduced suitably.
(However, if you use the the InnoDB storage engine,
Depending on the innodb_flush_method option, It might not seem to be affected by kernel parameters.
In this case, you can adjust innodb_max_dirty_pages_pct value for controlling the frequency of flushing data to disk.)
** innodb_max_dirty_pages_pct : ratio of dirty page to allow in innodb buffer pool


Latest Issue and Kernel Parameters

Lately, we have some issues about Disk I/O.
Some server is under the heavy load due to a sudden increase of Disk I/O.

If most of the Filesystem cache is full of dirty data, cache unmap script set to prevent swap reduces the cache until 1 gigabyte. 
but The all data left in the cache after unmapping is dirty data. so Disk i/o occurs again and It gives a load to the server. 
This is the cause of the issue.

so now, We adjusted kernel parameter of that server as follows.
/proc/sys/vm/dirty_background_ratio : 10 ===> 1
/proc/sys/vm/dirty_expire_centisecs : 3000 ===> 1000
It means that dirty page exist until one percent of total available memory and also data which has been dirty in-memory until 10 seconds.


Details of Mysql_Cache_Unmap

We have mentioned about cache_unmap script with swap in filesystem cache before.
(Reference URL : http://kakaodbe.blogspot.kr/2013/09/mysql-linux-filesystem-cache-2.html)
This chapter deals with how apply the script in server.

we wrote cache_unmap script by referring to yosinori's script. 
( you can see the source code through attached file. ==> mysql_cache_unmap.c )

Periodically for performing cache unmapping, we have been set a crontab as follows.

[ Crontab Setting ]
*/10 * * * * root LD_LIBRARY_PATH=/otp/mysql/lib: /otp/mysql/admin/mysql_cache_unmap --defaults-file=/etc/my.cnf --binary_os_cache_size=1024M > /otp/mysql/admin/mysql_cache_unmap.log2>&1

Options

 --defaults-file   :   you can specify the path to the configuration file of MySQL server
                                through this option. 
  cache_unmap script reads configuration file and
                                obtains file path of binary log and data 
file, innodb redo log.


 --binary_os_cache_size   :   It means the capacity to leave without removing it from the
                                                   Linux OS Cache.
  cache_unmap utility leaves the specified
                                                   capacity in the Linux OS Cache from the binary log that
                                                   occurred recently.


The following is a running log of mysql_cache_unmap script. 

root@host:~ 14:10:09> cat /opt/mysql/admin/mysql_cache_unmap.log
Read configuration
    innodb_data_dir : /opt/mysql/data
    innodb_log_dir : /opt/mysql/data
    binary_log_dir : /opt/mysql/data/mysql-binary
    binary_os_cache_size : 1073741824
    relay_log_dir : /opt/mysql/data/mysql-relay
> unmap_file_all : datafile : /opt/mysql/data/dbname1/tablename1.ibd
> unmap_file_all : datafile : /opt/mysql/data/dbname1/tablename2.ibd
> unmap_file_all : logfile : /opt/mysql/data/ib_logfile0
> unmap_file_all : logfile : /opt/mysql/data/ib_logfile1
> skip unmap file: binary : /opt/mysql/data/mysql-binary.041728 : 0 ~ 60083119
> skip unmap file: binary : /opt/mysql/data/mysql-binary.041727 : 0 ~ 104857974
> skip unmap file: binary : /opt/mysql/data/mysql-binary.041726 : 0 ~ 104857975
> skip unmap file: binary : /opt/mysql/data/mysql-binary.041725 : 0 ~ 104857883
> skip unmap file: binary : /opt/mysql/data/mysql-binary.041724 : 0 ~ 104857852
> skip unmap file: binary : /opt/mysql/data/mysql-binary.041723 : 0 ~ 104857920
> skip unmap file: binary : /opt/mysql/data/mysql-binary.041722 : 0 ~ 104857989
> skip unmap file: binary : /opt/mysql/data/mysql-binary.041721 : 0 ~ 104857781
> skip unmap file: binary : /opt/mysql/data/mysql-binary.041720 : 0 ~ 104857784
> skip unmap file: binary : /opt/mysql/data/mysql-binary.041719 : 0 ~ 104857666
> unmap_file_segment : binary : /opt/mysql/data/mysql-binary.041718 : 0 ~ 34920063 of 104857944
> unmap_file_all : binary : /opt/mysql/data/mysql-binary.041717 : 0 ~ 104857764
> unmap_file_all : binary : /opt/mysql/data/mysql-binary.041716 : 0 ~ 104857852
> unmap_file_all : binary : /opt/mysql/data/mysql-binary.041715 : 0 ~ 104857772
> unmap_file_all : binary : /opt/mysql/data/mysql-binary.041714 : 0 ~ 104857847

As above, mysql_cache_unmap script unmap the caching area of datafile and redo_log_file, binary_log_file from filesystem cache.
CentOs(under 5.x version) consider a Filesystem cache as a top priority. So if you do not have the actual free memory, the memory of the most widely used application would be down to swap.
If MySQL is using the most memory, cached data of MySQL is swapped. So when performing a query in MySQL, MySQL server put up result datas in memory again.
That is why we use mysql_cache_unmap script. we periodically perform this script for decreasing cache size.

Attention 

This script remove all cached contents of redo_log and innodb system data file from filesystem cache.
If innodb_flush_method is "O_DIRECT", innodb data file is performed as direct I/O but innodb redo log is performed as cached I/O. 
so in this case, Avoid unmapping about redo_log file. 
If innodb_flush_method is not "O_DIRECT" or "ALL_O_DIRECT", Cache unmapping about redo log and Innodb data file is not recommended. 
You can apply that by changing the mysql_cache_unmap program.


mysql_cache_unmap script unmap the area of caching by using 'posix_fadvise' method.

[ posix_fadvise SYSNOPSIS ]

#define _XOPEN_SOURCE 600
#include <fcntl.h>
int posix_fadvise(int fd, off_t offset, off_t len, int advice);

The advice applies to a (not necessarily existent) region starting at offset and extending for len bytes (or until the end of the file if len is 0) within the file referred to by fd.
The advice is not binding; it merely constitutes an expectation on behalf of the application. 
On success, zero is returned. On error, an error number is returned.

The following is an excerpt from the mysql_cache_unmap source code. Substantially cache unmapping is done In this part.

#define _XOPEN_SOURCE 600
#include <fcntl.h>
int unmap_file_segment(const char *fpath, size_t start, size_t len){
  int fd = open(fpath, O_RDONLY);
  if (fd < 0){
    fprintf(stderr, "ERROR : Failed to open %s\n", fpath);
    return 1;
  }
  int r = posix_fadvise(fd, start, len, POSIX_FADV_DONTNEED);
  if (r != 0){
    fprintf(stderr, "ERROR : posix_fadvice failed for %s\n", fpath);
  }
  close(fd);
  /* if posix_fadvise is succeeded, then sleep 25 milli seconds */
  usleep(25 1000);
  return 0;
}

You can see the value of the 'advice' parameter in this code is "POSIX_FADV_DONTNEED".
There are many kinds of advice. POSIX_FADV_DONTNEED is one of them. 
POSIX_FADV_DONTNEED attempts to free cached pages associated with the specified region.

2014년 4월 21일 월요일

Adding pt-online-schema-change new parameters

Adding new parameter for pt-online-schema-change 


Percona Toolkit has a lot of excellent features(module), Among that pt-online-schema-change is really really useful feature as almost DBAs already experienced.
But they can't cover every requests of the world even though that is the best tool and pt-online-schema-change also.
So we modified a little bit and let pt-online-schema-change adapt for our requirements.

  1) For changing both of PRIMARY KEY change and Partitioning table.
  2) For changing columns' default value and update new default value to columns.
  3) For stable schema change, Adding sleep time between copy of chunks.

We added 4 new parameters to pt-online-schema-change for above features.

  • --prompt-before-copy
    Same as "--ask-pass", this parameter doesn't need any value. if --prompt-before-copy specified, pt-online-schema-change will prompt (wait) user input after creating new table. If you want to check new table schema or have more chanage requirements on new table, you can use this parameter. Especially you may want to use it when change both of PRIMARY KEY and Table partitioning. But you can't change both change by one ALTER statement and also pt-online-schema-change. But you can do both task with --prompt-before-copy of patched pt-online-schema-change. 
  • --skip-copy-columns
    pt-online-schema-change will copy all common columns on both of old and new table. So direct "ALTER TABLE .. DROP fd1, ADD fd1 NOT NULL DEFAULT 'N'" ddl statement and pt-online-schema-change's result would be different. Sometimes we need to drop all column value but column name, pt-online-schema-change is useless at this time. But you can do this with --skip-copy-columns option of patched pt-online-schema-change.
  • --sleep-time-us
    pt-online-schema-change will control speed of row copy based on MySQL server's load(especially mysql status variables). But your query is very lightweight and fast, then pt-online-schema-chnage's rule would not be sufficient. So we made pt-online-schema-change doing sleep between each chunk of copy task by --sleep-time-us parameter. --sleep-time-us use micro seconds unit(1/1,000,000 second). And we usally set 10000(10 milli second) ~ 50000(50 milli second) as --sleep-time-us parameter.
  • --print-sql
    Sometimes we need to check the create table and create trigger DDL ran by pt-onine-schema-change. pt-online-schema-change has debugging mode already, but it may be so verbose to you. We added --print-sql parameter to make pt-online-schema-change print out only CREATE TABLE and CREATE TRIGGER DDL.


Applied parameters would be printed out like below when you run patched pt-online-schema-change.
-- Additional parameters ----------------------------------
  >> skip columns : Not specified
  >> sleep time (us) : 50000
  >> prompting user operation : Yes
-----------------------------------------------------------

So, let's see the simple examples for 1~3 scenarios.


1) For changing both of PRIMARY KEY change and Partitioning table.

Let's say we want to change below normal table to partitioned table based on DATETIME type column (fd2).

CREATE TABLE test.test_partition (
  id INT AUTO_INCREMENT,
  fd1 VARCHAR(10),
  fd2 DATETIME,
  PRIMARY KEY(id)
) ENGINE=InnoDB;

For partitioned table, First we have to add fd2 column as part of PRIMARY KEY.
So we need to below two ALTER statement.

ALTER TABLE test.test_partition DROP PRIMARY KEY, ADD PRIMARY KEY(id, fd2);
ALTER TABLE test.test_partition PARTITION ...

But above two ALTER statement can't be combined as one statement, and also pt-online-schema-change too.
In this case, we could use --prompt-before-copy parameter on patched pt-online-schema-change.

/usr/bin/pt-online-schema-change --alter "DROP PRIMARY KEY, ADD PRIMARY KEY(id, fd2)" D=test,t=test_partition \
--no-drop-old-table \
--no-drop-new-table \
--chunk-size=500 \
--chunk-size-limit=600 \
--defaults-file=/etc/my.cnf \
--host=127.0.0.1 \
--port=3306 \
--user=root \
--ask-pass \
--progress=time,30 \
--max-load="Threads_running=100" \
--critical-load="Threads_running=1000" \
--chunk-index=PRIMARY \
--charset=UTF8MB4 \
--no-check-alter \
--sleep-time-us=50000 \
--prompt-before-copy \
--print-sql \
--execute

Like below, patched pt-online-schema-change will print out some informational messages and prompt(wait) for user confirmation, because --prompt-before-copy is specified.
...
-- Additional parameters ----------------------------------
  >> skip columns : Not specified
  >> sleep time (us) : 50000
  >> prompting user operation : Yes
-----------------------------------------------------------
...
-- Create Triggers ---------------------------------------
CREATE TRIGGER `pt_osc_test_test_partition_ins` AFTER INSERT ON `test`.`test_partition` FOR EACH ROW REPLACE INTO `test`.`_test_partition_new` ...
CREATE TRIGGER `pt_osc_test_test_partition_upd` AFTER UPDATE ON `test`.`test_partition` FOR EACH ROW REPLACE INTO `test`.`_test_partition_new` ...
CREATE TRIGGER `pt_osc_test_test_partition_del` AFTER DELETE ON `test`.`test_partition` FOR EACH ROW DELETE IGNORE FROM `test`.`_test_partition_new` WHERE `test`.`_test_partition_new`.`id` <=> OLD.`id`;
----------------------------------------------------------

Table copy operation is paused temporarily by user request '--prompt-before-copy'.
pt-online-schema-change utility created new table, but not triggers.
   ==> new table name : `test`.`_test_partition_new`

So if you have any custom operation on new table, do it now.
Type 'yes', when you ready to go.
Should I continue to copy [Yes] ? : <== pt-online-schema-change will wait user input after creating new table (_test_partition_new)


At this time, we can run ALTER TABLE PARTITION .. statement on another terminal. After that type "yes" on pt-online-schema-change terminal.

ALTER TABLE _test_partition_new
PARTITION BY RANGE COLUMNS(CRT_DT)
(
 ...
 PARTITION PF_20140420 VALUES LESS THAN ('2014-04-21 00:00:00') ENGINE = InnoDB,
 PARTITION PF_20140421 VALUES LESS THAN ('2014-04-22 00:00:00') ENGINE = InnoDB,
 PARTITION PF_20140422 VALUES LESS THAN ('2014-04-23 00:00:00') ENGINE = InnoDB,
 PARTITION PF_20140423 VALUES LESS THAN ('2014-04-24 00:00:00') ENGINE = InnoDB
);

All after pt-online-schema-change task would be same as original pt-online-schema-change.
Finally we can get the table applied parimary key change and partitioning with just one time of pt-online-schema-change.

Warning: pt-online-schema-change will print warning message out and just terminated when you change primary key spec. So you may want to use --no-check-alter option for change primary key. Of couse you should be careful when you change PRIMARY KEY.




2) For changing columns' default value and update new default value to columns.

Let's say we want to change default value as 'N' of fd1 column and set new defalut value ('N') to fd1 column of existing rows.
In this case pt-online-schema-change can change table schema as DEFAULT 'N', but not existing rows' column value. Because pt-online-schema-change will copy all common columns' value of new and old table.

CREATE TABLE test.test_defaultvalue (
  id INT AUTO_INCREMENT,
  fd1 CHAR(1) DEFAULT 'Y',
  fd2 DATETIME,
  PRIMARY KEY(id)
) ENGINE=InnoDB;

ALTER TABLE test.test_defaultvalue MODIFY fd1 CHAR(1) DEFAULT 'N';

UPDATE test.test_defaultvalue SET fd2='N' WHERE fd2='Y';

So original pt-online-schema-change, we have to update all row's fd2 column value as 'N', and this update will hinder service queries' concurrency (this will lock all rows of table when there's no proper index).

On patched pt-online-schema-change, we can use --skip-copy-columns parameter to prevent pt-online-schema-change from copying some common column.
Like below example, If "--skip-copy-columns='fd1'" is specified, patched pt-online-schema-change just ignore fd1 column not to copy to new table. So new table's all row will get a default value 'N'.

/usr/bin/pt-online-schema-change --alter "MODIFY fd1 CHAR(1) DEFAULT 'N'" D=test,t=test_defaultvalue \
--no-drop-old-table \
--no-drop-new-table \
--chunk-size=500 \
--chunk-size-limit=600 \
--defaults-file=/etc/my.cnf \
--host=127.0.0.1 \
--port=3306 \
--user=root \
--ask-pass \
--progress=time,30 \
--max-load="Threads_running=100" \
--critical-load="Threads_running=1000" \
--chunk-index=PRIMARY \
--charset=UTF8MB4 \
--sleep-time-us=50000 \
--skip-copy-columns='fd1' \
--prompt-before-copy \
--print-sql \
--execute

And you may use --prompt-before-copy and --print-sql options to check TRIGGER and INSERT .. SELECT .. query ran by pt-online-schema-change.



3) For stable schema change, Adding sleep time between copy of chunks.

pt-online-schema-change already has a feature to control the copy speed based on mysql server's status variables. But as I mentioned before, MySQL server's workload is CPU bound and query is sample and fast, pt-online-schema-change's feature might not be sufficient.
So on patched pt-online-schema-change, we added --sleep-time-us parameter. If you specify --sleep-time-us parameter with proper integer value, patched pt-online-schema-change will sleep specified micro seconds after each chunk.

--sleep-time-us parameter will make slower online schema change job, but you can change table schema more stable manner.



Download

https://github.com/kakao/percona-toolkit