-
Notifications
You must be signed in to change notification settings - Fork 4k
/
Copy pathtable_histograms.h
316 lines (250 loc) · 12.6 KB
/
table_histograms.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
#ifndef HISTOGRAMS_TABLE_HISTOGRAMS_INCLUDED
#define HISTOGRAMS_TABLE_HISTOGRAMS_INCLUDED
/* Copyright (c) 2022, 2025, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2.0,
as published by the Free Software Foundation.
This program is designed to work with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation. The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have either included with
the program or referenced in the documentation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License, version 2.0, for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#include <array>
#include <cassert>
#include "map_helpers.h" // mem_root_unordered_map
#include "my_alloc.h" // MEM_ROOT
#include "sql/histograms/histogram.h"
#include "sql/psi_memory_key.h"
/*
The Table_histograms_collection holds a reference-counted collection of
Table_histograms objects.
Table_histograms_collection memory management
---------------------------------------------
Each TABLE_SHARE has a pointer to a Table_histograms_collection that is
allocated on the TABLE_SHARE MEM_ROOT when prepare_share() is called from
dd_table_share.cc:open_table_def().
The Table_histograms_collection is destroyed (its destructor is invoked) when
TABLE_SHARE::destroy() is called. The TABLE_SHARE MEM_ROOT also frees its memory
so the Table_histograms_collection object is also freed.
Table_histograms_collection and its managing of Table_histograms
----------------------------------------------------------------
The Table_histograms objects in the Table_histograms_collection each represent a
snapshot of the histograms on a table. The state of this collection is
manipulated through three methods:
insert(): Inserts a Table_histograms object and marks it current, removing the
current object if it has a reference count of zero.
acquire(): Returns a pointer to the current Table_histograms object and
increments its reference count.
release(): Releases a Table_histograms object back by decreasing its reference
count. Removes the object if it has a reference count of zero and is
non-current.
A Table_histograms object is inserted into the collection when the TABLE_SHARE
is first opened (not found in the table definition cache) in
sql_base.cc:get_table_share(). An updated Table_histograms object is inserted
following a successful ANALYZE TABLE UPDATE/DROP HISTOGRAM command. This happens
in sql_admin.cc:update_share_histograms().
TABLE objects acquire() a pointer to a Table_histograms object from the
Table_histograms_collection when a table is first opened through
table.cc:open_table_from_share(). This is the only place where acquire() is
called.
TABLE objects release() the pointer to the Table_histograms object back to the
Table_histograms_collection when the TABLE is destroyed and freed in
sql_base.cc:intern_close_table. If an error happens after a Table_histograms
object has been acquired during open_table_from_share() we also make sure to
release() it back. Finally histograms are released back in a few code paths that
perform ad-hoc opening of tables in connection with the REPAIR statement.
Table_histograms_collection concurrency
---------------------------------------
Because multiple threads can be attempting to insert/acquire/release
Table_histograms from the Table_histograms_collection on a single TABLE_SHARE we
require some concurrency control.
In order to protect the Table_histograms_collection from concurrent modification
we make sure to lock/unlock the LOCK_open mutex around certain operations. The
mutex protection is performed outside of the object (each object does not have
its own mutex), and must be seen in the context of the lifetime of the
TABLE_SHARE.
We do not use mutex protection when setting up or tearing down the TABLE_SHARE
object, because the appropriate protection should already be in place. For
example, for the insert() in sql_base.cc:get_table_share() we do not use mutex
protection since we are in the process of constructing the TABLE_SHARE.
-- insert() in histogram.cc:update_share_histograms(): protected by LOCK_open.
-- acquire() in table.cc:open_table_from_share(): protected by LOCK_open.
-- release() in sql_base.cc:intern_close_table(): protected by LOCK_open.
With respect to performance, for the insert() and release() operations we are
able to re-use existing lock/unlock pairs, but for the acquire() operation we
take out an additional lock. Since this lock is global and central to a lot of
server operations, we would have to benchmark to see if it is better to
introduce a new lock.
Table_histograms memory management
----------------------------------
Table_histograms objects are allocated on a MEM_ROOT that is a member of the
object itself. We create a Table_histograms object through the factory method
Table_histograms::create() which allocates a new Table_histograms object and
returns a pointer to it. It is the responsibility of the caller to ensure that
the destructor of this object is invoked which will free its memory.
When we want to insert() a new Table_histograms object in the
Table_histograms_collection on a TABLE_SHARE we first call
Table_histograms::create() to create an empty Table_histograms object. Next we
fill it with histograms by retrieving histograms from the data dictionary and
calling Table_histograms::insert_histogram() which copies the histogram to the
MEM_ROOT on the Table_histograms object. Finally we insert() the object into the
Table_histograms_collection which transfers ownership/lifetime responsibility
from the calling code to the collection.
*/
/**
The Table_histograms class represents a snapshot of the collection of
histograms associated with a table. Table_histograms contains a reference
counter to keep track of the number of TABLE objects that point to it.
Table_histogram objects are created using the static factory method create().
The object itself and everything it points to (including its MEM_ROOT) is
allocated on its own MEM_ROOT. Table_histogram objects are destroyed/freed by
calling destroy() that clears the MEM_ROOT.
*/
class Table_histograms {
public:
/**
Factory method to create Table_histogram objects. Allocates a
Table_histogram object on its own MEM_ROOT and returns a pointer.
Should be matched by a call to destroy().
@param psi_key performance schema instrumentation memory key to track all
memory used by the object.
@return A pointer to a Table_histograms object if construction was
successful, returns nullptr otherwise.
*/
static Table_histograms *create(PSI_memory_key psi_key) noexcept;
private:
Table_histograms() = default;
public:
Table_histograms(Table_histograms &&) = delete;
Table_histograms &operator=(Table_histograms &&) = delete;
Table_histograms(const Table_histograms &) = delete;
Table_histograms &operator=(const Table_histograms &) = delete;
~Table_histograms() = delete;
/**
Destroys the object and frees memory.
*/
void destroy();
/**
Perform a lookup in the local collection of histograms for a histogram on
a given field.
@param field_index Index of the field to find a histogram for.
@return Pointer to a histogram or nullptr if no histogram was found.
*/
const histograms::Histogram *find_histogram(unsigned int field_index) const;
/**
Copies the given histogram onto the local MEM_ROOT and inserts the copy
into the local collection of histograms.
@param field_index Index of the field to insert a histogram for.
@param histogram Pointer to the histogram to be copied and inserted.
@return False if success, true on error.
*/
bool insert_histogram(unsigned int field_index,
const histograms::Histogram *histogram);
friend class Table_histograms_collection;
private:
MEM_ROOT m_mem_root;
mem_root_unordered_map<unsigned int, const histograms::Histogram *>
*m_histograms{nullptr};
// The following members are only intended to be manipulated by the
// Table_histograms_collection that the Table_histograms object is inserted
// into.
/// The number of TABLE objects referencing this object.
int reference_count() const { return m_reference_counter; }
void increment_reference_counter() { ++m_reference_counter; }
void decrement_reference_counter() {
assert(m_reference_counter > 0);
--m_reference_counter;
}
/// The index of this object in the Table_histograms_collection.
int get_index() const { return m_index; }
void set_index(int index) { m_index = index; }
int m_reference_counter{0};
size_t m_index{0};
};
constexpr size_t kMaxNumberOfTableHistogramsInCollection = 16;
/**
The Table_histograms_collection manages a collection of reference-counted
snapshots of histogram statistics (Table_histograms objects) for a table. It
is intended to live on the TABLE_SHARE and provide TABLE objects with
reference-counted access to Table_histogram objects through the acquire() and
release() methods. The motivation for this class is to decouple the lifetime
of histogram statistics from the lifetime of the TABLE_SHARE, so that we
avoid having to invalidate the TABLE_SHARE when updating/dropping histograms.
Multiple threads can be opening/closing tables concurrently. Member functions
on the Table_histograms_collection should be protected by holding LOCK_open.
When the TABLE_SHARE is initialized and whenever the histograms associated
with a table are updated, we create a new Table_histograms object, insert it
into the collection, and mark it current.
*/
class Table_histograms_collection {
public:
Table_histograms_collection();
Table_histograms_collection(Table_histograms_collection &&) = delete;
Table_histograms_collection &operator=(Table_histograms_collection &&) =
delete;
Table_histograms_collection(const Table_histograms_collection &) = delete;
Table_histograms_collection &operator=(const Table_histograms_collection &) =
delete;
~Table_histograms_collection();
/**
Acquire a pointer to the most recently inserted Table_histograms object.
Increments the reference counter on the returned Table_histograms object.
@return Pointer to the current Table_histograms object or nullptr if none
exists.
*/
const Table_histograms *acquire();
/**
Release a previously acquired Table_histograms object, decreasing its
reference count. If the reference count of a non-current Table_histograms
object reaches zero we delete it. This frees up memory and makes room for a
new Table_histograms object in the collection.
@param histograms Pointer to a Table_histograms object to be released.
*/
void release(const Table_histograms *histograms);
/**
Attempt to insert the supplied Table_histograms object into the collection.
The insertion will fail if the collection is full. If the insertion succeeds
we mark the object as current and take ownership. The previous current
object is deleted if it has a reference count of zero
@param histograms Pointer to the Table_histograms object to be inserted.
@return False if the insertion took place, true otherwise.
*/
bool insert(Table_histograms *histograms);
/**
Count the total number of TABLE objects referencing Table_histograms objects
in the collection. Primarily used for testing.
@return The sum of Table_histogram reference counters. Zero if the
collection is empty.
*/
int total_reference_count() const;
/**
Counts the number of Table_histograms objects in the collection. Primarily
used for testing.
@return The count of non-null pointers to Table_histograms objects in the
collection.
*/
size_t size() const;
private:
/**
Frees a Table_histograms object from the collection and sets its pointer to
nullptr.
@param idx Index of the Table_histograms object to free.
*/
void free_table_histograms(size_t idx) {
m_table_histograms[idx]->destroy();
m_table_histograms[idx] = nullptr;
}
std::array<Table_histograms *, kMaxNumberOfTableHistogramsInCollection>
m_table_histograms;
size_t m_current_index{0};
};
#endif