1 | /* |
2 | Copyright (c) 2015, Facebook, Inc. |
3 | |
4 | This program is free software; you can redistribute it and/or modify |
5 | it under the terms of the GNU General Public License as published by |
6 | the Free Software Foundation; version 2 of the License. |
7 | |
8 | This program is distributed in the hope that it will be useful, |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | GNU General Public License for more details. |
12 | |
13 | You should have received a copy of the GNU General Public License |
14 | along with this program; if not, write to the Free Software |
15 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ |
16 | |
17 | #ifdef _WIN32 |
18 | #define _CRT_RAND_S |
19 | #endif |
20 | #include <my_global.h> |
21 | #ifdef _WIN32 |
22 | #include <stdlib.h> |
23 | #define rand_r rand_s |
24 | #endif |
25 | /* This C++ file's header file */ |
26 | #include "./properties_collector.h" |
27 | |
28 | /* Standard C++ header files */ |
29 | #include <algorithm> |
30 | #include <map> |
31 | #include <string> |
32 | #include <vector> |
33 | |
34 | /* MySQL header files */ |
35 | #include "./log.h" |
36 | #include "./my_stacktrace.h" |
37 | #include "./sql_array.h" |
38 | |
39 | /* MyRocks header files */ |
40 | #include "./rdb_datadic.h" |
41 | #include "./rdb_utils.h" |
42 | |
43 | namespace myrocks { |
44 | |
45 | std::atomic<uint64_t> rocksdb_num_sst_entry_put(0); |
46 | std::atomic<uint64_t> rocksdb_num_sst_entry_delete(0); |
47 | std::atomic<uint64_t> rocksdb_num_sst_entry_singledelete(0); |
48 | std::atomic<uint64_t> rocksdb_num_sst_entry_merge(0); |
49 | std::atomic<uint64_t> rocksdb_num_sst_entry_other(0); |
50 | my_bool rocksdb_compaction_sequential_deletes_count_sd = false; |
51 | |
52 | Rdb_tbl_prop_coll::Rdb_tbl_prop_coll(Rdb_ddl_manager *const ddl_manager, |
53 | const Rdb_compact_params ¶ms, |
54 | const uint32_t &cf_id, |
55 | const uint8_t &table_stats_sampling_pct) |
56 | : m_cf_id(cf_id), m_ddl_manager(ddl_manager), m_last_stats(nullptr), |
57 | m_rows(0l), m_window_pos(0l), m_deleted_rows(0l), m_max_deleted_rows(0l), |
58 | m_file_size(0), m_params(params), |
59 | m_cardinality_collector(table_stats_sampling_pct) { |
60 | DBUG_ASSERT(ddl_manager != nullptr); |
61 | |
62 | m_deleted_rows_window.resize(m_params.m_window, false); |
63 | } |
64 | |
65 | /* |
66 | This function is called by RocksDB for every key in the SST file |
67 | */ |
68 | rocksdb::Status Rdb_tbl_prop_coll::AddUserKey(const rocksdb::Slice &key, |
69 | const rocksdb::Slice &value, |
70 | rocksdb::EntryType type, |
71 | rocksdb::SequenceNumber seq, |
72 | uint64_t file_size) { |
73 | if (key.size() >= 4) { |
74 | AdjustDeletedRows(type); |
75 | |
76 | m_rows++; |
77 | |
78 | CollectStatsForRow(key, value, type, file_size); |
79 | } |
80 | |
81 | return rocksdb::Status::OK(); |
82 | } |
83 | |
84 | void Rdb_tbl_prop_coll::AdjustDeletedRows(rocksdb::EntryType type) { |
85 | if (m_params.m_window > 0) { |
86 | // record the "is deleted" flag into the sliding window |
87 | // the sliding window is implemented as a circular buffer |
88 | // in m_deleted_rows_window vector |
89 | // the current position in the circular buffer is pointed at by |
90 | // m_rows % m_deleted_rows_window.size() |
91 | // m_deleted_rows is the current number of 1's in the vector |
92 | // --update the counter for the element which will be overridden |
93 | const bool is_delete = (type == rocksdb::kEntryDelete || |
94 | (type == rocksdb::kEntrySingleDelete && |
95 | rocksdb_compaction_sequential_deletes_count_sd)); |
96 | |
97 | // Only make changes if the value at the current position needs to change |
98 | if (is_delete != m_deleted_rows_window[m_window_pos]) { |
99 | // Set or clear the flag at the current position as appropriate |
100 | m_deleted_rows_window[m_window_pos] = is_delete; |
101 | if (!is_delete) { |
102 | m_deleted_rows--; |
103 | } else if (++m_deleted_rows > m_max_deleted_rows) { |
104 | m_max_deleted_rows = m_deleted_rows; |
105 | } |
106 | } |
107 | |
108 | if (++m_window_pos == m_params.m_window) { |
109 | m_window_pos = 0; |
110 | } |
111 | } |
112 | } |
113 | |
114 | Rdb_index_stats *Rdb_tbl_prop_coll::AccessStats(const rocksdb::Slice &key) { |
115 | GL_INDEX_ID gl_index_id; |
116 | gl_index_id.cf_id = m_cf_id; |
117 | gl_index_id.index_id = rdb_netbuf_to_uint32(reinterpret_cast<const uchar*>(key.data())); |
118 | |
119 | if (m_last_stats == nullptr || m_last_stats->m_gl_index_id != gl_index_id) { |
120 | m_keydef = nullptr; |
121 | |
122 | // starting a new table |
123 | // add the new element into m_stats |
124 | m_stats.emplace_back(gl_index_id); |
125 | m_last_stats = &m_stats.back(); |
126 | |
127 | if (m_ddl_manager) { |
128 | // safe_find() returns a std::shared_ptr<Rdb_key_def> with the count |
129 | // incremented (so it can't be deleted out from under us) and with |
130 | // the mutex locked (if setup has not occurred yet). We must make |
131 | // sure to free the mutex (via unblock_setup()) when we are done |
132 | // with this object. Currently this happens earlier in this function |
133 | // when we are switching to a new Rdb_key_def and when this object |
134 | // is destructed. |
135 | m_keydef = m_ddl_manager->safe_find(gl_index_id); |
136 | if (m_keydef != nullptr) { |
137 | // resize the array to the number of columns. |
138 | // It will be initialized with zeroes |
139 | m_last_stats->m_distinct_keys_per_prefix.resize( |
140 | m_keydef->get_key_parts()); |
141 | m_last_stats->m_name = m_keydef->get_name(); |
142 | } |
143 | } |
144 | m_cardinality_collector.Reset(); |
145 | } |
146 | |
147 | return m_last_stats; |
148 | } |
149 | |
150 | void Rdb_tbl_prop_coll::CollectStatsForRow(const rocksdb::Slice &key, |
151 | const rocksdb::Slice &value, |
152 | const rocksdb::EntryType &type, |
153 | const uint64_t &file_size) { |
154 | auto stats = AccessStats(key); |
155 | |
156 | stats->m_data_size += key.size() + value.size(); |
157 | |
158 | // Incrementing per-index entry-type statistics |
159 | switch (type) { |
160 | case rocksdb::kEntryPut: |
161 | stats->m_rows++; |
162 | break; |
163 | case rocksdb::kEntryDelete: |
164 | stats->m_entry_deletes++; |
165 | break; |
166 | case rocksdb::kEntrySingleDelete: |
167 | stats->m_entry_single_deletes++; |
168 | break; |
169 | case rocksdb::kEntryMerge: |
170 | stats->m_entry_merges++; |
171 | break; |
172 | case rocksdb::kEntryOther: |
173 | stats->m_entry_others++; |
174 | break; |
175 | default: |
176 | // NO_LINT_DEBUG |
177 | sql_print_error("RocksDB: Unexpected entry type found: %u. " |
178 | "This should not happen so aborting the system." , |
179 | type); |
180 | abort(); |
181 | break; |
182 | } |
183 | |
184 | stats->m_actual_disk_size += file_size - m_file_size; |
185 | m_file_size = file_size; |
186 | |
187 | if (m_keydef != nullptr) { |
188 | m_cardinality_collector.ProcessKey(key, m_keydef.get(), stats); |
189 | } |
190 | } |
191 | |
192 | const char *Rdb_tbl_prop_coll::INDEXSTATS_KEY = "__indexstats__" ; |
193 | |
194 | /* |
195 | This function is called by RocksDB to compute properties to store in sst file |
196 | */ |
197 | rocksdb::Status |
198 | Rdb_tbl_prop_coll::Finish(rocksdb::UserCollectedProperties *const properties) { |
199 | uint64_t num_sst_entry_put = 0; |
200 | uint64_t num_sst_entry_delete = 0; |
201 | uint64_t num_sst_entry_singledelete = 0; |
202 | uint64_t num_sst_entry_merge = 0; |
203 | uint64_t num_sst_entry_other = 0; |
204 | |
205 | DBUG_ASSERT(properties != nullptr); |
206 | |
207 | for (auto it = m_stats.begin(); it != m_stats.end(); it++) { |
208 | num_sst_entry_put += it->m_rows; |
209 | num_sst_entry_delete += it->m_entry_deletes; |
210 | num_sst_entry_singledelete += it->m_entry_single_deletes; |
211 | num_sst_entry_merge += it->m_entry_merges; |
212 | num_sst_entry_other += it->m_entry_others; |
213 | } |
214 | |
215 | if (num_sst_entry_put > 0) { |
216 | rocksdb_num_sst_entry_put += num_sst_entry_put; |
217 | } |
218 | |
219 | if (num_sst_entry_delete > 0) { |
220 | rocksdb_num_sst_entry_delete += num_sst_entry_delete; |
221 | } |
222 | |
223 | if (num_sst_entry_singledelete > 0) { |
224 | rocksdb_num_sst_entry_singledelete += num_sst_entry_singledelete; |
225 | } |
226 | |
227 | if (num_sst_entry_merge > 0) { |
228 | rocksdb_num_sst_entry_merge += num_sst_entry_merge; |
229 | } |
230 | |
231 | if (num_sst_entry_other > 0) { |
232 | rocksdb_num_sst_entry_other += num_sst_entry_other; |
233 | } |
234 | |
235 | for (Rdb_index_stats &stat : m_stats) { |
236 | m_cardinality_collector.AdjustStats(&stat); |
237 | } |
238 | properties->insert({INDEXSTATS_KEY, Rdb_index_stats::materialize(m_stats)}); |
239 | return rocksdb::Status::OK(); |
240 | } |
241 | |
242 | bool Rdb_tbl_prop_coll::NeedCompact() const { |
243 | return m_params.m_deletes && (m_params.m_window > 0) && |
244 | (m_file_size > m_params.m_file_size) && |
245 | (m_max_deleted_rows > m_params.m_deletes); |
246 | } |
247 | |
248 | /* |
249 | Returns the same as above, but in human-readable way for logging |
250 | */ |
251 | rocksdb::UserCollectedProperties |
252 | Rdb_tbl_prop_coll::GetReadableProperties() const { |
253 | std::string s; |
254 | #ifdef DBUG_OFF |
255 | s.append("[..." ); |
256 | s.append(std::to_string(m_stats.size())); |
257 | s.append(" records...]" ); |
258 | #else |
259 | bool first = true; |
260 | for (auto it : m_stats) { |
261 | if (first) { |
262 | first = false; |
263 | } else { |
264 | s.append("," ); |
265 | } |
266 | s.append(GetReadableStats(it)); |
267 | } |
268 | #endif |
269 | return rocksdb::UserCollectedProperties{{INDEXSTATS_KEY, s}}; |
270 | } |
271 | |
272 | std::string Rdb_tbl_prop_coll::GetReadableStats(const Rdb_index_stats &it) { |
273 | std::string s; |
274 | s.append("(" ); |
275 | s.append(std::to_string(it.m_gl_index_id.cf_id)); |
276 | s.append(", " ); |
277 | s.append(std::to_string(it.m_gl_index_id.index_id)); |
278 | s.append("):{name:" ); |
279 | s.append(it.m_name); |
280 | s.append(", size:" ); |
281 | s.append(std::to_string(it.m_data_size)); |
282 | s.append(", m_rows:" ); |
283 | s.append(std::to_string(it.m_rows)); |
284 | s.append(", m_actual_disk_size:" ); |
285 | s.append(std::to_string(it.m_actual_disk_size)); |
286 | s.append(", deletes:" ); |
287 | s.append(std::to_string(it.m_entry_deletes)); |
288 | s.append(", single_deletes:" ); |
289 | s.append(std::to_string(it.m_entry_single_deletes)); |
290 | s.append(", merges:" ); |
291 | s.append(std::to_string(it.m_entry_merges)); |
292 | s.append(", others:" ); |
293 | s.append(std::to_string(it.m_entry_others)); |
294 | s.append(", distincts per prefix: [" ); |
295 | for (auto num : it.m_distinct_keys_per_prefix) { |
296 | s.append(std::to_string(num)); |
297 | s.append(" " ); |
298 | } |
299 | s.append("]}" ); |
300 | return s; |
301 | } |
302 | |
303 | /* |
304 | Given the properties of an SST file, reads the stats from it and returns it. |
305 | */ |
306 | |
307 | void Rdb_tbl_prop_coll::read_stats_from_tbl_props( |
308 | const std::shared_ptr<const rocksdb::TableProperties> &table_props, |
309 | std::vector<Rdb_index_stats> *const out_stats_vector) { |
310 | DBUG_ASSERT(out_stats_vector != nullptr); |
311 | const auto &user_properties = table_props->user_collected_properties; |
312 | const auto it2 = user_properties.find(std::string(INDEXSTATS_KEY)); |
313 | if (it2 != user_properties.end()) { |
314 | auto result MY_ATTRIBUTE((__unused__)) = |
315 | Rdb_index_stats::unmaterialize(it2->second, out_stats_vector); |
316 | DBUG_ASSERT(result == 0); |
317 | } |
318 | } |
319 | |
320 | /* |
321 | Serializes an array of Rdb_index_stats into a network string. |
322 | */ |
323 | std::string |
324 | Rdb_index_stats::materialize(const std::vector<Rdb_index_stats> &stats) { |
325 | String ret; |
326 | rdb_netstr_append_uint16(&ret, INDEX_STATS_VERSION_ENTRY_TYPES); |
327 | for (const auto &i : stats) { |
328 | rdb_netstr_append_uint32(&ret, i.m_gl_index_id.cf_id); |
329 | rdb_netstr_append_uint32(&ret, i.m_gl_index_id.index_id); |
330 | DBUG_ASSERT(sizeof i.m_data_size <= 8); |
331 | rdb_netstr_append_uint64(&ret, i.m_data_size); |
332 | rdb_netstr_append_uint64(&ret, i.m_rows); |
333 | rdb_netstr_append_uint64(&ret, i.m_actual_disk_size); |
334 | rdb_netstr_append_uint64(&ret, i.m_distinct_keys_per_prefix.size()); |
335 | rdb_netstr_append_uint64(&ret, i.m_entry_deletes); |
336 | rdb_netstr_append_uint64(&ret, i.m_entry_single_deletes); |
337 | rdb_netstr_append_uint64(&ret, i.m_entry_merges); |
338 | rdb_netstr_append_uint64(&ret, i.m_entry_others); |
339 | for (const auto &num_keys : i.m_distinct_keys_per_prefix) { |
340 | rdb_netstr_append_uint64(&ret, num_keys); |
341 | } |
342 | } |
343 | |
344 | return std::string((char *)ret.ptr(), ret.length()); |
345 | } |
346 | |
347 | /** |
348 | @brief |
349 | Reads an array of Rdb_index_stats from a string. |
350 | @return HA_EXIT_FAILURE if it detects any inconsistency in the input |
351 | @return HA_EXIT_SUCCESS if completes successfully |
352 | */ |
353 | int Rdb_index_stats::unmaterialize(const std::string &s, |
354 | std::vector<Rdb_index_stats> *const ret) { |
355 | const uchar *p = rdb_std_str_to_uchar_ptr(s); |
356 | const uchar *const p2 = p + s.size(); |
357 | |
358 | DBUG_ASSERT(ret != nullptr); |
359 | |
360 | if (p + 2 > p2) { |
361 | return HA_EXIT_FAILURE; |
362 | } |
363 | |
364 | const int version = rdb_netbuf_read_uint16(&p); |
365 | Rdb_index_stats stats; |
366 | // Make sure version is within supported range. |
367 | if (version < INDEX_STATS_VERSION_INITIAL || |
368 | version > INDEX_STATS_VERSION_ENTRY_TYPES) { |
369 | // NO_LINT_DEBUG |
370 | sql_print_error("Index stats version %d was outside of supported range. " |
371 | "This should not happen so aborting the system." , |
372 | version); |
373 | abort(); |
374 | } |
375 | |
376 | size_t needed = sizeof(stats.m_gl_index_id.cf_id) + |
377 | sizeof(stats.m_gl_index_id.index_id) + |
378 | sizeof(stats.m_data_size) + sizeof(stats.m_rows) + |
379 | sizeof(stats.m_actual_disk_size) + sizeof(uint64); |
380 | if (version >= INDEX_STATS_VERSION_ENTRY_TYPES) { |
381 | needed += sizeof(stats.m_entry_deletes) + |
382 | sizeof(stats.m_entry_single_deletes) + |
383 | sizeof(stats.m_entry_merges) + sizeof(stats.m_entry_others); |
384 | } |
385 | |
386 | while (p < p2) { |
387 | if (p + needed > p2) { |
388 | return HA_EXIT_FAILURE; |
389 | } |
390 | rdb_netbuf_read_gl_index(&p, &stats.m_gl_index_id); |
391 | stats.m_data_size = rdb_netbuf_read_uint64(&p); |
392 | stats.m_rows = rdb_netbuf_read_uint64(&p); |
393 | stats.m_actual_disk_size = rdb_netbuf_read_uint64(&p); |
394 | stats.m_distinct_keys_per_prefix.resize(rdb_netbuf_read_uint64(&p)); |
395 | if (version >= INDEX_STATS_VERSION_ENTRY_TYPES) { |
396 | stats.m_entry_deletes = rdb_netbuf_read_uint64(&p); |
397 | stats.m_entry_single_deletes = rdb_netbuf_read_uint64(&p); |
398 | stats.m_entry_merges = rdb_netbuf_read_uint64(&p); |
399 | stats.m_entry_others = rdb_netbuf_read_uint64(&p); |
400 | } |
401 | if (p + |
402 | stats.m_distinct_keys_per_prefix.size() * |
403 | sizeof(stats.m_distinct_keys_per_prefix[0]) > |
404 | p2) { |
405 | return HA_EXIT_FAILURE; |
406 | } |
407 | for (std::size_t i = 0; i < stats.m_distinct_keys_per_prefix.size(); i++) { |
408 | stats.m_distinct_keys_per_prefix[i] = rdb_netbuf_read_uint64(&p); |
409 | } |
410 | ret->push_back(stats); |
411 | } |
412 | return HA_EXIT_SUCCESS; |
413 | } |
414 | |
415 | /* |
416 | Merges one Rdb_index_stats into another. Can be used to come up with the stats |
417 | for the index based on stats for each sst |
418 | */ |
419 | void Rdb_index_stats::merge(const Rdb_index_stats &s, const bool &increment, |
420 | const int64_t &estimated_data_len) { |
421 | std::size_t i; |
422 | |
423 | DBUG_ASSERT(estimated_data_len >= 0); |
424 | |
425 | m_gl_index_id = s.m_gl_index_id; |
426 | if (m_distinct_keys_per_prefix.size() < s.m_distinct_keys_per_prefix.size()) { |
427 | m_distinct_keys_per_prefix.resize(s.m_distinct_keys_per_prefix.size()); |
428 | } |
429 | if (increment) { |
430 | m_rows += s.m_rows; |
431 | m_data_size += s.m_data_size; |
432 | |
433 | /* |
434 | The Data_length and Avg_row_length are trailing statistics, meaning |
435 | they don't get updated for the current SST until the next SST is |
436 | written. So, if rocksdb reports the data_length as 0, |
437 | we make a reasoned estimate for the data_file_length for the |
438 | index in the current SST. |
439 | */ |
440 | m_actual_disk_size += s.m_actual_disk_size ? s.m_actual_disk_size |
441 | : estimated_data_len * s.m_rows; |
442 | m_entry_deletes += s.m_entry_deletes; |
443 | m_entry_single_deletes += s.m_entry_single_deletes; |
444 | m_entry_merges += s.m_entry_merges; |
445 | m_entry_others += s.m_entry_others; |
446 | if (s.m_distinct_keys_per_prefix.size() > 0) { |
447 | for (i = 0; i < s.m_distinct_keys_per_prefix.size(); i++) { |
448 | m_distinct_keys_per_prefix[i] += s.m_distinct_keys_per_prefix[i]; |
449 | } |
450 | } else { |
451 | for (i = 0; i < m_distinct_keys_per_prefix.size(); i++) { |
452 | m_distinct_keys_per_prefix[i] += |
453 | s.m_rows >> (m_distinct_keys_per_prefix.size() - i - 1); |
454 | } |
455 | } |
456 | } else { |
457 | m_rows -= s.m_rows; |
458 | m_data_size -= s.m_data_size; |
459 | m_actual_disk_size -= s.m_actual_disk_size ? s.m_actual_disk_size |
460 | : estimated_data_len * s.m_rows; |
461 | m_entry_deletes -= s.m_entry_deletes; |
462 | m_entry_single_deletes -= s.m_entry_single_deletes; |
463 | m_entry_merges -= s.m_entry_merges; |
464 | m_entry_others -= s.m_entry_others; |
465 | if (s.m_distinct_keys_per_prefix.size() > 0) { |
466 | for (i = 0; i < s.m_distinct_keys_per_prefix.size(); i++) { |
467 | m_distinct_keys_per_prefix[i] -= s.m_distinct_keys_per_prefix[i]; |
468 | } |
469 | } else { |
470 | for (i = 0; i < m_distinct_keys_per_prefix.size(); i++) { |
471 | m_distinct_keys_per_prefix[i] -= |
472 | s.m_rows >> (m_distinct_keys_per_prefix.size() - i - 1); |
473 | } |
474 | } |
475 | } |
476 | } |
477 | |
478 | Rdb_tbl_card_coll::Rdb_tbl_card_coll(const uint8_t &table_stats_sampling_pct) |
479 | : m_table_stats_sampling_pct(table_stats_sampling_pct), |
480 | m_seed(time(nullptr)) {} |
481 | |
482 | bool Rdb_tbl_card_coll::IsSampingDisabled() { |
483 | // Zero means that we'll use all the keys to update statistics. |
484 | return m_table_stats_sampling_pct == 0 || |
485 | RDB_TBL_STATS_SAMPLE_PCT_MAX == m_table_stats_sampling_pct; |
486 | } |
487 | |
488 | bool Rdb_tbl_card_coll::ShouldCollectStats() { |
489 | if (IsSampingDisabled()) { |
490 | return true; // collect every key |
491 | } |
492 | |
493 | const int val = rand_r(&m_seed) % (RDB_TBL_STATS_SAMPLE_PCT_MAX - |
494 | RDB_TBL_STATS_SAMPLE_PCT_MIN + 1) + |
495 | RDB_TBL_STATS_SAMPLE_PCT_MIN; |
496 | |
497 | DBUG_ASSERT(val >= RDB_TBL_STATS_SAMPLE_PCT_MIN); |
498 | DBUG_ASSERT(val <= RDB_TBL_STATS_SAMPLE_PCT_MAX); |
499 | |
500 | return val <= m_table_stats_sampling_pct; |
501 | } |
502 | |
503 | void Rdb_tbl_card_coll::ProcessKey(const rocksdb::Slice &key, |
504 | const Rdb_key_def *keydef, |
505 | Rdb_index_stats *stats) { |
506 | if (ShouldCollectStats()) { |
507 | std::size_t column = 0; |
508 | bool new_key = true; |
509 | |
510 | if (!m_last_key.empty()) { |
511 | rocksdb::Slice last(m_last_key.data(), m_last_key.size()); |
512 | new_key = (keydef->compare_keys(&last, &key, &column) == 0); |
513 | } |
514 | |
515 | if (new_key) { |
516 | DBUG_ASSERT(column <= stats->m_distinct_keys_per_prefix.size()); |
517 | |
518 | for (auto i = column; i < stats->m_distinct_keys_per_prefix.size(); i++) { |
519 | stats->m_distinct_keys_per_prefix[i]++; |
520 | } |
521 | |
522 | // assign new last_key for the next call |
523 | // however, we only need to change the last key |
524 | // if one of the first n-1 columns is different |
525 | // If the n-1 prefix is the same, no sense in storing |
526 | // the new key |
527 | if (column < stats->m_distinct_keys_per_prefix.size()) { |
528 | m_last_key.assign(key.data(), key.size()); |
529 | } |
530 | } |
531 | } |
532 | } |
533 | |
534 | void Rdb_tbl_card_coll::Reset() { m_last_key.clear(); } |
535 | |
536 | // We need to adjust the index cardinality numbers based on the sampling |
537 | // rate so that the output of "SHOW INDEX" command will reflect reality |
538 | // more closely. It will still be an approximation, just a better one. |
539 | void Rdb_tbl_card_coll::AdjustStats(Rdb_index_stats *stats) { |
540 | if (IsSampingDisabled()) { |
541 | // no sampling was done, return as stats is |
542 | return; |
543 | } |
544 | for (int64_t &num_keys : stats->m_distinct_keys_per_prefix) { |
545 | num_keys = num_keys * 100 / m_table_stats_sampling_pct; |
546 | } |
547 | } |
548 | |
549 | } // namespace myrocks |
550 | |