2

I have MariaDB 10.5 on my desktop with multiple disks (SSD and HDD) for write-intensive projects. Writing to a single table is fast and the percentage of dirty pages remains close to zero with 1000-3000 writes/s.

However, when I actively write to multiple tables at the same time, the percentage of dirty pages quickly goes up. The problem is that flush to the disk drops to the level of 100 writes/s and remains at that level.

This behaviour remains until a restart.

I think the problem is somehow related (not exactly) to that identified by Percona 10 years ago.

Is there any trick to keep the speed of flushing?

key_buffer_size     = 20M
max_allowed_packet  = 5G
thread_stack        = 256K
thread_cache_size       = 8
innodb_buffer_pool_size = 70G
innodb_log_buffer_size = 512M
innodb_log_file_size = 20G
innodb_thread_concurrency = 0
innodb_flush_log_at_trx_commit = 0
innodb_compression_level = 6
innodb_io_capacity=2000
innodb_io_capacity_max=30000
innodb_max_dirty_pages_pct=0
innodb_doublewrite = 0
innodb_flush_method = O_DIRECT
innodb_lru_scan_depth=128
innodb_purge_threads=8
innodb_purge_batch_size=600
innodb_flush_neighbors=0
innodb_change_buffer_max_size=50
innodb_buffer_pool_load_at_startup=OFF
innodb_buffer_pool_dump_at_shutdown=OFF
innodb-ft-result-cache-limit=4G
innodb_fatal_semaphore_wait_threshold=7200
innodb_compression_default=ON
innodb_random_read_ahead=1

UPDATE: Possible Solution

I do not post this, as I am not sure if it is the real solution. After much experimentations, I found the problem is adaptive flushing. I tackled the problem by

innodb_adaptive_flushing=0
innodb_adaptive_flushing_lwm=70

Apparently, when adaptive flushing is triggered to avoid high I/O, it stays for a long time.

UPDATE2: page vs column compression

I identified the problem to be

innodb_compression_default=ON

Following a suggestion by Rick James, I created similar tables with column compression instead of page compression. The compression is about 300% for both methods (10-20% better with page compression, as applies to the whole table rather than selective columns), but the performance was significantly different on HDD.

I think the problem is when writing to multiple sparse files created by page compression at the same time on an HDD (it should not be an issue on SSD).

I need to recreate all the tables to be sure, and the process is painfully time-consuming.

4
  • How many cores? How much RAM?
    – Rick James
    Commented Oct 8, 2021 at 20:39
  • @RickJames 16/32 c/t with 128GB RAM.
    – Googlebot
    Commented Oct 8, 2021 at 23:09
  • There has been some development work with adaptive flushing. I'm not sure which 10.5 version but maybe one of them has been fixed. Or if not, worthy of a bug report.
    – danblack
    Commented Oct 9, 2021 at 1:02
  • ----- 2021-05-07 MariaDB 10.5.10 -- -- ----- MDEV-25093 : Adaptive flushing fails to kick in even if innodb_adaptive_flushing_lwm is hit. (possible regression) Merge Revision #559efad44e 2021-04-27 09:10:47 +0300 - Merge 10.4 into 10.5
    – Rick James
    Commented Oct 9, 2021 at 20:09

4 Answers 4

1

With MariaDB and "rows usually have long mediumtext fields", consider using column compression.

5
  • what's the benefit of column compression when page compression innodb_compression_default=ON is enabled?
    – Googlebot
    Commented Oct 8, 2021 at 23:31
  • I find that "page" compression is not very good. "row" has some benefits; "column" sounds best, especially for, say, a Mediumtext.
    – Rick James
    Commented Oct 8, 2021 at 23:33
  • @Googlebot - This makes a pretty good argument: mydbops.wordpress.com/2019/10/06/… (Of course, benchmarks rarely reflect real life.)
    – Rick James
    Commented Oct 8, 2021 at 23:36
  • If you do experiment with column compression, please report on your results -- the more anecdotes, the better.
    – Rick James
    Commented Oct 8, 2021 at 23:37
  • I think your idea of column compression can resolve the problem as I updated the equation. I will post further results of experimentation.
    – Googlebot
    Commented Oct 9, 2021 at 15:17
1

I suspect that innodb_io_capacity_max=30000 is much too large. Try 5000.

innodb_max_dirty_pages_pct=0 -- Dirty pages are good; don't try to avoid them with "0". The default is 75 (percent); MariaDB 10.5.7 decided that a better default is 90. Try one of those. Note that that setting is GLOBAL and dynamic, so no restart is needed.

By not aggressively flushing 'dirty' pages, you are providing the possibility that a block ('page') will be written to more than once before it actually needs to be written to disk.

