Sunday, June 28, 2020

Fun with Bugs #100 - On MySQL Bug Reports I am Subscribed to, Part XXXIV

I delayed this post #100 in the "Fun with Bugs" series for few weeks - the previous one was published 4 weeks ago. The idea was to make it the last one, and for this I needed something to celebrate. Two days ago proper event happened, we have MySQL Bug #100000 reported! Here it is:
  • Bug #100000 - "Provide an index hint that only affects the choice of index for NL join". This nice feature request was added by former optimizer developer in MySQL, Øystein Grøvlen. Hundreds of other feature requests are waiting for the attention both from the MySQL Verification Team and from developers, so good to see a feature request getting the number that nobody ever forget!
Actually Øystein Grøvlen created several interesting bug report during that day:
  • Bug #99994 - "Index range scan is chosen where table scan takes 40% less time". Clear and simple bug report that relies on the world sample database.
  • Bug #99995 - "Histogram is not used for filtering estimate when index is disabled".
  • Bug #99996 - "Prefer histogram over index statistics when eq_range_index_dive_limit is exceeded". This was verified as a feature request.
  • Bug #99997 - "Range estimates are usually off by a factor of 2 for large ranges". It was declared a duplicate of older bug report I am also subscribed to, Bug #73386 - "For ranges, innodb doubles estimates, or caps estimates to half the table". See also this MariaDB bug report, MDEV-19424 - "InnoDB's records_in_range estimates are capped at about 50%", and links from it for a lot of related discussions. Let's wait and see what vendor resolves this faster...
    The other report, Bug #99998 - "For large ranges, the range estimate will never exceed 50%", is probably also a duplicate of the same old bug.
  • Bug #99999 - "EXPLAIN FORMAT=TREE does not show cost/rows for semijoin materialization". Yet another nice and clear bug report.
So, hardly anyone else had a chance to get that #100000 filed. As far as I can see, all these were reported during a very short period of 2 minutes, from "26 Jun 7:57" till "26 Jun 7:58"! Not sure how to do this without some automation or at least all the details ready for quick copy/pasting!

Now back to some older bugs I've subscribed to over last 4 weeks:
  • Bug #99791 - "MySQL 8 orphaned table due to unchecked non-existent row format check." As reported by Marc Reilly, tables created in versions < MySQL 8 which use row_format COMPRESSED or REDUNDANT, where row_format is not set explicitly in the Table DDL allow users to create un-prefixed indexes on fields which exceed the maximum column size of 767 bytes. Upgrading to MySQL 8 do nothing with these tables, but as soon as new index is added and reboot happens, such a table becomes inaccessible. What a surprise!
  • Bug #99794 - "MySQL 57 client is inefficient at bulkloads/binlog replay". In this bug report  Marc Reilly basically asks to back port the fix from MySQL 8.0.13.
  • Bug #99800 - "ps_truncate_all_tables() does not work in super_read_only mode". This regression bug was reported by Lalit Choudhary.
  • Bug #99805 - "mysql async client is incomplete". There is no way to determine file descriptor state (should it block on read or write), so it is impossible to use it in asynchronous contexts without busy looping. This bug report by Domas Mituzas was used in one discussion as an argument that MySQL bugs database still gets proper attention from MySQL engineers. It's truly so.
  • Bug #99892 - "initialize with innodb_page_size=4096 gets "Specified key was too long" errors". This is a regression vs 5.7 (without a tag). As Mark Callaghan found out, one can not initialize MySQL 8 instance without errors with such a small innodb_page_size.
  • Bug #99924 - "The record per key value from InnoDB is not suitable when n_diff is zero". As reported by Ze Yang, due to lack of locking when server reads the innodb_rec_per_key, the n_diff value may be 0 (not set) while the table->stat_n_rows is > 0. As a result (see great comment by Øystein Grøvlen), if a table object is opened during the recalculation of statistics, the rec_per_key for a column/index may be quite misleading. It will be interpreted as all rows have the same value, and the index will probably not be chosen for any non-covering scans. There is a patch suggested (to set rec_per_key to 1 or 10 in such case), as well as other suggestion to set the value REC_PER_KEY_UNKNOWN. Useful reading!
  • Bug #99933  - "In-memory hash join will only use two-thirds of join buffer". Yet another bug report related to hash joins from Øystein Grøvlen, with a fix suggested. See also his Bug #99934 - "Hash join adds columns to the hash table that is not needed." There is a lot of work ahead to improve the implementation of this new feature in MySQL 8.
  • Bug #99935 - "innodb_doublewrite_files is not correct when innodb_buffer_pool_size > 1G". Just 2 files are created instead of 16 according to the manual. This bug was reported by Satya Bodapati.
  • Bug #99943 - "Hash join does not work for Semijoin and Antijoin". This bug report from Tibor Korocz was "Verified", but later comments suggest that it's more like wrong expectations/interpretation of cases when the feature has to be used (it is supposed to be used instead of BNL, but not instead of semijoin materialization and subquery materialization). Let's wait and see how it ends up...
  • Bug #99966 - "Switching to use NUMA-SMART Counter for statistical counters". Great bug report from Krunal Bauskar, with a patch suggested. I hope to get a NUMA system one day myself to understand the challenges and performance problems there better.  
