Skip to content

Commit 061d482

Browse files
author
Priyanka Sangam
committed
Bug #24754897 RPL_INJECTOR API FUNCTION FOR UPDATE_ROW WITH BEFORE AND AFTER COLUMN BITMAPS
An UPDATE_ROW binlog event contains 2 versions of the updated row: a Before Image which has the row before the update, and an After Image of the row after the update. The Before and After Image do not always include all the columns in the row. The binlog format decides which columns will be included. There is a requirement in ndbcluster to reduce the size of the binlog event while retaining the UPDATE_ROW format of BeforeImage/AfterImage. This can be done by writing only primary key columns from the Before Image and only modified columns from the After Image. However, the existing binlog formats do not support different sets of columns in the Before and After Images. The --binlog-format=MINIMAL option is not usable for ndbcluster because enabling the binlog format code breaks the --ndb-log-updated-only option in ndb replication. This patch implements 2 new ndb binlog formats for UPDATE_ROW where the Before Image and After Image are minimised to remove duplicate information. UPDATED_ONLY_USE_UPDATE_MINIMAL: log update as UPDATE_ROW, log only primary key columns in Before Image and only updated columns in After Image FULL_USE_UPDATE_MINIMAL: log update as UPDATE_ROW, log only primary key columns in Before Image and all non primary key columns in After Image This means that each column is contained only once in each event, bringing efficiency close to that achieved by the variants logging with WRITE_ROW events. The UPDATED_ONLY_USE_UPDATE_MINIMAL and FULL_USE_UPDATE_MINIMAL formats can be used with the conflict resolution algorithms NDB$EPOCH(), NDB$EPOCH2() and NDB$EPOCH_TRANS(), because these do not require the 'Before' values of non-primary-key-columns. Conflict resolution algorithms like NDB$MAX() and NDB$OLD() require the old values of non-PK columns, and will not work correctly with these new variants.
1 parent fc104de commit 061d482

File tree

7 files changed

+173
-32
lines changed

7 files changed

+173
-32
lines changed

sql/ha_ndbcluster.cc

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2004, 2016, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved.
22

33
This program is free software; you can redistribute it and/or modify
44
it under the terms of the GNU General Public License as published by
@@ -19038,13 +19038,27 @@ static MYSQL_SYSVAR_BOOL(
1903819038
opt_ndb_log_update_as_write, /* var */
1903919039
PLUGIN_VAR_OPCMDARG,
1904019040
"For efficiency log only after image as a write event. "
19041-
"Ignore before image. This may cause compatability problems if "
19041+
"Ignore before image. This may cause compatibility problems if "
1904219042
"replicating to other storage engines than ndbcluster.",
1904319043
NULL, /* check func. */
1904419044
NULL, /* update func. */
1904519045
1 /* default */
1904619046
);
1904719047

19048+
my_bool opt_ndb_log_update_minimal;
19049+
static MYSQL_SYSVAR_BOOL(
19050+
log_update_minimal, /* name */
19051+
opt_ndb_log_update_minimal, /* var */
19052+
PLUGIN_VAR_OPCMDARG,
19053+
"For efficiency, log updates in a minimal format"
19054+
"Log only the primary key value(s) in the before "
19055+
"image. Log only the changed columns in the after "
19056+
"image. This may cause compatibility problems if "
19057+
"replicating to other storage engines than ndbcluster.",
19058+
NULL, /* check func. */
19059+
NULL, /* update func. */
19060+
0 /* default */
19061+
);
1904819062

