Performance

Magento Database Bloat: Tables That Grow Without Limit

Magento never prunes most log, report, session, and quote tables. Here is how to find the tables eating your database, measure growth, and clean them safely.

Jason Schuman · February 1, 2026

Magento database growth is not all business data

A Magento database grows every day the store runs. Some growth is expected business data, such as orders, customers, and products. Other growth comes from log, report, session, and quote data that Magento writes often and may keep for a long time.

This article shows which tables can grow without a useful limit, how to measure the space they use, and how to clean them without breaking the store.

You will see how to rank tables with SQL, separate current size from growth rate, and build a cleanup routine that prevents the same problem from returning.

a big database isn't always a busy store

A large database can hide operational data

During an audit, a store that feels slow or expensive to host often has a database much larger than its order history suggests. For example, 80,000 lifetime orders do not by themselves explain a 60GB database.

The extra space often sits in a small group of tables that hold temporary or operational data. These tables record activity, but the rows can remain for years if no cleanup process removes them.

Magento 2 has no general log:clean command the way Magento 1 did. Most of these tables are never pruned unless you configure a cron to do it or delete the rows yourself.

Database bloat and disk logs are separate problems

Before looking at tables, separate two problems that people often combine. The bloat covered here lives in MySQL or MariaDB, inside the tables Magento writes to.

Log files live on the filesystem under var/log. Examples include system.log, debug.log, and exception.log. These files can fill the disk too, especially when debug logging remains enabled in production.

Both problems belong in a storage audit, but they use different cleanup methods. This article focuses on database tables. Files under var/log need their own retention and rotation routine.

Which table groups usually grow

Table groups grow for different reasons. The group tells you what the data represents and how carefully you need to clean it:

  • Report and behavior tables, such as recently viewed products, compared products, and report events. Customer activity writes these rows, but Magento may not remove them automatically.
  • Quote and cart tables, where quote and its child tables hold carts, including bot traffic and abandoned carts, until a cron job removes expired records.
  • Session and operational tables, including database-backed sessions, asynchronous bulk operations, and Admin notifications. Traffic and background work grow these tables even when sales do not increase.
  • Scheduling and import tables, such as cron_schedule and legacy dataflow tables. These can fill when cron is misconfigured or imports run repeatedly.

These groups account for much of the bloat found in Magento audits. The largest table differs by store, so measure first and change nothing until you know what the rows represent.

Report tables are common sources of bloat

The Reports module records customer behavior in several tables. Each session can add more rows, so these tables can become the largest part of a busy store's database.

report_event and recently viewed data

Check report_event, report_viewed_product_index, report_compared_product_index, and catalog_product_frontend_action. Product views, comparisons, and "recently viewed" actions write rows to these tables.

A default installation does not automatically clean all of this history. After three years, a store can hold tens of millions of rows in report_event alone.

When truncating report tables is safe

These report tables are usually safe to truncate because they hold derived behavior data, not orders or customers. Truncating them removes the "recently viewed" and "compared products" blocks and the related behavior reports in the Admin.

If the store does not use those features, truncating the tables can produce a large, immediate size reduction. Confirm that the frontend does not display recently viewed products before truncating.

Stop report_event from filling again

Truncating report_event without stopping new writes only clears the current rows. Observers in the Magento_Reports module repopulate the table when customers view products, add items to carts, use wishlists, or compare products.

If the store does not use behavioral reports or the recently viewed and compared products blocks, disable the module at the source with bin/magento module:disable Magento_Reports. This stops the writes and removes the Admin Reports section that depends on them.

Disabling the module removes features. Confirm that the merchandising team does not rely on product-view or bestsellers reports before making that change.

On many stores the largest tables are behavior and session data, not orders.

Quote tables grow from carts that never become orders

The quote table stores one row for each cart a visitor starts. Its child tables, quote_item, quote_address, quote_payment, and quote_item_option, add rows for the items, addresses, payment data, and options in those carts.

Most carts never become orders. Bots, price scrapers, and abandoned shopping can create quote rows that remain until cleanup runs.

Magento provides the clean_expired_quotes cron job for this task. It deletes quotes older than the lifetime set in checkout/cart/delete_quote_after, which is measured in days.

If quote tables are huge, check two things before deleting rows manually: whether cron runs clean_expired_quotes, and whether the quote lifetime is longer than the business needs.

Sessions and operational tables add more rows

If app/etc/env.php sets the session save handler to db, Magento stores sessions in the session table. The table grows with visitors and depends on garbage collection to remove old sessions.

For a store with significant traffic, Redis or Valkey is usually a better session backend. It keeps session data out of the Magento database and lets the database focus on business data.

Other operational tables also grow during normal work. magento_operation and related bulk tables store asynchronous and message-queue operations. adminnotification_inbox stores Adobe notifications received by the store.

cron_schedule should stay small

The cron_schedule table records scheduled jobs that Magento plans and runs. On a healthy store, Magento removes old history so the table stays reasonably small.

Magento configures this cleanup under Stores, Configuration, Advanced, System, Cron. Each cron group has a history lifetime for successful and failed jobs, plus a schedule that controls how often old history is pruned.

If cron_schedule holds hundreds of thousands of rows, inspect the maintenance job, the job schedules, and the history lifetime. Cron may not be running cleanup, a job may be scheduled too often, or the lifetime may be too long.

Fix the configuration and cron health before clearing the table. If you empty it without fixing the cause, it can refill within days.

Measure which tables are actually large

Do not guess which table is causing the problem. Rank the tables by size with information_schema, which MySQL and MariaDB expose for database metadata.

SELECT table_name,
       ROUND((data_length + index_length) / 1024 / 1024, 1) AS size_mb,
       table_rows