So, that's it, my very last post in the "Fun with Bugs" series that started more than 7 years ago. The series where I listed most of the interesting bug reports I keep an eye on, since Bug #2. It was a long way with a lot of fun and a lot of (rarely appreciated) work in the process, but now my watch has ended. I am not going to try to micro manage MySQL bugs processing any more and finally let the MySQL Verification Team do their job without my regular attention. Good luck!

Percona had recently started to blog about bugs, so I am sure they will keep an eye and share lists of important bugs on a regular basis. They should really care more than I do these days.

Sunday, June 21, 2020

The Magic Behind the innodb_dedicated_server=ON

Two days ago I noted this tweet by Mark Callaghan:
"This month I learned that innodb_dedicated_server is great. Don't need to set many options beyond it. I have been doing it wrong."
and decided to check what this great setting (that I also never cared to use with MySQL 8) is really doing. I could, surely, just re-read the fine manual (and I did that), but what can be better than just trying it?

Yesterday I had some free time in the morning for testing random things and was sitting near my good old Fedora (yes, I know, time to use 32 if not 33...) test box, with 8G of RAM and 4 cores:
[openxs@fc29 8.0]$ pt-summary
# Percona Toolkit System Summary Report ######################
        Date | 2020-06-20 05:27:35 UTC (local TZ: EEST +0300)
    Hostname | fc29
      Uptime | 39 min,  1 user,  load average: 0.29, 0.37, 0.39
    Platform | Linux
     Release | Fedora release 29 (Twenty Nine)
      Kernel | 5.3.11-100.fc29.x86_64
Architecture | CPU = 64-bit, OS = 64-bit
   Threading | NPTL 2.28
    Compiler | GNU CC version 8.3.1 20190223 (Red Hat 8.3.1-2).
     SELinux | Enforcing
 Virtualized | No virtualization detected
# Processor ##################################################
  Processors | physical = 1, cores = 4, virtual = 4, hyperthreading = no
      Speeds | 1x1999.777, 1x1999.816, 1x2001.216, 1x2030.647
      Models | 4xIntel(R) Core(TM)2 Quad CPU Q8300 @ 2.50GHz
      Caches | 4x2048 KB
# Memory #####################################################
       Total | 7.7G
        Free | 247.4M
        Used | physical = 2.1G, swap allocated = 0.0, swap used = 0.0, virtual = 2.1G
      Shared | 198.6M
     Buffers | 5.4G
      Caches | 5.1G
       Dirty | 352 kB
     UsedRSS | 4.3G
  Swappiness | 60
 DirtyPolicy | 20, 10
 DirtyStatus | 0, 0
