Skip to content

Commit c279878

Browse files
committed
Thread safe histograms loading
Previously multiple threads were allowed to load histograms concurrently. There were no known problems caused by this. But given amount of data races in this code, it'd happen sooner or later. To avoid scalability bottleneck, histograms loading is protected by per-TABLE_SHARE atomic variable. Whenever histograms were loaded by preceding statement (hot-path), a scalable load-acquire check is performed. Whenever histograms have to be loaded anew, mutual exclusion for loaders is established by atomic variable. If histograms are being loaded concurrently, statement waits until load is completed. - Table_statistics::total_hist_size moved to TABLE_STATISTICS_CB: only meaningful within TABLE_SHARE (not used for collected stats). - TABLE_STATISTICS_CB::histograms_can_be_read and TABLE_STATISTICS_CB::histograms_are_read are replaced with a tri state atomic variable. - Simplified away alloc_histograms_for_table_share(). Note: there's still likely a data race if a thread attempts accessing histograms data after it failed to load it (because of concurrent load). It was there previously and goes out of the scope of this effort. One way of fixing it could be reviving TABLE::histograms_are_read and adding appropriate checks whenever it is needed. Part of MDEV-19061 - table_share used for reading statistical tables is not protected
1 parent 609a0d3 commit c279878

File tree

4 files changed

+34
-103
lines changed

4 files changed

+34
-103
lines changed

sql/sql_statistics.cc

Lines changed: 20 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -2280,78 +2280,6 @@ static int alloc_statistics_for_table_share(THD* thd, TABLE_SHARE *table_share)
22802280
}
22812281

22822282

2283-
/**
2284-
@brief
2285-
Allocate memory for the histogram used by a table share
2286-
2287-
@param
2288-
thd Thread handler
2289-
@param
2290-
table_share Table share for which the memory for histogram data is allocated
2291-
@param
2292-
is_safe TRUE <-> at any time only one thread can perform the function
2293-
2294-
@note
2295-
The function allocates the memory for the histogram built for a table in the
2296-
table's share memory with the intention to read the data there from the
2297-
system persistent statistical table mysql.column_stats,
2298-
The memory is allocated in the table_share's mem_root.
2299-
If the parameter is_safe is TRUE then it is guaranteed that at any given time
2300-
only one thread is executed the code of the function.
2301-
2302-
@retval
2303-
0 If the memory for all statistical data has been successfully allocated
2304-
@retval
2305-
1 Otherwise
2306-
2307-
@note
2308-
Currently the function always is called with the parameter is_safe set
2309-
to FALSE.
2310-
*/
2311-
2312-
static
2313-
int alloc_histograms_for_table_share(THD* thd, TABLE_SHARE *table_share,
2314-
bool is_safe)
2315-
{
2316-
TABLE_STATISTICS_CB *stats_cb= &table_share->stats_cb;
2317-
2318-
DBUG_ENTER("alloc_histograms_for_table_share");
2319-
2320-
if (!is_safe)
2321-
mysql_mutex_lock(&table_share->LOCK_share);
2322-
2323-
if (stats_cb->histograms_can_be_read)
2324-
{
2325-
if (!is_safe)
2326-
mysql_mutex_unlock(&table_share->LOCK_share);
2327-
DBUG_RETURN(0);
2328-
}
2329-
2330-
Table_statistics *table_stats= stats_cb->table_stats;
2331-
ulong total_hist_size= table_stats->total_hist_size;
2332-
2333-
if (total_hist_size && !table_stats->histograms)
2334-
{
2335-
uchar *histograms= (uchar *) alloc_root(&stats_cb->mem_root,
2336-
total_hist_size);
2337-
if (!histograms)
2338-
{
2339-
if (!is_safe)
2340-
mysql_mutex_unlock(&table_share->LOCK_share);
2341-
DBUG_RETURN(1);
2342-
}
2343-
memset(histograms, 0, total_hist_size);
2344-
table_stats->histograms= histograms;
2345-
stats_cb->histograms_can_be_read= TRUE;
2346-
}
2347-
2348-
if (!is_safe)
2349-
mysql_mutex_unlock(&table_share->LOCK_share);
2350-
2351-
DBUG_RETURN(0);
2352-
2353-
}
2354-
23552283
/**
23562284
@brief
23572285
Initialize the aggregation fields to collect statistics on a column
@@ -2925,7 +2853,7 @@ int read_statistics_for_table(THD *thd, TABLE *table, TABLE_LIST *stat_tables)
29252853
column_stat.get_stat_values();
29262854
total_hist_size+= table_field->read_stats->histogram.get_size();
29272855
}
2928-
read_stats->total_hist_size= total_hist_size;
2856+
table_share->stats_cb.total_hist_size= total_hist_size;
29292857

29302858
/* Read statistics from the statistical table index_stats */
29312859
stat_table= stat_tables[INDEX_STAT].table;
@@ -3059,35 +2987,35 @@ void delete_stat_values_for_table_share(TABLE_SHARE *table_share)
30592987
static
30602988
int read_histograms_for_table(THD *thd, TABLE *table, TABLE_LIST *stat_tables)
30612989
{
3062-
TABLE_SHARE *table_share= table->s;
3063-
2990+
TABLE_STATISTICS_CB *stats_cb= &table->s->stats_cb;
30642991
DBUG_ENTER("read_histograms_for_table");
30652992

3066-
if (!table_share->stats_cb.histograms_can_be_read)
2993+
if (stats_cb->start_histograms_load())
30672994
{
3068-
(void) alloc_histograms_for_table_share(thd, table_share, FALSE);
3069-
}
3070-
if (table_share->stats_cb.histograms_can_be_read &&
3071-
!table_share->stats_cb.histograms_are_read)
3072-
{
3073-
Field **field_ptr;
3074-
uchar *histogram= table_share->stats_cb.table_stats->histograms;
3075-
TABLE *stat_table= stat_tables[COLUMN_STAT].table;
3076-
Column_stat column_stat(stat_table, table);
3077-
for (field_ptr= table_share->field; *field_ptr; field_ptr++)
2995+
uchar *histogram= (uchar *) alloc_root(&stats_cb->mem_root,
2996+
stats_cb->total_hist_size);
2997+
if (!histogram)
2998+
{
2999+
stats_cb->abort_histograms_load();
3000+
DBUG_RETURN(1);
3001+
}
3002+
memset(histogram, 0, stats_cb->total_hist_size);
3003+
3004+
Column_stat column_stat(stat_tables[COLUMN_STAT].table, table);
3005+
for (Field **field_ptr= table->s->field; *field_ptr; field_ptr++)
30783006
{
30793007
Field *table_field= *field_ptr;
3080-
uint hist_size= table_field->read_stats->histogram.get_size();
3081-
if (hist_size)
3008+
if (uint hist_size= table_field->read_stats->histogram.get_size())
30823009
{
30833010
column_stat.set_key_fields(table_field);
30843011
table_field->read_stats->histogram.set_values(histogram);
30853012
column_stat.get_histogram_value();
30863013
histogram+= hist_size;
30873014
}
30883015
}
3016+
stats_cb->end_histograms_load();
30893017
}
3090-
3018+
table->histograms_are_read= true;
30913019
DBUG_RETURN(0);
30923020
}
30933021