FROM information_schema.tables
WHERE table_schema = DATABASE()
ORDER BY (data_length + index_length) DESC
LIMIT 25;

Run the query against the Magento database and inspect the largest tables. The report, quote, session, or scheduling tables discussed above may appear ahead of sales_order and catalog_product_entity.

table_rows from information_schema is an estimate for InnoDB, not an exact count. It is accurate enough to rank tables at this stage. Use a separate count query when you need an exact row total.

Separate data size from index size

The sizing query adds data_length and index_length. Look at those values separately to see what creates the table's total size.

If a table's index_length is close to or larger than its data_length, indexes make up a large part of the storage. This often appears in report_event and quote tables, or after an extension adds indexes that are no longer needed.

For a table you plan to truncate, the two values are removed together. For tables you keep, such as sales_order and catalog tables, an oversized index needs its own review.

Measure current size and growth rate

A large table shows the past. A fast-growing table shows where the store is heading. You need both measurements to choose the right fix.

Save the sizing query results in a small table or dated file. Run the query again a week later and compare each table. Divide the size change by the number of days between captures to get megabytes per day.

A report_event table that adds two million rows each week needs a different response from a large table that stays the same size. Truncating the first table without stopping its writes will only restore the bloat later.

You do not need a monitoring tool for this first trend check. A small history table and the same sizing query can show the growth:

CREATE TABLE _size_history (
    captured_at DATE,
    table_name  VARCHAR(255),
    size_mb     DECIMAL(10,1)
);

INSERT INTO _size_history
SELECT CURDATE(), table_name,
       ROUND((data_length + index_length) / 1024 / 1024, 1)
FROM information_schema.tables
WHERE table_schema = DATABASE();

Run the insert now and again in a week. For each table, divide the size difference by the days between captures. The result is the growth rate in megabytes per day.

Measuring growth over time separates a one-time cleanup from a recurring one.

Clean each table with the right method

Match the cleanup method to the data. Behavior tables may be safe to truncate. Quote and operational tables should use their intended cleanup mechanism, not a blind delete.

  • Report and recently viewed tables: TRUNCATE is acceptable after you confirm that the related storefront blocks and reports are unused.
  • Quote tables: fix the clean_expired_quotes cron job and the checkout/cart/delete_quote_after lifetime, then let cleanup remove expired carts.
  • cron_schedule: correct the cron configuration and history lifetime before clearing old rows, or the table will refill.
  • Sessions: move session storage to Redis, which makes the database table unused and stops new session rows from accumulating there.
Never truncate quote on a live store to save space. You will delete every active shopping cart, including customers mid-checkout. Clean expired quotes through cron and lifetime configuration instead.

Reclaim disk space after deleting rows

Deleting rows from an InnoDB table does not automatically return space to the operating system. The table keeps its allocated size and reuses the freed pages for future rows.

To shrink the file on disk, run OPTIMIZE TABLE. This rebuilds the table. When innodb_file_per_table is enabled, the rebuild can release the table's space back to the filesystem.

Rebuilding a large table can lock it and take time, so schedule the operation in a maintenance window. Without the rebuild, the sizing query may still show the old allocated size even though rows were deleted.

Check whether tables use their own files with SHOW VARIABLES LIKE 'innodb_file_per_table';. Expect the value ON before planning to reclaim space this way.

When the value is off, InnoDB keeps data in one shared ibdata1 file that does not shrink. OPTIMIZE TABLE will not return that shared space to the disk. Treat this as a separate database-storage project, not a quick cleanup step.

Build a repeatable cleanup routine

A one-time cleanup may help for a few months. A recurring routine keeps the database within a planned size range.

Use Magento's built-in cleanup, disable data collection the business does not use, and monitor table growth so a new source of bloat appears quickly.

  • Make sure cron is healthy so clean_expired_quotes and cron history cleanup run.
  • Set a quote lifetime that fits the business, often 14 to 30 days instead of the default.
  • If the business does not use behavioral reports, turn off report event collection so report_event stops filling.
  • Keep sessions in Redis or Valkey, not the database.
  • Re-run the sizing query on a schedule and track both current size and growth rate.

This routine uses configuration and a recurring check. It is easy to skip, so schedule it before the database becomes a hosting or backup problem.

Never clean business tables as bloat

Measure first because some large tables contain business records. Deleting rows from those tables deletes data the store needs.

Do not truncate these tables just because they are large:

  • sales_order, sales_invoice, sales_shipment, and their line-item children. These tables contain order history.
  • customer_entity and its attribute tables. These tables contain customer records.
  • The catalog_product_entity and catalog_category_entity families. These tables contain the catalog.
  • The *_grid tables, such as sales_order_grid. These tables are rebuildable, but the Admin reads them often. Reindex them through the intended process instead of truncating them blindly.

If one of these tables is too large for the store's order, customer, or catalog count, review archiving and indexing with care. That is a separate data-management problem from the operational tables discussed here.

When table bloat reaches customers

Table bloat is easy to ignore while the store still works. The database can become slower and more expensive before the storefront shows an obvious error.

Bloat becomes an incident when backups miss their window, replication falls behind the primary, the disk fills and takes the site down, or a query against a large table slows the storefront.

At that point, the repair still uses the steps described here, but the team has to run them under pressure. Measuring and cleaning on a schedule gives the team more control.

Start with a size and growth report

Run the sizing query on the store. If the largest tables contain behavior, session, or quote data, record their size and growth rate. You then have a measurable cleanup target.

Use those measurements to choose the right action: configure cleanup, disable unused data collection, move sessions to Redis or Valkey, or schedule a safe table rebuild. Include the results in the next platform performance review.