and it had MySQL 8.0.18 there (also time to rebuild from recent GitHub source, I know...). So, I tried to compare settings with --no-defaults:
[openxs@fc29 8.0]$ ./bin/mysqld_safe --no-defaults --basedir=/home/openxs/dbs/8.0 --datadir=/home/openxs/dbs/8.0/data &
[1] 3764
[openxs@fc29 8.0]$ 2020-06-20T05:21:24.289203Z mysqld_safe Logging to '/home/openxs/dbs/8.0/data/fc29.err'.
2020-06-20T05:21:24.378630Z mysqld_safe Starting mysqld daemon with databases from /home/openxs/dbs/8.0/data
[openxs@fc29 8.0]$ ./bin/mysql -uroot -e'select version()';
| version() |
| 8.0.18    |
[openxs@fc29 8.0]$ ./bin/mysql -uroot -e'show global variables' >/tmp/default.txt
[openxs@fc29 8.0]$ ./bin/mysql -uroot -e'shutdown';
[openxs@fc29 8.0]$ 2020-06-20T05:22:35.220091Z mysqld_safe mysqld from pid file /home/openxs/dbs/8.0/data/ ended
[1]+  Done                    ./bin/mysqld_safe --no-defaults --basedir=/home/openxs/dbs/8.0 --datadir=/home/openxs/dbs/8.0/data
[openxs@fc29 8.0]$ cat /tmp/default.txt | grep innodb_dedicated
innodb_dedicated_server OFF
Then I've added the magic option and repeated the same steps:
[openxs@fc29 8.0]$ ./bin/mysqld_safe --no-defaults --basedir=/home/openxs/dbs/8.0 --datadir=/home/openxs/dbs/8.0/data --innodb_dedicated_server=ON &
[1] 3993
[openxs@fc29 8.0]$ 2020-06-20T05:23:10.824899Z mysqld_safe Logging to '/home/openxs/dbs/8.0/data/fc29.err'.
2020-06-20T05:23:10.870749Z mysqld_safe Starting mysqld daemon with databases from /home/openxs/dbs/8.0/data
[openxs@fc29 8.0]$ tail -f data/fc29.err 
2020-06-20T05:23:10.870749Z mysqld_safe Starting mysqld daemon with databases from /home/openxs/dbs/8.0/data
2020-06-20T05:23:10.888077Z 0 [Warning] [MY-010139] [Server] Changed limits: max_open_files: 1024 (requested 8161)
2020-06-20T05:23:10.888097Z 0 [Warning] [MY-010142] [Server] Changed limits: table_open_cache: 431 (requested 4000)
2020-06-20T05:23:11.168384Z 0 [System] [MY-010116] [Server] /home/openxs/dbs/8.0/bin/mysqld (mysqld 8.0.18) starting as process 4117
 100 200 300 400 500
 100 200 300 400 500
 100 200 300 400 500
 100 200 300 400 500
 100 200 300 400 500
 100 200 300 400 500
2020-06-20T05:23:59.904782Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed.
2020-06-20T05:24:00.145885Z 0 [System] [MY-010931] [Server] /home/openxs/dbs/8.0/bin/mysqld: ready for connections. Version: '8.0.18'  socket: '/tmp/mysql.sock'  port: 3306  Source distribution.
2020-06-20T05:24:00.218482Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Socket: '/tmp/mysqlx.sock' bind-address: '::' port: 33060
[openxs@fc29 8.0]$ ./bin/mysql -uroot -e'show global variables' >/tmp/dedicated.txt
So, what was the real difference? Here it is, thanks diff:
[openxs@fc29 8.0]$ diff -u /tmp/default.txt /tmp/dedicated.txt 
--- /tmp/default.txt    2020-06-20 08:22:25.674368791 +0300
+++ /tmp/dedicated.txt  2020-06-20 08:24:40.938561950 +0300
@@ -130,11 +130,11 @@
 innodb_buffer_pool_dump_pct    25
 innodb_buffer_pool_filename    ib_buffer_pool
 innodb_buffer_pool_in_core_file    ON
-innodb_buffer_pool_instances   1
+innodb_buffer_pool_instances   8
 innodb_buffer_pool_load_abort  OFF
 innodb_buffer_pool_load_at_startup ON
 innodb_buffer_pool_load_now    OFF
-innodb_buffer_pool_size    134217728
+innodb_buffer_pool_size    6442450944
 innodb_change_buffer_max_size  25
 innodb_change_buffering    all
 innodb_checksum_algorithm  crc32
@@ -147,7 +147,7 @@
 innodb_data_file_path  ibdata1:12M:autoextend
 innodb_deadlock_detect ON
-innodb_dedicated_server    OFF
+innodb_dedicated_server    ON
 innodb_default_row_format  dynamic
 innodb_disable_sort_file_cache OFF
@@ -157,7 +157,7 @@
 innodb_fill_factor 100
 innodb_flush_log_at_timeout    1
 innodb_flush_log_at_trx_commit 1