What tool is telling you "100 writes/s" ?

For a deeper dive, please provide the Global Status and Variables: http://mysql.rjweb.org/doc.php/mysql_analysis#tuning

3
  • By writes/s, I was referring to the FILE I/O section of SHOW ENGINE INNODB STATUS \G. I don't see any benefit for dirty pages in a write-intensive system. It is beneficial to avoid unnecessary high I/O affecting reads. But when only writing, it is better to empty the buffer pool for the subsequent operations.
    – Googlebot
    Commented Oct 8, 2021 at 23:12
  • @Googlebot - Allowing dirty pages to pile up in the buffer_pool benefits reads. It is essentially a way to do "delayed" writes. How much data in the entire database? How many reads per second -- both when busy with writes and when not?
    – Rick James
    Commented Oct 8, 2021 at 23:17
  • It's a single-user database. There's no read when I write. The reads are not frequent but heavy, JOINs of millions of rows from tables with billions of rows. Or reading millions of rows with mediumtext.
    – Googlebot
    Commented Oct 8, 2021 at 23:23
1

100 disk writes per second -- That sounds like the max speed of an HDD.

100 rows written per second -- That sounds like a very inefficient way to do INSERT (or UPDATE).

autocommit = ON and not inside BEGIN..COMMIT, plus innodb_flush_log_at_trx_commit = 1 and/or sync_binlog = 1 -- That sounds like there will be a flush to the log for every statement.

Show us the "write" statement. Let's discuss how you can "batch" INSERT, thereby avoiding the flush/sync for every row.

5
  • The data come through long programming loops and processing. I considered merging INSERTs, but it adds to the complication with little improvement, particularly because the rows usually have long mediumtext fields.
    – Googlebot
    Commented Oct 8, 2021 at 23:17
  • @Googlebot - A batch insert of 100 rows runs about 10 times as fast as 100 single-row insert statements. OK, MEDIUMTEXT complicates it -- since there is a limit on size of an SQL. What kind of data is in that column?
    – Rick James
    Commented Oct 8, 2021 at 23:20
  • They are unformatted texts that I store for future parsing to extract structured data.
    – Googlebot
    Commented Oct 8, 2021 at 23:33
  • @Googlebot - If the text starts out as a file, consider leaving it as a file, then have the path to the file in the database table.
    – Rick James
    Commented Oct 9, 2021 at 19:57
  • I initially considered archiving them as text files, but, 1. it is easier to loop through one million rows rather than opening one million files. 2. backing up and moving is really difficult, 3. compression/decompression via database is easier than single files.
    – Googlebot
    Commented Oct 9, 2021 at 21:54
1

All the optimizations in InnoDB for dirty pages, redo logging, page flushing, io capacity etc. are designed with the assumption that high write traffic is intermittent. That is, it depends on there being period of low write traffic so all the deferred flushing can "catch up."

If you have a very high, continuous rate of writes, and your InnoDB engine can't keep up, then you can't keep throwing more write traffic at a single disk device. There's a hard ceiling to the amount of I/O any single disk can handle.

So if other means of optimization are tapped out, then your options may be:

  • Move to a more high-end I/O system, for example RAID0 or RAID10. Even if you have an SSD disk, that's not infinitely scalable. A striped array of SSD disks is superior to a single SSD disk.

  • Distribute writes to multiple MySQL instances, even if they are sharing the same storage. You might have the most powerful I/O array in the world, so eventually your bottleneck will be InnoDB itself.

  • Distribute writes to multiple server hosts. You gain more parallel I/O capacity by using more servers. I support some apps that need to spread out writes over hundreds of shards, running in docker containers, using between 2 and 8 containers per physical host. Whatever it takes to get enough parallelization and I/O capacity to handle the write traffic.

If you need to scale further, you may consider that MySQL being designed to optimize OLTP traffic, is not the best technology for a high rate of writes. You may want to evaluate RocksDB or ScyllaDB or other specialized log-structured data services.

3
  • Good points indeed, but my problem is not hitting the I/O capacity. The problem is that the I/O capacity falls to 1/10 of the maximum performance at a point. I suspect adaptive flushing impose the limit. I have many databases, which should be JOINed. For the sake of consistency, I have to stick to innoDB.
    – Googlebot
    Commented Oct 8, 2021 at 23:48
  • A hardware RAID controller is likely to be better than having the OS simulate striping.
    – Rick James
    Commented Oct 9, 2021 at 20:12
  • True, but I tried software-RAID to stripe the drives on a EC2 i3 instance and it was great. Commented Oct 9, 2021 at 23:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.