A store's database grows differently from a blog's. Most of the growth is not orders, and most of it is disposable, but the tables sit next to ones that must never be touched, which is why cleanup advice for stores deserves more care than the usual "optimise your database" article.
Where the size actually goes
Look before deleting anything:
SELECT table_name, ROUND(((data_length + index_length)/1024/1024),1) AS mb FROM information_schema.TABLES WHERE table_schema = DATABASE() ORDER BY (data_length + index_length) DESC LIMIT 15;
On most stores the largest tables are postmeta, the sessions table, the scheduled-actions tables, and the options table. Orders themselves are rarely the biggest thing.
Sessions
WooCommerce creates a session for every visitor who touches a cart, buyer or not. On a store with traffic that is thousands of rows a week, and the great majority belong to people who never bought anything.
WooCommerce expires them on a schedule. If that scheduled task is not running, they accumulate indefinitely, and a scheduled task that is not running is common on low-traffic stores, because WordPress only triggers them when someone visits.
This is the single most common cause of a store database that is inexplicably large. Why scheduled tasks do not run walks through fixing the cause instead of the symptom.
Scheduled actions
WooCommerce queues background work: emails, stock adjustments, subscription events, and records each one. Completed and failed entries stay unless something removes them.
Old completed rows are safe to remove. Failed ones are worth reading first: a repeated failure is a real problem that has been happening quietly, and deleting the rows removes the only evidence.
Logs
Payment gateways and shipping plugins write logs, sometimes verbosely, sometimes forever. Check both the log table and the files in the uploads directory, where several plugins write instead.
Old logs are safe to clear. A log growing quickly is worth a look before you do; a gateway logging an error on every transaction is telling you something.
What must not be deleted
Orders, order items, customers, products, refunds. These are business records, and in many places you are required to keep them for years.
Be specific about what a cleanup tool is offering. "Delete old orders" and "delete expired sessions" appear in the same interfaces, and one of them is a compliance problem.
The middle case
Trashed orders, cancelled orders and failed payment attempts can be removed, and they accumulate quickly on stores with a lot of abandoned checkouts.
Decide a retention period deliberately (ninety days is a common choice) rather than clearing them all. Failed attempts in particular are useful when a customer says a payment did not work.
The slow admin
A WooCommerce admin that takes many seconds to list orders is usually not the server. It is postmeta: order data spread across many metadata rows, joined on every list query.
Newer WooCommerce versions store orders in dedicated tables rather than as posts and metadata, which addresses this directly. If your store is on a version that offers it and is not using it, that is the largest available improvement, and it is a migration, so take a backup first and test on staging.
Speeding up a slow WordPress admin deals with the non-store causes, and backing up a store taking live orders goes into doing it safely before a change like that.
Make it a schedule
Cleaning once fixes today. Sessions and scheduled actions start accumulating again immediately.
Set the cleanup to run regularly, confirm the scheduled tasks are actually firing, and check the table sizes again in a month. If the same table is large again, the cleanup is not running, which is a different problem from the database being large.
Cleaning up the WordPress database explains the non-store tables in the same database.
Measure before deciding anything
The advice above is general; your store's actual distribution may be different, and it takes one query to find out.
SELECT table_name, ROUND((data_length+index_length)/1024/1024,1) AS mb, table_rows FROM information_schema.TABLES WHERE table_schema = DATABASE() ORDER BY (data_length+index_length) DESC LIMIT 20;
Read both columns. A table that is large in megabytes is holding data; a table that is large in row count with modest size is holding a great many small records, and those are usually the disposable ones.
Repeat the query after any cleanup. A table that returns to the same size within a month means the cleanup is not running rather than that the data is needed.
Metadata is where the size usually is
On stores that have not moved to dedicated order tables, the post metadata table is typically the largest thing in the database, and much of it belongs to orders.
Two contributors are worth knowing about. Plugins that attach data to every order accumulate rows at the rate you take orders, forever. And plugins that have been removed leave their metadata behind, referenced by nothing.
Orphaned metadata: rows whose parent post no longer exists, is genuinely safe to remove and can be a substantial share:
SELECT COUNT(*) FROM wp_postmeta pm LEFT JOIN wp_posts p ON p.ID = pm.post_id WHERE p.ID IS NULL;
Take a backup before deleting anything that query finds. On a store that means a consistent dump instead of a copy taken while orders arrive. For the difference, see backing up a store that takes orders continuously.
Optimising tables, and when it helps
OPTIMIZE TABLE wp_postmeta;
Deleting rows does not immediately return the space to the filesystem; the table keeps the freed pages for reuse. Optimising rebuilds it and returns the space.
It is worth running after a large deletion and pointless as routine maintenance. On a large table it locks or rebuilds, so it belongs in a quiet window rather than during trading. Planning maintenance windows sets out having one.
Growth that is not the database
A store filling its account is not always the database, and checking the wrong thing wastes the most time.
du -sh ~/public_html/wp-content/uploads du -sh ~/public_html/wp-content/uploads/woocommerce_uploads find ~ -name '*.log' -size +50M 2>/dev/null
Downloadable product files, invoice PDFs generated per order, and gateway logs all grow with sales and none of them is in the database.
The file count matters as much as the size here: an invoice per order is one file per order, indefinitely. Understanding inodes deals with the limit that is reached first.