-innodb_flush_method    fsync
+innodb_flush_method    O_DIRECT_NO_FSYNC
 innodb_flush_neighbors 0
 innodb_flush_sync  ON
 innodb_flushing_avg_loops  30
@@ -183,8 +183,8 @@
 innodb_log_buffer_size 16777216
 innodb_log_checksums   ON
 innodb_log_compressed_pages    ON
-innodb_log_file_size   50331648
-innodb_log_files_in_group  2
+innodb_log_file_size   536870912
+innodb_log_files_in_group  6
 innodb_log_group_home_dir  ./
 innodb_log_spin_cpu_abs_lwm    80
 innodb_log_spin_cpu_pct_hwm    50
@@ -205,7 +205,7 @@
 innodb_online_alter_log_max_size   134217728
 innodb_open_files  431
 innodb_optimize_fulltext_only  OFF
-innodb_page_cleaners   1
+innodb_page_cleaners   4
 innodb_page_size   16384
 innodb_parallel_read_threads   4
 innodb_print_all_deadlocks OFF
[openxs@fc29 8.0]$ 
Actually, fine manual page that Mark Callaghan pointed to, explain most of these changes:
"When innodb_dedicated_server is enabled, InnoDB automatically configures the following variables:
Only consider enabling innodb_dedicated_server if the MySQL instance resides on a dedicated server where it can use all available system resources."
It described the settings in details. For systems with >4G of RAM (like mine) 75% of RAM is allocated for innodb_buffer_poll_size. Then if innodb_buffer_pool_size <= 8G, innodb_log_file_size is set to 512M. Number of redo log files in group is explained properly:
"The number of log files is configured according to the automatically configured buffer pool size (in gigabytes)."
That's why we see 6G for innodb_buffer_pool_size (75% of almost 8G) and 6 for innodb_log_files_in_group. I am not sure if that many (or more than 3) log files make any practical sense or help with anything, but probably it's a way to make sure total capacity of redo log is "large enough", while each log is not larger than 2048M.

As for setting innodb_flush_method, the value of O_DIRECT_NO_FSYNC is questionable, because of a known bug, Bug #94912 - "O_DIRECT_NO_FSYNC possible write hole". The manual is clear about this case:

"Data loss is possible if redo log files and data files reside on different storage devices, and a crash occurs before data file writes are flushed from a device cache that is not battery-backed. If you use or intend to use different storage devices for redo log files and data files, and your data files reside on a device with a cache that is not battery-backed, use O_DIRECT instead."
But I am not yet sure if the implementation is smart enough NOT to make this setting when redo logs are stored on different filesystem. Something to check later.

You may ask why setting innodb_page_cleaners to 4 is  NOT documented? Well, it is the default setting and the reason why it was adjusted to 1 in my initial configuration is explained in the manual here:
"If the number of page cleaner threads exceeds the number of buffer pool instances, innodb_page_cleaners is automatically set to the same value as innodb_buffer_pool_instances. "
I had one buffer pool instance by default, as default innodb_buffer_pool_size was < 1G. As a result of this magic tuning it's 6G, so 8 instances are used (and we can use 4 innodb_page_cleaners). Again, a questionable (but default) setting.

The default value of innodb_page_cleaners, 4, is not the best fit for all. As recent Percona blog stated:
"With 4 cleaner threads, InnoDB is able to flush at a very high rate. Actually, unless you are using Percona Server for MySQL with the parallel doublewrite buffers feature, very likely the doublewrite buffer will bottleneck before the cleaner threads."
After reading the manual pages related to the feature, I found it well documented. The only formal problem was reported as Bug #99945 - "Missing details in the description of innodb_dedicated_server".

Sometimes we have to clarify the details of some MySQL features, to find out there is no real magic behind them.
To summarize:
  • I would not say that innodb_dedicated_server setting replaces setting of really many options. Just 4 by the manual and 6 in my corner case that compared to --no-defaults settings.
  • Some settings are obvious and good (use 75% of RAM for InnoDB buffer pool if RAM is large enough, use reasonably large redo log space, others are somewhat questionable. The company I work for moves to the opposite direction with them for version 10.5.
  • The feature is well documented.
  • Personally I do not think that doing one setting instead of 4-6, for key InnoDB variables, is such a big deal for any use cases besides some artificial benchmarks.