Merge lp:~tsarev/percona-server/19957 into lp:percona-server/rnt-5.1
- 19957
- Merge into rnt-5.1
Status: | Merged |
---|---|
Approved by: | Oleg Tsarev |
Approved revision: | no longer in the source branch. |
Merged at revision: | 208 |
Proposed branch: | lp:~tsarev/percona-server/19957 |
Merge into: | lp:percona-server/rnt-5.1 |
Prerequisite: | lp:~tsarev/percona-server/rnt-5.1_fix_bug_911237 |
Diff against target: |
745 lines (+733/-0) 2 files modified
patches/bug63320.patch (+732/-0) patches/series (+1/-0) |
To merge this branch: | bzr merge lp:~tsarev/percona-server/19957 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Laurynas Biveinis (community) | Approve | ||
Oleg Tsarev (community) | Needs Resubmitting | ||
Review via email: mp+87362@code.launchpad.net |
This proposal supersedes a proposal from 2012-01-02.
Commit message
Description of the change
Jenkins: http://
Return back Percona's "innodb_
Related Launchpad bug #892405
Related MySQL bug http://
I added new value "nulls_
Current upsteam "nulls_ignored" algorithm calculate not_null values on every estimated (random) page, and after that try to approximates total value count.
Unfortunately, this algorithm works incorrectly on mostly null indexes (see MySQL Bug #63320).
Percona'a algorithm excludes from estimation pages with nulls, calculates the count of different values and approximates total value count.
This algorithm works correctly on mostly null indexes, but has limitation - it works correctly just on one-column-key indexes. On compound-keys algorithm can works incorrectly (I didn't test this).
Unfortunately, I can't imagine now how to discard this limitation.
Laurynas Biveinis (laurynas-biveinis) wrote : | # |
Oleg Tsarev (tsarev) wrote : | # |
Laurynas,
1) Upstream bug analyze has been received after MP review submit
2) Upstream bug analyze doesn't provide way to fix customer problem _right now_.
3) Upstream bug analyze doesn't provide way to fix customer problem in suitable way (without full scan). From this point of view this fix would be not a short-live.
4) Previous Percona's algorithm has been implemented in patch innodb_stats.patch, and has been removed from Percona-Server after integration of upstream algorithm (this was Percona-Server 5.5.8 and Percona-Server 5.1.56)
Here is Yasufumi commit to lp:percona-server/5.1 with remove of the Percona's algorithm.
-------
revno: 208
committer: Yasufumi Kinoshita <kinoyasu@rynex6>
branch nick: 5.1.56-tmp
timestamp: Sat 2011-03-12 00:25:38 +0900
message:
Yasufumi patches and several are ported to 5.1.56; Note: option innodb_stats_method was removed from innodb_stats.patch, because implemented officially. And, bug733317 should be fixed before release
-------
Oleg Tsarev (tsarev) wrote : | # |
Information provided, comment just for "resubmit" status
Laurynas Biveinis (laurynas-biveinis) wrote : | # |
I don't see how 1) is relevant.
Just to be clear, is this patch just a straightforward port of a previously present feature of Percona Server?
Oleg Tsarev (tsarev) wrote : | # |
The 1) JFYI.
Yes, this is "a straightforward port of a previously present feature of Percona Server" integrated with current upstream implementation.
Current upstream works as before (nulls_equal, nulls_unequal, nulls_ignored) and previously present feature also works (nulls_
Laurynas Biveinis (laurynas-biveinis) wrote : | # |
LGTM (for RNT branch only)
Preview Diff
1 | === added file 'patches/bug63320.patch' |
2 | --- patches/bug63320.patch 1970-01-01 00:00:00 +0000 |
3 | +++ patches/bug63320.patch 2012-01-03 14:13:50 +0000 |
4 | @@ -0,0 +1,732 @@ |
5 | +--- a/mysql-test/include/restart_mysqld.inc |
6 | ++++ b/mysql-test/include/restart_mysqld.inc |
7 | +@@ -18,8 +18,13 @@ |
8 | + shutdown_server 10; |
9 | + |
10 | + # Write file to make mysql-test-run.pl start up the server again |
11 | +---exec echo "restart" > $_expect_file_name |
12 | ++if ($restart_mysqld_options) { |
13 | ++--exec echo "restart: $restart_mysqld_options" > $_expect_file_name |
14 | ++} |
15 | + |
16 | ++if (!$restart_mysqld_options) { |
17 | ++--exec echo "restart" > $_expect_file_name |
18 | ++} |
19 | + # Turn on reconnect |
20 | + --enable_reconnect |
21 | + |
22 | +--- /dev/null |
23 | ++++ b/mysql-test/r/percona_stats_null.result |
24 | +@@ -0,0 +1,25 @@ |
25 | ++restart_mysqld_options=--default-storage-engine=myisam --myisam_stats_method=nulls_equal |
26 | ++restart_mysqld_options=--default-storage-engine=myisam --myisam_stats_method=nulls_unequal |
27 | ++restart_mysqld_options=--default-storage-engine=myisam --myisam_stats_method=nulls_ignored |
28 | ++restart_mysqld_options=--default-storage-engine=innodb --innodb_stats_on_metadata=0 --innodb_stats_sample_pages=1000 --innodb_stats_method=nulls_equal |
29 | ++restart_mysqld_options=--default-storage-engine=innodb --innodb_stats_on_metadata=0 --innodb_stats_sample_pages=1000 --innodb_stats_method=nulls_unequal |
30 | ++restart_mysqld_options=--default-storage-engine=innodb --innodb_stats_on_metadata=0 --innodb_stats_sample_pages=1000 --innodb_stats_method=nulls_ignored_exact |
31 | ++restart_mysqld_options=--default-storage-engine=innodb --innodb_stats_on_metadata=0 --innodb_stats_sample_pages=500 --innodb_stats_method=nulls_equal |
32 | ++restart_mysqld_options=--default-storage-engine=innodb --innodb_stats_on_metadata=0 --innodb_stats_sample_pages=500 --innodb_stats_method=nulls_unequal |
33 | ++restart_mysqld_options=--default-storage-engine=innodb --innodb_stats_on_metadata=0 --innodb_stats_sample_pages=500 --innodb_stats_method=nulls_ignored_exact |
34 | ++restart_mysqld_options=--default-storage-engine=innodb --innodb_stats_on_metadata=0 --innodb_stats_sample_pages=250 --innodb_stats_method=nulls_equal |
35 | ++restart_mysqld_options=--default-storage-engine=innodb --innodb_stats_on_metadata=0 --innodb_stats_sample_pages=250 --innodb_stats_method=nulls_unequal |
36 | ++restart_mysqld_options=--default-storage-engine=innodb --innodb_stats_on_metadata=0 --innodb_stats_sample_pages=250 --innodb_stats_method=nulls_ignored_exact |
37 | ++restart_mysqld_options=--default-storage-engine=innodb --innodb_stats_on_metadata=0 --innodb_stats_sample_pages=100 --innodb_stats_method=nulls_equal |
38 | ++restart_mysqld_options=--default-storage-engine=innodb --innodb_stats_on_metadata=0 --innodb_stats_sample_pages=100 --innodb_stats_method=nulls_unequal |
39 | ++restart_mysqld_options=--default-storage-engine=innodb --innodb_stats_on_metadata=0 --innodb_stats_sample_pages=100 --innodb_stats_method=nulls_ignored_exact |
40 | ++restart_mysqld_options=--default-storage-engine=innodb --innodb_stats_on_metadata=0 --innodb_stats_sample_pages=50 --innodb_stats_method=nulls_equal |
41 | ++restart_mysqld_options=--default-storage-engine=innodb --innodb_stats_on_metadata=0 --innodb_stats_sample_pages=50 --innodb_stats_method=nulls_unequal |
42 | ++restart_mysqld_options=--default-storage-engine=innodb --innodb_stats_on_metadata=0 --innodb_stats_sample_pages=50 --innodb_stats_method=nulls_ignored_exact |
43 | ++restart_mysqld_options=--default-storage-engine=innodb --innodb_stats_on_metadata=0 --innodb_stats_sample_pages=25 --innodb_stats_method=nulls_equal |
44 | ++restart_mysqld_options=--default-storage-engine=innodb --innodb_stats_on_metadata=0 --innodb_stats_sample_pages=25 --innodb_stats_method=nulls_unequal |
45 | ++restart_mysqld_options=--default-storage-engine=innodb --innodb_stats_on_metadata=0 --innodb_stats_sample_pages=25 --innodb_stats_method=nulls_ignored_exact |
46 | ++restart_mysqld_options=--default-storage-engine=innodb --innodb_stats_on_metadata=0 --innodb_stats_sample_pages=10 --innodb_stats_method=nulls_equal |
47 | ++restart_mysqld_options=--default-storage-engine=innodb --innodb_stats_on_metadata=0 --innodb_stats_sample_pages=10 --innodb_stats_method=nulls_unequal |
48 | ++restart_mysqld_options=--default-storage-engine=innodb --innodb_stats_on_metadata=0 --innodb_stats_sample_pages=10 --innodb_stats_method=nulls_ignored_exact |
49 | ++DROP TABLE t; |
50 | +--- /dev/null |
51 | ++++ b/mysql-test/t/percona_stats_null.test |
52 | +@@ -0,0 +1,136 @@ |
53 | ++--source include/have_innodb.inc |
54 | ++ |
55 | ++--enable_result_log |
56 | ++--enable_query_log |
57 | ++ |
58 | ++--let delta_coeff=0.2 |
59 | ++ |
60 | ++# engine |
61 | ++# 2 - myisam |
62 | ++# 1 - innodb |
63 | ++# 0 - end |
64 | ++--let engine_index=2 |
65 | ++ |
66 | ++while ($engine_index) |
67 | ++{ |
68 | ++ |
69 | ++--let engine=`SELECT CASE $engine_index WHEN 2 THEN 'myisam' WHEN 1 THEN 'innodb' END` |
70 | ++ |
71 | ++--let engine_cmd=`SELECT CONCAT('--default-storage-engine=', '$engine')` |
72 | ++ |
73 | ++--let additional_cmd=`SELECT CASE $engine_index WHEN 2 THEN '' WHEN 1 THEN '--innodb_stats_on_metadata=0' END` |
74 | ++ |
75 | ++# sample_pages_index |
76 | ++# 7 - 1000 |
77 | ++# 6 - 500 |
78 | ++# 5 - 250 |
79 | ++# 4 - 100 |
80 | ++# 3 - 50 |
81 | ++# 2 - 25 |
82 | ++# 1 - 10 |
83 | ++# 0 - end |
84 | ++--let sample_pages_index=`SELECT CASE $engine_index WHEN 2 THEN 1 WHEN 1 THEN 7 END` |
85 | ++ |
86 | ++while ($sample_pages_index) |
87 | ++{ |
88 | ++--let sample_pages=`SELECT CASE $sample_pages_index WHEN 7 THEN 1000 WHEN 6 THEN 500 WHEN 5 THEN 250 WHEN 4 THEN 100 WHEN 3 THEN 50 WHEN 2 THEN 25 WHEN 1 THEN 10 END` |
89 | ++--let sample_pages_cmd=`SELECT CASE $engine_index WHEN 2 THEN '' WHEN 1 THEN (SELECT CONCAT('--innodb_stats_sample_pages=', $sample_pages)) END` |
90 | ++ |
91 | ++# stats_method_index |
92 | ++ |
93 | ++# 3 - nulls_equal |
94 | ++# 2 - nulls_unequal |
95 | ++# 1 - nulls_ignored | nulls_ignored_exact |
96 | ++# 0 - end |
97 | ++--let stats_method_index=3 |
98 | ++ |
99 | ++while ($stats_method_index) |
100 | ++{ |
101 | ++--let stats_method=`SELECT CASE $stats_method_index WHEN 3 THEN 'nulls_equal' WHEN 2 THEN 'nulls_unequal' WHEN 1 THEN (CASE $engine_index WHEN 2 THEN 'nulls_ignored' WHEN 1 THEN 'nulls_ignored_exact' END) END` |
102 | ++--let stats_method_cmd=`SELECT CONCAT('--', '$engine', '_stats_method=', '$stats_method')` |
103 | ++--let expected_not_null=`SELECT CASE $stats_method_index WHEN 3 THEN TRUE WHEN 2 THEN FALSE WHEN 1 THEN TRUE END` |
104 | ++--let expected_null=`SELECT CASE $stats_method_index WHEN 3 THEN FALSE WHEN 2 THEN TRUE WHEN 1 THEN FALSE END` |
105 | ++ |
106 | ++--let restart_mysqld_options=$engine_cmd $additional_cmd $sample_pages_cmd $stats_method_cmd |
107 | ++--echo restart_mysqld_options=$restart_mysqld_options |
108 | ++--source include/restart_mysqld.inc |
109 | ++ |
110 | ++# prepare data |
111 | ++ |
112 | ++--disable_query_log |
113 | ++--disable_result_log |
114 | ++ |
115 | ++--disable_warnings |
116 | ++DROP TABLE IF EXISTS t; |
117 | ++--enable_warnings |
118 | ++ |
119 | ++eval CREATE TABLE `t` (`id` int(11) DEFAULT NULL, KEY `t$id` (`id`)) ENGINE=$engine; |
120 | ++ |
121 | ++# insert 100 null values |
122 | ++--let i=100 |
123 | ++while($i) |
124 | ++{ |
125 | ++ eval INSERT INTO t VALUES(); |
126 | ++ dec $i; |
127 | ++} |
128 | ++# insert 6 not null values |
129 | ++--let i=6 |
130 | ++while($i) |
131 | ++{ |
132 | ++eval INSERT INTO t(id) VALUES ($i); |
133 | ++dec $i; |
134 | ++} |
135 | ++ |
136 | ++# clone to 100000 |
137 | ++INSERT INTO t SELECT a.* FROM t as a, t as b, t as c LIMIT 99894; |
138 | ++ |
139 | ++ANALYZE TABLE t; |
140 | ++ |
141 | ++--enable_query_log |
142 | ++--enable_result_log |
143 | ++ |
144 | ++--let count= `SELECT count(*) FROM t` |
145 | ++--let null_count= `SELECT count(*) FROM t WHERE id IS NULL` |
146 | ++--let not_null_count=`SELECT count(*) FROM t WHERE id IS NOT NULL` |
147 | ++--let cardinality= query_get_value('SHOW INDEXES FROM t', Cardinality, 1) |
148 | ++--let delta=`SELECT $count * $delta_coeff` |
149 | ++ |
150 | ++--let cardinality_not_null_low=`SELECT IF($not_null_count < $delta, 0, $not_null_count - $delta)` |
151 | ++--let cardinality_not_null_high=`SELECT $not_null_count + $delta` |
152 | ++--let cardinality_null_low=`SELECT IF($null_count < $delta, 0, $null_count - $delta)` |
153 | ++--let cardinality_null_high=`SELECT $null_count + $delta` |
154 | ++ |
155 | ++--let cardinality_near_not_null=`SELECT ($cardinality_not_null_low <= $cardinality) AND ($cardinality <= $cardinality_not_null_high)` |
156 | ++--let cardinality_near_null=`SELECT ($cardinality_null_low <= $cardinality) AND ($cardinality <= $cardinality_null_high)` |
157 | ++ |
158 | ++--let not_null_ok=`SELECT $cardinality_near_not_null = $expected_not_null` |
159 | ++--let null_ok=`SELECT $cardinality_near_null = $expected_null` |
160 | ++ |
161 | ++--let ok=`SELECT ($not_null_ok = TRUE) and ($null_ok = TRUE)` |
162 | ++#--let ok=1 |
163 | ++if (!$ok) |
164 | ++{ |
165 | ++--echo Failed with '$restart_mysqld_options' |
166 | ++--echo Delta is $delta_coeff |
167 | ++--echo Engine is $engine |
168 | ++--echo Sample pages is $sample_pages |
169 | ++--echo Count is $count |
170 | ++--echo Not null count is $not_null_count |
171 | ++--echo Null count is $null_count |
172 | ++--echo Cardinality is $cardinality |
173 | ++--echo Expected range of not null $cardinality_not_null_low...$cardinality_not_null_high |
174 | ++--echo Expected range of null $cardinality_null_low...$cardinality_null_high |
175 | ++--echo Expected near not null is $expected_not_null |
176 | ++--echo Expected near null is $expected_null |
177 | ++} |
178 | ++ |
179 | ++ dec $stats_method_index; |
180 | ++} |
181 | ++ |
182 | ++ dec $sample_pages_index; |
183 | ++} |
184 | ++ |
185 | ++ dec $engine_index; |
186 | ++} |
187 | ++ |
188 | ++DROP TABLE t; |
189 | +--- a/storage/innodb_plugin/btr/btr0cur.c |
190 | ++++ b/storage/innodb_plugin/btr/btr0cur.c |
191 | +@@ -108,8 +108,8 @@ |
192 | + @param ext_size external stored data size |
193 | + @param not_empty table not empty |
194 | + @return estimated table wide stats from sampled value */ |
195 | +-#define BTR_TABLE_STATS_FROM_SAMPLE(value, index, sample, ext_size, not_empty)\ |
196 | +- (((value) * (ib_int64_t) index->stat_n_leaf_pages \ |
197 | ++#define BTR_TABLE_STATS_FROM_SAMPLE(value, effective_pages, sample, ext_size, not_empty) \ |
198 | ++ (((value) * (ib_int64_t) effective_pages \ |
199 | + + (sample) - 1 + (ext_size) + (not_empty)) / ((sample) + (ext_size))) |
200 | + |
201 | + /* @} */ |
202 | +@@ -973,6 +973,108 @@ |
203 | + } |
204 | + } |
205 | + |
206 | ++ |
207 | ++/**********************************************************************//** |
208 | ++Positions a cursor at a randomly chosen position within a B-tree |
209 | ++after the given path |
210 | ++@return TRUE if the position is at the first page, and cursor must point |
211 | ++ the first record for used by the caller.*/ |
212 | ++UNIV_INTERN |
213 | ++ibool |
214 | ++btr_cur_open_at_rnd_pos_after_path( |
215 | ++/*====================*/ |
216 | ++ dict_index_t* index, /*!< in: index */ |
217 | ++ ulint latch_mode, /*!< in: BTR_SEARCH_LEAF, ... */ |
218 | ++ btr_path_t* first_rec_path, |
219 | ++ btr_cur_t* cursor, /*!< in/out: B-tree cursor */ |
220 | ++ mtr_t* mtr) /*!< in: mtr */ |
221 | ++{ |
222 | ++ page_cur_t* page_cursor; |
223 | ++ btr_path_t* slot; |
224 | ++ ibool is_first_rec = TRUE; |
225 | ++ ulint page_no; |
226 | ++ ulint space; |
227 | ++ ulint zip_size; |
228 | ++ ulint height; |
229 | ++ rec_t* node_ptr; |
230 | ++ mem_heap_t* heap = NULL; |
231 | ++ ulint offsets_[REC_OFFS_NORMAL_SIZE]; |
232 | ++ ulint* offsets = offsets_; |
233 | ++ rec_offs_init(offsets_); |
234 | ++ |
235 | ++ if (latch_mode == BTR_MODIFY_TREE) { |
236 | ++ mtr_x_lock(dict_index_get_lock(index), mtr); |
237 | ++ } else { |
238 | ++ mtr_s_lock(dict_index_get_lock(index), mtr); |
239 | ++ } |
240 | ++ |
241 | ++ page_cursor = btr_cur_get_page_cur(cursor); |
242 | ++ cursor->index = index; |
243 | ++ |
244 | ++ space = dict_index_get_space(index); |
245 | ++ zip_size = dict_table_zip_size(index->table); |
246 | ++ page_no = dict_index_get_page(index); |
247 | ++ |
248 | ++ height = ULINT_UNDEFINED; |
249 | ++ slot = first_rec_path; |
250 | ++ |
251 | ++ for (;;) { |
252 | ++ buf_block_t* block; |
253 | ++ page_t* page; |
254 | ++ |
255 | ++ block = buf_page_get_gen(space, zip_size, page_no, |
256 | ++ RW_NO_LATCH, NULL, BUF_GET, |
257 | ++ __FILE__, __LINE__, mtr); |
258 | ++ page = buf_block_get_frame(block); |
259 | ++ ut_ad(0 == ut_dulint_cmp(index->id, |
260 | ++ btr_page_get_index_id(page))); |
261 | ++ |
262 | ++ if (height == ULINT_UNDEFINED) { |
263 | ++ /* We are in the root node */ |
264 | ++ |
265 | ++ height = btr_page_get_level(page, mtr); |
266 | ++ } |
267 | ++ |
268 | ++ if (height == 0) { |
269 | ++ btr_cur_latch_leaves(page, space, zip_size, page_no, |
270 | ++ latch_mode, cursor, mtr); |
271 | ++ } |
272 | ++ |
273 | ++ if (is_first_rec && slot->nth_rec != ULINT_UNDEFINED) { |
274 | ++ if (height == 0) { |
275 | ++ /* must open the first rec */ |
276 | ++ page_cur_open_on_nth_user_rec(block, page_cursor, slot->nth_rec); |
277 | ++ } else { |
278 | ++ is_first_rec = page_cur_open_on_rnd_user_rec_after_nth(block, |
279 | ++ page_cursor, slot->nth_rec); |
280 | ++ } |
281 | ++ } else { |
282 | ++ is_first_rec = FALSE; |
283 | ++ page_cur_open_on_rnd_user_rec(block, page_cursor); |
284 | ++ } |
285 | ++ |
286 | ++ if (height == 0) { |
287 | ++ break; |
288 | ++ } |
289 | ++ |
290 | ++ ut_ad(height > 0); |
291 | ++ |
292 | ++ height--; |
293 | ++ slot++; |
294 | ++ |
295 | ++ node_ptr = page_cur_get_rec(page_cursor); |
296 | ++ offsets = rec_get_offsets(node_ptr, cursor->index, offsets, |
297 | ++ ULINT_UNDEFINED, &heap); |
298 | ++ /* Go to the child node */ |
299 | ++ page_no = btr_node_ptr_get_child_page_no(node_ptr, offsets); |
300 | ++ } |
301 | ++ |
302 | ++ if (UNIV_LIKELY_NULL(heap)) { |
303 | ++ mem_heap_free(heap); |
304 | ++ } |
305 | ++ |
306 | ++ return (is_first_rec); |
307 | ++} |
308 | + /*==================== B-TREE INSERT =========================*/ |
309 | + |
310 | + /*************************************************************//** |
311 | +@@ -3615,6 +3717,154 @@ |
312 | + } |
313 | + |
314 | + /*******************************************************************//** |
315 | ++Estimates the number of pages which have not null value of the key of n_cols. |
316 | ++@return estimated number of pages */ |
317 | ++UNIV_INTERN |
318 | ++ulint |
319 | ++btr_estimate_n_pages_not_null( |
320 | ++/*=========================*/ |
321 | ++ dict_index_t* index, /*!< in: index */ |
322 | ++ ulint n_cols, /*!< in: The cols should be not null */ |
323 | ++ btr_path_t* path1) /*!< in: path1[BTR_PATH_ARRAY_N_SLOTS] */ |
324 | ++{ |
325 | ++ dtuple_t* tuple1; |
326 | ++ btr_path_t path2[BTR_PATH_ARRAY_N_SLOTS]; |
327 | ++ btr_cur_t cursor; |
328 | ++ btr_path_t* slot1; |
329 | ++ btr_path_t* slot2; |
330 | ++ ibool diverged; |
331 | ++ ibool diverged_lot; |
332 | ++ ulint divergence_level; |
333 | ++ ulint n_pages; |
334 | ++ ulint i; |
335 | ++ mtr_t mtr; |
336 | ++ mem_heap_t* heap; |
337 | ++ |
338 | ++ heap = mem_heap_create(n_cols * sizeof(dfield_t) |
339 | ++ + sizeof(dtuple_t)); |
340 | ++ |
341 | ++ /* make tuple1 (NULL,NULL,,,) from n_cols */ |
342 | ++ tuple1 = dtuple_create(heap, n_cols); |
343 | ++ dict_index_copy_types(tuple1, index, n_cols); |
344 | ++ |
345 | ++ for (i = 0; i < n_cols; i++) { |
346 | ++ dfield_set_null(dtuple_get_nth_field(tuple1, i)); |
347 | ++ } |
348 | ++ |
349 | ++ mtr_start(&mtr); |
350 | ++ |
351 | ++ cursor.path_arr = path1; |
352 | ++ |
353 | ++ btr_cur_search_to_nth_level(index, 0, tuple1, PAGE_CUR_G, |
354 | ++ BTR_SEARCH_LEAF | BTR_ESTIMATE, |
355 | ++ &cursor, 0, __FILE__, __LINE__, &mtr); |
356 | ++ |
357 | ++ mtr_commit(&mtr); |
358 | ++ |
359 | ++ |
360 | ++ |
361 | ++ mtr_start(&mtr); |
362 | ++ |
363 | ++ cursor.path_arr = path2; |
364 | ++ |
365 | ++ btr_cur_open_at_index_side(FALSE, index, |
366 | ++ BTR_SEARCH_LEAF | BTR_ESTIMATE, |
367 | ++ &cursor, &mtr); |
368 | ++ |
369 | ++ mtr_commit(&mtr); |
370 | ++ |
371 | ++ mem_heap_free(heap); |
372 | ++ |
373 | ++ /* We have the path information for the range in path1 and path2 */ |
374 | ++ |
375 | ++ n_pages = 1; |
376 | ++ diverged = FALSE; /* This becomes true when the path is not |
377 | ++ the same any more */ |
378 | ++ diverged_lot = FALSE; /* This becomes true when the paths are |
379 | ++ not the same or adjacent any more */ |
380 | ++ divergence_level = 1000000; /* This is the level where paths diverged |
381 | ++ a lot */ |
382 | ++ for (i = 0; ; i++) { |
383 | ++ ut_ad(i < BTR_PATH_ARRAY_N_SLOTS); |
384 | ++ |
385 | ++ slot1 = path1 + i; |
386 | ++ slot2 = path2 + i; |
387 | ++ |
388 | ++ if ((slot1 + 1)->nth_rec == ULINT_UNDEFINED |
389 | ++ || (slot2 + 1)->nth_rec == ULINT_UNDEFINED) { |
390 | ++ |
391 | ++ if (i > divergence_level + 1) { |
392 | ++ /* In trees whose height is > 1 our algorithm |
393 | ++ tends to underestimate: multiply the estimate |
394 | ++ by 2: */ |
395 | ++ |
396 | ++ n_pages = n_pages * 2; |
397 | ++ } |
398 | ++ |
399 | ++ /* Do not estimate the number of rows in the range |
400 | ++ to over 1 / 2 of the estimated rows in the whole |
401 | ++ table */ |
402 | ++ |
403 | ++ if (n_pages > index->stat_n_leaf_pages / 2) { |
404 | ++ n_pages = index->stat_n_leaf_pages / 2; |
405 | ++ |
406 | ++ /* If there are just 0 or 1 rows in the table, |
407 | ++ then we estimate all rows are in the range */ |
408 | ++ |
409 | ++ if (n_pages == 0) { |
410 | ++ n_pages = index->stat_n_leaf_pages; |
411 | ++ } |
412 | ++ } |
413 | ++ |
414 | ++ return(n_pages); |
415 | ++ } |
416 | ++ |
417 | ++ if (!diverged && slot1->nth_rec != slot2->nth_rec) { |
418 | ++ |
419 | ++ diverged = TRUE; |
420 | ++ |
421 | ++ if (slot1->nth_rec < slot2->nth_rec) { |
422 | ++ n_pages = slot2->nth_rec - slot1->nth_rec; |
423 | ++ |
424 | ++ if (n_pages > 1) { |
425 | ++ diverged_lot = TRUE; |
426 | ++ divergence_level = i; |
427 | ++ } |
428 | ++ } else { |
429 | ++ /* Maybe the tree has changed between |
430 | ++ searches */ |
431 | ++ |
432 | ++ return(10); |
433 | ++ } |
434 | ++ |
435 | ++ } else if (diverged && !diverged_lot) { |
436 | ++ |
437 | ++ if (slot1->nth_rec < slot1->n_recs |
438 | ++ || slot2->nth_rec > 1) { |
439 | ++ |
440 | ++ diverged_lot = TRUE; |
441 | ++ divergence_level = i; |
442 | ++ |
443 | ++ n_pages = 0; |
444 | ++ |
445 | ++ if (slot1->nth_rec < slot1->n_recs) { |
446 | ++ n_pages += slot1->n_recs |
447 | ++ - slot1->nth_rec; |
448 | ++ } |
449 | ++ |
450 | ++ if (slot2->nth_rec > 1) { |
451 | ++ n_pages += slot2->nth_rec - 1; |
452 | ++ } |
453 | ++ } |
454 | ++ } else if (diverged_lot) { |
455 | ++ |
456 | ++ n_pages = (n_pages * (slot1->n_recs + slot2->n_recs)) |
457 | ++ / 2; |
458 | ++ } |
459 | ++ } |
460 | ++} |
461 | ++ |
462 | ++/*******************************************************************//** |
463 | + Estimates the number of different key values in a given index, for |
464 | + each n-column prefix of the index where n <= dict_index_get_n_unique(index). |
465 | + The estimates are stored in the array index->stat_n_diff_key_vals. |
466 | +@@ -3646,9 +3896,30 @@ |
467 | + mem_heap_t* heap = NULL; |
468 | + ulint* offsets_rec = NULL; |
469 | + ulint* offsets_next_rec = NULL; |
470 | ++ btr_path_t first_rec_path[BTR_PATH_ARRAY_N_SLOTS]; |
471 | ++ ulint effective_pages; /* effective leaf pages */ |
472 | + |
473 | + n_cols = dict_index_get_n_unique(index); |
474 | + |
475 | ++ if (srv_innodb_stats_method == SRV_STATS_NULLS_IGNORED_EXACT) { |
476 | ++ /* estimate effective pages and path for the first effective record */ |
477 | ++ /* TODO: make it work also for n_cols > 1. */ |
478 | ++ effective_pages = btr_estimate_n_pages_not_null(index, |
479 | ++ 1 /*k*/, |
480 | ++ first_rec_path); |
481 | ++ |
482 | ++ if (!effective_pages) { |
483 | ++ for (j = 0; j <= n_cols; j++) { |
484 | ++ index->stat_n_diff_key_vals[j] = (ib_int64_t)index->stat_n_leaf_pages; |
485 | ++ } |
486 | ++ return; |
487 | ++ } else if (effective_pages > index->stat_n_leaf_pages) { |
488 | ++ effective_pages = index->stat_n_leaf_pages; |
489 | ++ } |
490 | ++ } else { |
491 | ++ effective_pages = index->stat_n_leaf_pages; |
492 | ++ } |
493 | ++ |
494 | + heap = mem_heap_create((sizeof *n_diff + sizeof *n_not_null) |
495 | + * (n_cols + 1) |
496 | + + dict_index_get_n_fields(index) |
497 | +@@ -3668,6 +3939,7 @@ |
498 | + * sizeof *n_not_null); |
499 | + /* fall through */ |
500 | + |
501 | ++ case SRV_STATS_NULLS_IGNORED_EXACT: |
502 | + case SRV_STATS_NULLS_UNEQUAL: |
503 | + /* for both SRV_STATS_NULLS_IGNORED and SRV_STATS_NULLS_UNEQUAL |
504 | + case, we will treat NULLs as unequal value */ |
505 | +@@ -3684,9 +3956,9 @@ |
506 | + |
507 | + /* It makes no sense to test more pages than are contained |
508 | + in the index, thus we lower the number if it is too high */ |
509 | +- if (srv_stats_sample_pages > index->stat_index_size) { |
510 | +- if (index->stat_index_size > 0) { |
511 | +- n_sample_pages = index->stat_index_size; |
512 | ++ if (srv_stats_sample_pages > effective_pages) { |
513 | ++ if (effective_pages > 0) { |
514 | ++ n_sample_pages = effective_pages; |
515 | + } else { |
516 | + n_sample_pages = 1; |
517 | + } |
518 | +@@ -3697,9 +3969,22 @@ |
519 | + /* We sample some pages in the index to get an estimate */ |
520 | + |
521 | + for (i = 0; i < n_sample_pages; i++) { |
522 | ++ ibool is_first_page = TRUE; |
523 | + mtr_start(&mtr); |
524 | + |
525 | +- btr_cur_open_at_rnd_pos(index, BTR_SEARCH_LEAF, &cursor, &mtr); |
526 | ++ if (srv_innodb_stats_method == SRV_STATS_NULLS_IGNORED_EXACT) { |
527 | ++ is_first_page = |
528 | ++ btr_cur_open_at_rnd_pos_after_path(index, |
529 | ++ BTR_SEARCH_LEAF, |
530 | ++ first_rec_path, |
531 | ++ &cursor, |
532 | ++ &mtr); |
533 | ++ } else { |
534 | ++ btr_cur_open_at_rnd_pos(index, |
535 | ++ BTR_SEARCH_LEAF, |
536 | ++ &cursor, |
537 | ++ &mtr); |
538 | ++ } |
539 | + |
540 | + /* Count the number of different key values for each prefix of |
541 | + the key on this index page. If the prefix does not determine |
542 | +@@ -3714,7 +3999,14 @@ |
543 | + } |
544 | + ut_a(page); |
545 | + |
546 | +- rec = page_rec_get_next(page_get_infimum_rec(page)); |
547 | ++ if (srv_innodb_stats_method == SRV_STATS_NULLS_IGNORED_EXACT && |
548 | ++ is_first_page) { |
549 | ++ /* the cursor should be the first record of the page. */ |
550 | ++ /* Counting should be started from here. */ |
551 | ++ rec = btr_cur_get_rec(&cursor); |
552 | ++ } else { |
553 | ++ rec = page_rec_get_next(page_get_infimum_rec(page)); |
554 | ++ } |
555 | + |
556 | + if (!page_rec_is_supremum(rec)) { |
557 | + not_empty_flag = 1; |
558 | +@@ -3810,8 +4102,11 @@ |
559 | + for (j = 0; j <= n_cols; j++) { |
560 | + index->stat_n_diff_key_vals[j] |
561 | + = BTR_TABLE_STATS_FROM_SAMPLE( |
562 | +- n_diff[j], index, n_sample_pages, |
563 | +- total_external_size, not_empty_flag); |
564 | ++ n_diff[j], |
565 | ++ effective_pages, |
566 | ++ n_sample_pages, |
567 | ++ total_external_size, |
568 | ++ not_empty_flag); |
569 | + |
570 | + /* If the tree is small, smaller than |
571 | + 10 * n_sample_pages + total_external_size, then |
572 | +@@ -3821,7 +4116,7 @@ |
573 | + different key values, or even more. Let us try to approximate |
574 | + that: */ |
575 | + |
576 | +- add_on = index->stat_n_leaf_pages |
577 | ++ add_on = effective_pages |
578 | + / (10 * (n_sample_pages |
579 | + + total_external_size)); |
580 | + |
581 | +@@ -3838,8 +4133,24 @@ |
582 | + if (n_not_null != NULL && (j < n_cols)) { |
583 | + index->stat_n_non_null_key_vals[j] = |
584 | + BTR_TABLE_STATS_FROM_SAMPLE( |
585 | +- n_not_null[j], index, n_sample_pages, |
586 | +- total_external_size, not_empty_flag); |
587 | ++ n_not_null[j], |
588 | ++ effective_pages, |
589 | ++ n_sample_pages, |
590 | ++ total_external_size, |
591 | ++ not_empty_flag); |
592 | ++ } |
593 | ++ |
594 | ++ if (srv_innodb_stats_method == SRV_STATS_NULLS_IGNORED_EXACT) { |
595 | ++ /* index->stat_n_diff_key_vals[k] is used for calc |
596 | ++ rec_per_key, as |
597 | ++ "stats.records / index->stat_n_diff_key_vals[x]". |
598 | ++ So it should be adjusted to the value which is based |
599 | ++ on whole of the index. |
600 | ++ */ |
601 | ++ index->stat_n_diff_key_vals[j] = |
602 | ++ index->stat_n_diff_key_vals[j] |
603 | ++ * (ib_int64_t)index->stat_n_leaf_pages |
604 | ++ / (ib_int64_t)effective_pages; |
605 | + } |
606 | + } |
607 | + |
608 | +--- a/storage/innodb_plugin/include/page0cur.h |
609 | ++++ b/storage/innodb_plugin/include/page0cur.h |
610 | +@@ -293,6 +293,22 @@ |
611 | + /*==========================*/ |
612 | + buf_block_t* block, /*!< in: page */ |
613 | + page_cur_t* cursor);/*!< out: page cursor */ |
614 | ++ |
615 | ++UNIV_INTERN |
616 | ++void |
617 | ++page_cur_open_on_nth_user_rec( |
618 | ++/*==========================*/ |
619 | ++ buf_block_t* block, /*!< in: page */ |
620 | ++ page_cur_t* cursor, /*!< out: page cursor */ |
621 | ++ ulint nth); |
622 | ++ |
623 | ++UNIV_INTERN |
624 | ++ibool |
625 | ++page_cur_open_on_rnd_user_rec_after_nth( |
626 | ++/*==========================*/ |
627 | ++ buf_block_t* block, /*!< in: page */ |
628 | ++ page_cur_t* cursor, /*!< out: page cursor */ |
629 | ++ ulint nth); |
630 | + #endif /* !UNIV_HOTBACKUP */ |
631 | + /***********************************************************//** |
632 | + Parses a log record of a record insert on a page. |
633 | +--- a/storage/innodb_plugin/include/srv0srv.h |
634 | ++++ b/storage/innodb_plugin/include/srv0srv.h |
635 | +@@ -425,7 +425,11 @@ |
636 | + for innodb_stats_method */ |
637 | + SRV_STATS_NULLS_UNEQUAL, /* All NULL values are treated as |
638 | + NOT equal. */ |
639 | +- SRV_STATS_NULLS_IGNORED /* NULL values are ignored */ |
640 | ++ SRV_STATS_NULLS_IGNORED, /* NULL values are ignored */ |
641 | ++ SRV_STATS_NULLS_IGNORED_EXACT /* NULL values are ignored exact |
642 | ++ (direct skip of null pages) |
643 | ++ (limited by key with length 1) |
644 | ++ */ |
645 | + }; |
646 | + |
647 | + typedef enum srv_stats_method_name_enum srv_stats_method_name_t; |
648 | +--- a/storage/innodb_plugin/page/page0cur.c |
649 | ++++ b/storage/innodb_plugin/page/page0cur.c |
650 | +@@ -564,6 +564,76 @@ |
651 | + } while (rnd--); |
652 | + } |
653 | + |
654 | ++ |
655 | ++UNIV_INTERN |
656 | ++void |
657 | ++page_cur_open_on_nth_user_rec( |
658 | ++/*==========================*/ |
659 | ++ buf_block_t* block, /*!< in: page */ |
660 | ++ page_cur_t* cursor, /*!< out: page cursor */ |
661 | ++ ulint nth) |
662 | ++{ |
663 | ++ ulint n_recs = page_get_n_recs(buf_block_get_frame(block)); |
664 | ++ |
665 | ++ page_cur_set_before_first(block, cursor); |
666 | ++ |
667 | ++ if (UNIV_UNLIKELY(n_recs == 0)) { |
668 | ++ |
669 | ++ return; |
670 | ++ } |
671 | ++ |
672 | ++ nth--; |
673 | ++ |
674 | ++ if (nth >= n_recs) { |
675 | ++ nth = n_recs - 1; |
676 | ++ } |
677 | ++ |
678 | ++ do { |
679 | ++ page_cur_move_to_next(cursor); |
680 | ++ } while (nth--); |
681 | ++} |
682 | ++ |
683 | ++ |
684 | ++UNIV_INTERN |
685 | ++ibool |
686 | ++page_cur_open_on_rnd_user_rec_after_nth( |
687 | ++/*==========================*/ |
688 | ++ buf_block_t* block, /*!< in: page */ |
689 | ++ page_cur_t* cursor, /*!< out: page cursor */ |
690 | ++ ulint nth) |
691 | ++{ |
692 | ++ ulint rnd; |
693 | ++ ulint n_recs = page_get_n_recs(buf_block_get_frame(block)); |
694 | ++ ibool ret; |
695 | ++ |
696 | ++ page_cur_set_before_first(block, cursor); |
697 | ++ |
698 | ++ if (UNIV_UNLIKELY(n_recs == 0)) { |
699 | ++ |
700 | ++ return (FALSE); |
701 | ++ } |
702 | ++ |
703 | ++ nth--; |
704 | ++ |
705 | ++ if (nth >= n_recs) { |
706 | ++ nth = n_recs - 1; |
707 | ++ } |
708 | ++ |
709 | ++ rnd = (ulint) (nth + page_cur_lcg_prng() % (n_recs - nth)); |
710 | ++ |
711 | ++ if (rnd == nth) { |
712 | ++ ret = TRUE; |
713 | ++ } else { |
714 | ++ ret = FALSE; |
715 | ++ } |
716 | ++ |
717 | ++ do { |
718 | ++ page_cur_move_to_next(cursor); |
719 | ++ } while (rnd--); |
720 | ++ |
721 | ++ return (ret); |
722 | ++} |
723 | ++ |
724 | + /***********************************************************//** |
725 | + Writes the log record of a record insert on a page. */ |
726 | + static |
727 | +--- a/storage/innodb_plugin/handler/ha_innodb.cc |
728 | ++++ b/storage/innodb_plugin/handler/ha_innodb.cc |
729 | +@@ -216,6 +216,7 @@ |
730 | + "nulls_equal", |
731 | + "nulls_unequal", |
732 | + "nulls_ignored", |
733 | ++ "nulls_ignored_exact", |
734 | + NullS |
735 | + }; |
736 | + |
737 | |
738 | === modified file 'patches/series' |
739 | --- patches/series 2011-11-17 12:41:18 +0000 |
740 | +++ patches/series 2012-01-03 14:13:50 +0000 |
741 | @@ -86,3 +86,4 @@ |
742 | bug54127.patch |
743 | replication_slave_skip_columns.patch |
744 | mysql-test-rnt.diff |
745 | +bug63320.patch |
If upstream analysis is correct, then this patch will be short-lived.
When you say "return back percona algorithm", do you mean that there was an old implementation? Can you give branch/revno for it?