@@ -3178,8 +3106,8 @@ int read_statistics_for_tables(THD *thd, TABLE_LIST *tables)
31783106
if (!tl->table->stats_is_read)
31793107
dump_stats_from_share_to_table(tl->table);
31803108
tl->table->histograms_are_read=
3181-
table_share->stats_cb.histograms_are_read;
3182-
if (table_share->stats_cb.histograms_are_read ||
3109+
table_share->stats_cb.histograms_are_ready();
3110+
if (table_share->stats_cb.histograms_are_ready() ||
31833111
thd->variables.optimizer_use_condition_selectivity <= 3)
31843112
continue;
31853113
}
@@ -3217,14 +3145,8 @@ int read_statistics_for_tables(THD *thd, TABLE_LIST *tables)
32173145
else
32183146
continue;
32193147
}
3220-
if (thd->variables.optimizer_use_condition_selectivity > 3 &&
3221-
!table_share->stats_cb.histograms_are_read)
3222-
{
3148+
if (thd->variables.optimizer_use_condition_selectivity > 3)
32233149
(void) read_histograms_for_table(thd, tl->table, stat_tables);
3224-
table_share->stats_cb.histograms_are_read= TRUE;
3225-
}
3226-
if (table_share->stats_cb.histograms_are_read)
3227-
tl->table->histograms_are_read= TRUE;
32283150
}
32293151
}
32303152

sql/sql_statistics.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,6 @@ class Table_statistics
280280
Column_statistics *column_stats; /* Array of statistical data for columns */
281281
Index_statistics *index_stats; /* Array of statistical data for indexes */
282282
ulong *idx_avg_frequency; /* Array of records per key for index prefixes */
283-
ulong total_hist_size; /* Total size of all histograms */
284283
uchar *histograms; /* Sequence of histograms */
285284
};
286285

sql/table.cc

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -416,8 +416,6 @@ void TABLE_SHARE::destroy()
416416

417417
delete_stat_values_for_table_share(this);
418418
free_root(&stats_cb.mem_root, MYF(0));
419-
stats_cb.histograms_can_be_read= FALSE;
420-
stats_cb.histograms_are_read= FALSE;
421419

422420
/* The mutexes are initialized only for shares that are part of the TDC */
423421
if (tmp_table == NO_TMP_TABLE)

sql/table.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -659,13 +659,25 @@ class TABLE_STATISTICS_CB
659659
};
660660

661661
class Statistics_state stats_state;
662+
class Statistics_state hist_state;
662663

663664
public:
664665
MEM_ROOT mem_root; /* MEM_ROOT to allocate statistical data for the table */
665666
Table_statistics *table_stats; /* Structure to access the statistical data */
666-
bool histograms_can_be_read;
667-
bool histograms_are_read;
667+
ulong total_hist_size; /* Total size of all histograms */
668668

669+
bool histograms_are_ready() const
670+
{
671+
return !total_hist_size || hist_state.is_ready();
672+
}
673+
674+
bool start_histograms_load()
675+
{
676+
return total_hist_size && hist_state.start_load();
677+
}
678+
679+
void end_histograms_load() { hist_state.end_load(); }
680+
void abort_histograms_load() { hist_state.abort_load(); }
669681
bool stats_are_ready() const { return stats_state.is_ready(); }
670682
bool start_stats_load() { return stats_state.start_load(); }
671683
void end_stats_load() { stats_state.end_load(); }

0 commit comments

Comments
 (0)