1904919063
my_bool opt_ndb_log_updated_only;
1905019064
static MYSQL_SYSVAR_BOOL(
@@ -19053,7 +19067,7 @@ static MYSQL_SYSVAR_BOOL(
1905319067
PLUGIN_VAR_OPCMDARG,
1905419068
"For efficiency log only updated columns. Columns are considered "
1905519069
"as \"updated\" even if they are updated with the same value. "
19056-
"This may cause compatability problems if "
19070+
"This may cause compatibility problems if "
1905719071
"replicating to other storage engines than ndbcluster.",
1905819072
NULL, /* check func. */
1905919073
NULL, /* update func. */
@@ -19375,6 +19389,7 @@ static struct st_mysql_sys_var* system_variables[]= {
1937519389
MYSQL_SYSVAR(eventbuffer_free_percent),
1937619390
MYSQL_SYSVAR(log_update_as_write),
1937719391
MYSQL_SYSVAR(log_updated_only),
19392+
MYSQL_SYSVAR(log_update_minimal),
1937819393
MYSQL_SYSVAR(log_empty_update),
1937919394
MYSQL_SYSVAR(log_orig),
1938019395
MYSQL_SYSVAR(distribution),

sql/ha_ndbcluster_binlog.cc

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
2+
Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
33
44
This program is free software; you can redistribute it and/or modify
55
it under the terms of the GNU General Public License as published by
@@ -44,6 +44,7 @@ extern my_bool opt_ndb_log_orig;
4444
extern my_bool opt_ndb_log_bin;
4545
extern my_bool opt_ndb_log_update_as_write;
4646
extern my_bool opt_ndb_log_updated_only;
47+
extern my_bool opt_ndb_log_update_minimal;
4748
extern my_bool opt_ndb_log_binlog_index;
4849
extern my_bool opt_ndb_log_apply_status;
4950
extern ulong opt_ndb_extra_logging;
@@ -356,6 +357,8 @@ ndb_binlog_open_shadow_table(THD *thd, NDB_SHARE *share)
356357
if (shadow_table->s->blob_fields != 0)
357358
share->flags|= NSF_BLOB_FLAG;
358359

360+
event_data->init_pk_bitmap();
361+
359362
#ifndef DBUG_OFF
360363
dbug_print_table("table", shadow_table);
361364
#endif
@@ -4283,6 +4286,10 @@ set_binlog_flags(NDB_SHARE *share,
42834286
{
42844287
set_binlog_use_update(share);
42854288
}
4289+
if (opt_ndb_log_update_minimal)
4290+
{
4291+
set_binlog_update_minimal(share);
4292+
}
42864293
break;
42874294
case NBT_UPDATED_ONLY:
42884295
DBUG_PRINT("info", ("NBT_UPDATED_ONLY"));
@@ -4306,6 +4313,20 @@ set_binlog_flags(NDB_SHARE *share,
43064313
set_binlog_full(share);
43074314
set_binlog_use_update(share);
43084315
break;
4316+
case NBT_UPDATED_ONLY_MINIMAL:
4317+
DBUG_PRINT("info", ("NBT_UPDATED_ONLY_MINIMAL"));
4318+
set_binlog_updated_only(share);
4319+
set_binlog_use_update(share);
4320+
set_binlog_update_minimal(share);
4321+
break;
4322+
case NBT_UPDATED_FULL_MINIMAL:
4323+
DBUG_PRINT("info", ("NBT_UPDATED_FULL_MINIMAL"));
4324+
set_binlog_full(share);
4325+
set_binlog_use_update(share);
4326+
set_binlog_update_minimal(share);
4327+
break;
4328+
default:
4329+
DBUG_VOID_RETURN;
43094330
}
43104331
set_binlog_logging(share);
43114332
DBUG_VOID_RETURN;
@@ -5806,10 +5827,10 @@ handle_data_event(THD* thd, Ndb *ndb, NdbEventOperation *pOp,
58065827
DBUG_PRINT("info", ("Assuming %u columns for table %s",
58075828
n_fields, table->s->table_name.str));
58085829
MY_BITMAP b;
5809-
/* Potential buffer for the bitmap */
5810-
uint32 bitbuf[128 / (sizeof(uint32) * 8)];
5811-
const bool own_buffer = n_fields <= sizeof(bitbuf) * 8;
5812-
bitmap_init(&b, own_buffer ? bitbuf : NULL, n_fields, FALSE);
5830+
my_bitmap_map bitbuf[(NDB_MAX_ATTRIBUTES_IN_TABLE +
5831+
8*sizeof(my_bitmap_map) - 1) /
5832+
(8*sizeof(my_bitmap_map))];
5833+
bitmap_init(&b, bitbuf, n_fields, FALSE);
58135834
bitmap_set_all(&b);
58145835

58155836
/*
@@ -5954,9 +5975,24 @@ handle_data_event(THD* thd, Ndb *ndb, NdbEventOperation *pOp,
59545975
}
59555976
ndb_unpack_record(table, event_data->ndb_value[1], &b, table->record[1]);
59565977
DBUG_EXECUTE("info", print_records(table, table->record[1]););
5978+
5979+
MY_BITMAP col_bitmap_before_update;
5980+
my_bitmap_map bitbuf[(NDB_MAX_ATTRIBUTES_IN_TABLE +
5981+
8*sizeof(my_bitmap_map) - 1) /
5982+
(8*sizeof(my_bitmap_map))];
5983+
bitmap_init(&col_bitmap_before_update, bitbuf, n_fields, FALSE);
5984+
if (get_binlog_update_minimal(share))
5985+
{
5986+
event_data->generate_minimal_bitmap(&col_bitmap_before_update, &b);
5987+
}
5988+
else
5989+
{
5990+
bitmap_copy(&col_bitmap_before_update, &b);
5991+
}
5992+
59575993
ret = trans.update_row(logged_server_id,
59585994
injector::transaction::table(table, true),
5959-
&b, n_fields,
5995+
&col_bitmap_before_update, &b, n_fields,
59605996
table->record[1], // before values
59615997
table->record[0], // after values
59625998
extra_row_info_ptr);
@@ -5976,11 +6012,6 @@ handle_data_event(THD* thd, Ndb *ndb, NdbEventOperation *pOp,
59766012
my_free(blobs_buffer[1], MYF(MY_ALLOW_ZERO_PTR));
59776013
}
59786014

5979-
if (!own_buffer)
5980-
{
5981-
bitmap_free(&b);
5982-
}
5983-
59846015
return 0;
59856016
}
59866017

sql/ndb_event_data.cc

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
2+
Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
33
44
This program is free software; you can redistribute it and/or modify
55
it under the terms of the GNU General Public License as published by
@@ -22,7 +22,8 @@
2222

2323
Ndb_event_data::Ndb_event_data(NDB_SHARE *the_share) :
2424
shadow_table(NULL),
25-
share(the_share)
25+
share(the_share),
26+
pk_bitmap(NULL)
2627
{
2728
ndb_value[0]= NULL;
2829
ndb_value[1]= NULL;
@@ -34,6 +35,10 @@ Ndb_event_data::~Ndb_event_data()
3435
if (shadow_table)
3536
closefrm(shadow_table, 1);
3637
shadow_table= NULL;
38+
39+
delete pk_bitmap;
40+
pk_bitmap = NULL;
41+
3742
free_root(&mem_root, MYF(0));
3843
share= NULL;
3944
/*
@@ -84,3 +89,66 @@ void Ndb_event_data::print(const char* where, FILE* file) const
8489
fprintf(file, " - mem_root used: %lu\n", mem_root_used);
8590
}
8691
}
92+
93+
/*
94+
* While writing an UPDATE_ROW event to the binlog, a bitmap is
95+
* used to indicate which columns should be written. An
96+
* UPDATE_ROW event contains 2 versions of the row: a Before Image
97+
* of the row before the update was done, and an After Image of
98+
* the row after the update. Column bitmaps are used to decide
99+
* which columns will be written to both images. The Before
100+
* Image and After Image can contain different columns.
101+
*
102+
* For the binlog formats UPDATED_ONLY_USE_UPDATE_MINIMAL and
103+
* FULL_USE_UPDATE_MINIMAL, it is necessary to write only primary
104+
* key columns to the Before Image, and to remove all primary key
105+
* columns from the After Image. A bitmap of primary key columns is
106+
* created for this purpose.
107+
*/
108+
void Ndb_event_data::init_pk_bitmap()
109+
{
110+
if (shadow_table->s->primary_key == MAX_KEY)
111+
{
112+
// Table without pk, no need for pk_bitmap since minimal is full
113+
return;
114+
}
115+
pk_bitmap = new MY_BITMAP();
116+
bitmap_init(pk_bitmap, pk_bitbuf, shadow_table->s->fields, FALSE);
117+
KEY* key = shadow_table->key_info + shadow_table->s->primary_key;
118+
KEY_PART_INFO* key_part_info = key->key_part;
119+
const uint key_parts = key->user_defined_key_parts;
120+
for (uint i = 0; i < key_parts; i++, key_part_info++)
121+
{
122+
bitmap_set_bit(pk_bitmap, key_part_info->fieldnr - 1);
123+
}
124+
assert(!bitmap_is_clear_all(pk_bitmap));
125+
}
126+
127+
/*
128+
* Modify the column bitmaps generated for UPDATE_ROW as per
129+
* the MINIMAL binlog format type. Expected arguments:
130+
*
131+
* @before: empty bitmap to be populated with PK columns
132+
* @after: bitmap with updated cols, if ndb_log_updated_only=TRUE
133+
* bitmap with all cols, if ndb_log_updated_only=FALSE
134+
*
135+
* If no PK is defined, bitmaps revert to default behaviour:
136+
* - before and after bitmaps are identical
137+
* - bitmaps contain all/updated cols as per ndb_log_updated_only
138+
*/
139+
void Ndb_event_data::generate_minimal_bitmap(MY_BITMAP *before, MY_BITMAP *after)
140+
{
141+
if (pk_bitmap)
142+
{
143+
assert(!bitmap_is_clear_all(pk_bitmap));
144+
// set Before Image to contain only primary keys
145+
bitmap_copy(before, pk_bitmap);
146+
// remove primary keys from After Image
147+
bitmap_subtract(after, pk_bitmap);
148+
}
149+
else
150+
{
151+
// no usable PK bitmap, set Before Image = After Image
152+
bitmap_copy(before, after);
153+
}
154+
}

sql/ndb_event_data.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
2+
Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
33
44
This program is free software; you can redistribute it and/or modify
55
it under the terms of the GNU General Public License as published by
@@ -20,6 +20,9 @@
2020

2121
#include <my_global.h> // my_alloc.h
2222
#include <my_alloc.h> // MEM_ROOT
23+
#include <my_bitmap.h>
24+
25+
#include <ndbapi/ndbapi_limits.h>
2326

2427
class Ndb_event_data
2528
{
@@ -34,8 +37,15 @@ class Ndb_event_data
3437
struct TABLE *shadow_table;
3538
struct NDB_SHARE *share;
3639
union NdbValue *ndb_value[2];
40+
/* Bitmap with bit set for all primary key columns. */
41+
MY_BITMAP *pk_bitmap;
42+
my_bitmap_map pk_bitbuf[(NDB_MAX_ATTRIBUTES_IN_TABLE +
43+
8*sizeof(my_bitmap_map) - 1) /
44+
(8*sizeof(my_bitmap_map))];
3745

3846
void print(const char* where, FILE* file) const;
47+
void init_pk_bitmap();
48+
void generate_minimal_bitmap(MY_BITMAP *before, MY_BITMAP *after);
3949
};
4050

4151
#endif

sql/ndb_share.h

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
2+
Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
33
44
This program is free software; you can redistribute it and/or modify
55
it under the terms of the GNU General Public License as published by
@@ -38,9 +38,11 @@ enum Ndb_binlog_type
3838
,NBT_NO_LOGGING = 1
3939
,NBT_UPDATED_ONLY = 2
4040
,NBT_FULL = 3
41-
,NBT_USE_UPDATE = 4 /* bit 0x4 indicates USE_UPDATE */
42-
,NBT_UPDATED_ONLY_USE_UPDATE = NBT_UPDATED_ONLY | NBT_USE_UPDATE
43-
,NBT_FULL_USE_UPDATE = NBT_FULL | NBT_USE_UPDATE
41+
,NBT_USE_UPDATE = 4
42+
,NBT_UPDATED_ONLY_USE_UPDATE = 6
43+
,NBT_FULL_USE_UPDATE = 7
44+
,NBT_UPDATED_ONLY_MINIMAL = 8
45+
,NBT_UPDATED_FULL_MINIMAL = 9
4446
};
4547
#endif
4648

@@ -138,6 +140,8 @@ set_ndb_share_state(NDB_SHARE *share, NDB_SHARE_STATE state)
138140
#define NSF_BINLOG_FULL 8u /* table should be binlogged with full rows */
139141
#define NSF_BINLOG_USE_UPDATE 16u /* table update should be binlogged using
140142
update log event */
143+
#define NSF_BINLOG_MINIMAL_UPDATE 32u /* table update should be binlogged using
144+
minimal format: before(PK):after(changed cols) */
141145
inline void set_binlog_logging(NDB_SHARE *share)
142146
{
143147
DBUG_PRINT("info", ("set_binlog_logging"));
@@ -175,6 +179,16 @@ inline void set_binlog_use_update(NDB_SHARE *share)
175179
inline my_bool get_binlog_use_update(NDB_SHARE *share)
176180
{ return (share->flags & NSF_BINLOG_USE_UPDATE) != 0; }
177181

182+
static inline void set_binlog_update_minimal(NDB_SHARE *share)
183+
{
184+
DBUG_PRINT("info", ("set_binlog_update_minimal"));
185+
share->flags|= NSF_BINLOG_MINIMAL_UPDATE;
186+
}
187+
188+
static inline bool get_binlog_update_minimal(const NDB_SHARE *share)
189+
{
190+
return (share->flags & NSF_BINLOG_MINIMAL_UPDATE) != 0;
191+
}
178192

179193
NDB_SHARE *ndbcluster_get_share(const char *key,
180194
struct TABLE *table,

sql/rpl_injector.cc

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2006, 2017, Oracle and/or its affiliates. All rights reserved.
22
33
This program is free software; you can redistribute it and/or modify
44
it under the terms of the GNU General Public License as published by
@@ -229,10 +229,11 @@ int injector::transaction::delete_row(server_id_type sid, table tbl,
229229
return delete_row(sid, tbl, cols, colcnt, record, NULL);
230230
}
231231

232-
233-
int injector::transaction::update_row(server_id_type sid, table tbl,
234-
MY_BITMAP const* cols, size_t colcnt,
235-
record_type before, record_type after,
232+
int injector::transaction::update_row(server_id_type sid, table tbl,
233+
MY_BITMAP const* before_cols,
234+
MY_BITMAP const* after_cols,
235+
size_t colcnt,
236+
record_type before, record_type after,
236237
const uchar* extra_row_info)
237238
{
238239
DBUG_ENTER("injector::transaction::update_row(...)");
@@ -244,7 +245,7 @@ int injector::transaction::update_row(server_id_type sid, table tbl,
244245
server_id_type save_id= m_thd->server_id;
245246
m_thd->set_server_id(sid);
246247
// The read- and write sets with autorestore (in the destructor)
247-
table::save_sets saveset(tbl, cols, cols);
248+
table::save_sets saveset(tbl, before_cols, after_cols);
248249

249250
error= m_thd->binlog_update_row(tbl.get_table(), tbl.is_transactional(),
250251
before, after, extra_row_info);
@@ -253,10 +254,10 @@ int injector::transaction::update_row(server_id_type sid, table tbl,
253254
}
254255

255256
int injector::transaction::update_row(server_id_type sid, table tbl,
256-
MY_BITMAP const* cols, size_t colcnt,
257-
record_type before, record_type after)
257+
MY_BITMAP const* cols, size_t colcnt,
258+
record_type before, record_type after)
258259
{
259-
return update_row(sid, tbl, cols, colcnt, before, after, NULL);
260+
return update_row(sid, tbl, cols, cols, colcnt, before, after, NULL);
260261
}
261262

262263
injector::transaction::binlog_pos injector::transaction::start_pos() const

sql/rpl_injector.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2006, 2017, Oracle and/or its affiliates. All rights reserved.
22
33
This program is free software; you can redistribute it and/or modify
44
it under the terms of the GNU General Public License as published by
@@ -224,7 +224,9 @@ class injector
224224
Add an 'update row' entry to the transaction.
225225
*/
226226
int update_row(server_id_type sid, table tbl,
227-
MY_BITMAP const *cols, size_t colcnt,
227+
MY_BITMAP const *before_cols,
228+
MY_BITMAP const *after_cols,
229+
size_t colcnt,
228230
record_type before, record_type after,
229231
const uchar* extra_row_info);
230232
int update_row(server_id_type sid, table tbl,

0 commit comments

Comments
 (0)