π What You'll Learn
- What WordPress actually is under the hood (beyond the marketing pitch)
- How WordPress structures its database β and why it's built that way
- What database engine WordPress runs on
- Every core table explained with real-world examples
- Which admin panel settings map to which tables (side-by-side)
- Performance and security teaser for the next article
π What is WordPress?
Let's be real β most developers have a short answer ready: "It's a blogging platform." But that undersells it massively.
WordPress is an open-source Content Management System (CMS) built with PHP and backed by a MySQL-compatible database. It powers roughly 43% of all websites on the internet as of 2024. That's not a CMS anymore β that's infrastructure.
At its core, WordPress is a framework for managing content, users, settings, and relationships between all of them. When you install WordPress, it doesn't just unzip some files β it creates an entire relational database schema with 12 default tables that handle everything from your blog post drafts to your plugin options.
Here's what makes WordPress interesting from a developer's perspective:
- It separates content from presentation β posts, pages, and custom post types live in the DB; themes only control how they look
- It's hook-driven β the database structure is designed to be extended without modification
-
It stores almost everything as key-value metadata β giving it tremendous flexibility at the cost of query complexity
> π‘ Developer Insight: WordPress uses the Entity-Attribute-Value (EAV) pattern for metadata (
postmeta,usermeta,options). This makes it incredibly extensible but also means you'll often see wide tables replaced by tall ones.
π§± Database Fundamentals of WordPress
Before we jump into tables, let's set the stage with some foundational concepts.
The WordPress Database Prefix
Every WordPress table has a configurable prefix β by default, wp_. This is defined in wp-config.php:
$table_prefix = 'wp_';
Why does this matter?
-
Security: Changing the prefix from
wp_makes SQL injection attacks targeting default table names harder -
Multisite: WordPress Multisite creates additional tables for each site (e.g.,
wp_2_posts,wp_3_posts) - Multiple Installs: You can run multiple WordPress installs in the same database using different prefixes ### WPDB β The WordPress Database Abstraction Layer
WordPress doesn't let you write raw SQL queries directly (well, you can, but you shouldn't). It provides the global $wpdb object β an instance of the wpdb class that abstracts all database interactions:
global $wpdb;
// Safe query with placeholder
$results = $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM {$wpdb->posts} WHERE post_status = %s LIMIT %d",
'publish',
10
)
);
The $wpdb object also gives you named table properties so you never hardcode table names:
| Property | Resolves To |
|---|---|
$wpdb->posts |
wp_posts |
$wpdb->postmeta |
wp_postmeta |
$wpdb->users |
wp_users |
$wpdb->options |
wp_options |
π¬ What Database Does WordPress Use?
WordPress officially uses MySQL or its compatible forks:
| Engine | Compatibility | Notes |
|---|---|---|
| MySQL | β Full | The default and most tested |
| MariaDB | β Full | Drop-in replacement, used by most hosts |
| Percona Server | β Full | MySQL fork with performance enhancements |
| SQLite | β οΈ Experimental | Supported via plugin (since WP 6.1 experimental) |
| PostgreSQL | β Not Official | Possible via third-party plugins only |
Minimum Requirements (as of WordPress 6.x)
- MySQL: 5.7.0 or higher
- MariaDB: 10.3 or higher
- PHP: 7.4+ (8.0+ recommended) ### Storage Engine: InnoDB vs MyISAM
Modern WordPress installations use InnoDB as the default storage engine β and for good reason:
| Feature | InnoDB | MyISAM |
|---|---|---|
| Foreign keys | β | β |
| Transactions | β | β |
| Row-level locking | β | β (table-level) |
| Full-text search | β (MySQL 5.6+) | β |
| Crash recovery | β | β οΈ Limited |
Why this matters: If you're running a high-traffic site or using WooCommerce, InnoDB's row-level locking is critical. MyISAM locks the entire table on writes β a problem when multiple users are checking out simultaneously.
π All WordPress Core Tables β Explained with Examples
Here's a complete walkthrough of every default table WordPress creates.
1. wp_posts β The Heart of WordPress
This is the most important table in the entire WordPress ecosystem. Almost everything in WordPress is a post.
Schema overview:
CREATE TABLE wp_posts (
ID BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
post_author BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
post_date DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
post_date_gmt DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
post_content LONGTEXT NOT NULL,
post_title TEXT NOT NULL,
post_excerpt TEXT NOT NULL,
post_status VARCHAR(20) NOT NULL DEFAULT 'publish',
comment_status VARCHAR(20) NOT NULL DEFAULT 'open',
ping_status VARCHAR(20) NOT NULL DEFAULT 'open',
post_name VARCHAR(200) NOT NULL DEFAULT '',
post_modified DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
post_type VARCHAR(20) NOT NULL DEFAULT 'post',
PRIMARY KEY (ID)
);
What post_type stores:
post_type Value |
What It Represents |
|---|---|
post |
Blog posts |
page |
Static pages |
attachment |
Media uploads |
revision |
Auto-saved revisions |
nav_menu_item |
Navigation menu items |
custom_css |
Customizer CSS |
product |
WooCommerce products |
any_cpt |
Your custom post types |
Real-world example:
global $wpdb;
// Get all published posts with their authors
$posts = $wpdb->get_results(
$wpdb->prepare(
"SELECT ID, post_title, post_status, post_type
FROM {$wpdb->posts}
WHERE post_type = %s
AND post_status = %s
ORDER BY post_date DESC
LIMIT %d",
'post',
'publish',
5
)
);
2. wp_postmeta β The Post's Extra Drawers
If wp_posts is the filing cabinet, wp_postmeta is all the sticky notes attached to each folder. Every piece of additional data tied to a post lives here.
CREATE TABLE wp_postmeta (
meta_id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
post_id BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
meta_key VARCHAR(255) DEFAULT NULL,
meta_value LONGTEXT,
PRIMARY KEY (meta_id),
KEY post_id (post_id),
KEY meta_key (meta_key(191))
);
What lives in wp_postmeta:
meta_key |
Example Value | Set By |
|---|---|---|
_thumbnail_id |
42 |
WordPress core (featured image) |
_edit_lock |
1712345678:1 |
Block editor |
_wp_page_template |
page-full-width.php |
Theme |
_price |
29.99 |
WooCommerce |
_yoast_wpseo_title |
My Custom SEO Title |
Yoast SEO |
Real-world example:
// Get all posts that have a featured image set
$posts_with_thumbnail = $wpdb->get_col(
"SELECT DISTINCT post_id
FROM {$wpdb->postmeta}
WHERE meta_key = '_thumbnail_id'"
);
β οΈ Performance Note: The
meta_keyindex is only 191 characters long (not 255). This is a UTF-8 MB4 limitation. Long meta keys won't be indexed fully β keep your custom meta keys short and consistent.
3. wp_users β The User Registry
This table stores the core identity of every user on your WordPress site.
CREATE TABLE wp_users (
ID BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
user_login VARCHAR(60) NOT NULL DEFAULT '',
user_pass VARCHAR(255) NOT NULL DEFAULT '',
user_nicename VARCHAR(50) NOT NULL DEFAULT '',
user_email VARCHAR(100) NOT NULL DEFAULT '',
user_url VARCHAR(100) NOT NULL DEFAULT '',
user_registered DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
user_activation_key VARCHAR(255) NOT NULL DEFAULT '',
user_status INT(11) NOT NULL DEFAULT 0,
display_name VARCHAR(250) NOT NULL DEFAULT '',
PRIMARY KEY (ID),
KEY user_login_key (user_login),
KEY user_nicename (user_nicename),
KEY user_email (user_email)
);
π Security Note:
user_passstores a hashed password using WordPress'swp_hash_password()function (based on phpass). Never store or compare plain text passwords β always usewp_check_password().
Real-world example:
// Get users registered in the last 30 days
$new_users = $wpdb->get_results(
$wpdb->prepare(
"SELECT ID, user_login, user_email, user_registered
FROM {$wpdb->users}
WHERE user_registered >= DATE_SUB(NOW(), INTERVAL %d DAY)
ORDER BY user_registered DESC",
30
)
);
4. wp_usermeta β User Preferences and Roles
Just like wp_postmeta extends posts, wp_usermeta extends users.
CREATE TABLE wp_usermeta (
umeta_id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
meta_key VARCHAR(255) DEFAULT NULL,
meta_value LONGTEXT,
PRIMARY KEY (umeta_id),
KEY user_id (user_id),
KEY meta_key (meta_key(191))
);
Key rows stored here:
meta_key |
Example meta_value
|
Meaning |
|---|---|---|
wp_capabilities |
a:1:{s:13:"administrator";b:1;} |
User role |
wp_user_level |
10 |
Legacy user level |
session_tokens |
(serialized array) | Active login sessions |
dismissed_wp_pointers |
wp111_privacy |
Admin UI tips dismissed |
show_admin_bar_front |
true |
Admin bar visibility |
π‘ Interesting Fact: User roles are stored as serialized PHP arrays in
wp_usermeta. The key is{prefix}capabilities. This is why switching table prefixes can break user roles β the key name changes!
5. wp_options β The Site's Brain
This is arguably the most queried table in WordPress on every single page load. Every WordPress setting, every plugin configuration, every theme mod β it all ends up here.
CREATE TABLE wp_options (
option_id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
option_name VARCHAR(191) NOT NULL DEFAULT '',
option_value LONGTEXT NOT NULL,
autoload VARCHAR(20) NOT NULL DEFAULT 'yes',
PRIMARY KEY (option_id),
UNIQUE KEY option_name (option_name),
KEY autoload (autoload)
);
The autoload column is critical. If set to yes, WordPress loads this option on every single page request via a single SELECT query. Too many autoloaded options = slow site.
Key options and what they control:
option_name |
Example Value | Controls |
|---|---|---|
siteurl |
https://example.com |
Site URL |
blogname |
My Awesome Site |
Site title |
blogdescription |
Just another site |
Tagline |
admin_email |
admin@example.com |
Admin email |
active_plugins |
(serialized array) | Active plugins list |
template |
twentytwentyfour |
Active theme |
permalink_structure |
/%postname%/ |
URL structure |
uploads_use_yearmonth_folders |
1 |
Media organization |
wp_user_roles |
(serialized array) | All defined roles |
Real-world example:
// Check autoloaded options bloat
$autoloaded = $wpdb->get_results(
"SELECT option_name, LENGTH(option_value) as option_size
FROM {$wpdb->options}
WHERE autoload = 'yes'
ORDER BY option_size DESC
LIMIT 20"
);
foreach ( $autoloaded as $option ) {
echo esc_html( $option->option_name ) . ': ' . esc_html( size_format( $option->option_size ) ) . '<br>';
}
6. wp_terms β The Taxonomy Vocabulary
Terms are the individual labels β like a category named "Technology" or a tag called "JavaScript".
CREATE TABLE wp_terms (
term_id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(200) NOT NULL DEFAULT '',
slug VARCHAR(200) NOT NULL DEFAULT '',
term_group BIGINT(10) NOT NULL DEFAULT 0,
PRIMARY KEY (term_id),
KEY slug (slug(191)),
KEY name (name(191))
);
7. wp_term_taxonomy β What Kind of Term Is It?
A term alone doesn't know if it's a category, tag, or custom taxonomy. That's wp_term_taxonomy's job.
CREATE TABLE wp_term_taxonomy (
term_taxonomy_id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
term_id BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
taxonomy VARCHAR(32) NOT NULL DEFAULT '',
description LONGTEXT NOT NULL,
parent BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
count BIGINT(20) NOT NULL DEFAULT 0,
PRIMARY KEY (term_taxonomy_id),
UNIQUE KEY term_id_taxonomy (term_id, taxonomy),
KEY taxonomy (taxonomy)
);
taxonomy column values:
| Value | Represents |
|---|---|
category |
Post categories |
post_tag |
Post tags |
nav_menu |
Navigation menus |
link_category |
Blogroll categories |
wp_theme |
Theme-related terms |
product_cat |
WooCommerce categories |
8. wp_term_relationships β The Many-to-Many Bridge
This table connects posts to their terms. A post can have many categories; a category can have many posts. Classic many-to-many.
CREATE TABLE wp_term_relationships (
object_id BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
term_taxonomy_id BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
term_order INT(11) NOT NULL DEFAULT 0,
PRIMARY KEY (object_id, term_taxonomy_id),
KEY term_taxonomy_id (term_taxonomy_id)
);
Real-world example:
// Get all posts in the "News" category (term_id = 5)
$news_posts = $wpdb->get_results(
$wpdb->prepare(
"SELECT p.ID, p.post_title
FROM {$wpdb->posts} p
INNER JOIN {$wpdb->term_relationships} tr ON p.ID = tr.object_id
INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
WHERE tt.term_id = %d
AND tt.taxonomy = %s
AND p.post_status = %s",
5,
'category',
'publish'
)
);
9. wp_termmeta β Metadata for Terms
Added in WordPress 4.4, this table works the same as wp_postmeta and wp_usermeta but for taxonomy terms.
CREATE TABLE wp_termmeta (
meta_id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
term_id BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
meta_key VARCHAR(255) DEFAULT NULL,
meta_value LONGTEXT,
PRIMARY KEY (meta_id),
KEY term_id (term_id),
KEY meta_key (meta_key(191))
);
Common use cases:
- Storing a category's banner image ID
- WooCommerce product category thumbnails
- Custom taxonomy term colors or icons
10. wp_comments β Every Comment Ever Made
CREATE TABLE wp_comments (
comment_ID BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
comment_post_ID BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
comment_author TINYTEXT NOT NULL,
comment_author_email VARCHAR(100) NOT NULL DEFAULT '',
comment_author_url VARCHAR(200) NOT NULL DEFAULT '',
comment_author_IP VARCHAR(100) NOT NULL DEFAULT '',
comment_date DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
comment_date_gmt DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
comment_content TEXT NOT NULL,
comment_karma INT(11) NOT NULL DEFAULT 0,
comment_approved VARCHAR(20) NOT NULL DEFAULT '1',
comment_agent VARCHAR(255) NOT NULL DEFAULT '',
comment_type VARCHAR(20) NOT NULL DEFAULT 'comment',
comment_parent BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
user_id BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (comment_ID)
);
comment_approved values:
| Value | Meaning |
|---|---|
1 |
Approved (visible) |
0 |
Pending moderation |
spam |
Marked as spam |
trash |
Soft deleted |
11. wp_commentmeta β Comment Metadata
Same EAV pattern β extended data for comments. Akismet stores its spam detection data here.
CREATE TABLE wp_commentmeta (
meta_id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
comment_id BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
meta_key VARCHAR(255) DEFAULT NULL,
meta_value LONGTEXT,
PRIMARY KEY (meta_id),
KEY comment_id (comment_id),
KEY meta_key (meta_key(191))
);
12. wp_links β The Legacy Blogroll
CREATE TABLE wp_links (
link_id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
link_url VARCHAR(255) NOT NULL DEFAULT '',
link_name VARCHAR(255) NOT NULL DEFAULT '',
link_image VARCHAR(255) NOT NULL DEFAULT '',
link_target VARCHAR(25) NOT NULL DEFAULT '',
link_description VARCHAR(255) NOT NULL DEFAULT '',
link_visible VARCHAR(20) NOT NULL DEFAULT 'Y',
link_owner BIGINT(20) UNSIGNED NOT NULL DEFAULT 1,
link_rating INT(11) NOT NULL DEFAULT 0,
link_updated DATETIME NOT NULL DEFAULT '0000-00-00 00:00-00',
link_rel VARCHAR(255) NOT NULL DEFAULT '',
link_notes MEDIUMTEXT NOT NULL,
link_rss VARCHAR(255) NOT NULL DEFAULT '',
PRIMARY KEY (link_id),
KEY link_visible (link_visible)
);
π Worth Knowing:
wp_linksis a relic from the blogroll era (pre-WordPress 3.5). The Links Manager was hidden in WordPress 3.5 and is no longer shown by default. You'll still find it in the database, and some legacy plugins still use it β but for new projects, it's effectively retired.
πΊοΈ Admin Settings β Database Table Mapping
Here's where it all comes together. This is the cheat sheet you'll want to bookmark.
Settings β General
| Admin Setting | Table |
option_name / Column |
|---|---|---|
| Site Title | wp_options |
blogname |
| Tagline | wp_options |
blogdescription |
| WordPress Address (URL) | wp_options |
siteurl |
| Site Address (URL) | wp_options |
home |
| Admin Email | wp_options |
admin_email |
| Membership (Anyone can register) | wp_options |
users_can_register |
| New User Default Role | wp_options |
default_role |
| Site Language | wp_options |
WPLANG |
| Timezone | wp_options |
timezone_string |
| Date Format | wp_options |
date_format |
| Time Format | wp_options |
time_format |
Settings β Reading
| Admin Setting | Table | option_name |
|---|---|---|
| Your homepage displays | wp_options |
show_on_front (posts/page) |
| Homepage (static page) | wp_options |
page_on_front (stores Post ID) |
| Posts page | wp_options |
page_for_posts (stores Post ID) |
| Blog pages show at most | wp_options |
posts_per_page |
| Syndication feeds show | wp_options |
posts_per_rss |
| Search engine visibility | wp_options |
blog_public |
Settings β Discussion
| Admin Setting | Table | option_name |
|---|---|---|
| Allow comments on new posts | wp_options |
default_comment_status |
| Comment must be approved | wp_options |
comment_moderation |
| Hold comment for moderation | wp_options |
comment_max_links |
| Comment author must have approved comment | wp_options |
comment_previously_approved |
| Before a comment appears | wp_options |
require_name_email |
| Notify by email (new comment) | wp_options |
comments_notify |
Settings β Permalinks
| Admin Setting | Table | option_name |
|---|---|---|
| Permalink structure | wp_options |
permalink_structure |
| Category base | wp_options |
category_base |
| Tag base | wp_options |
tag_base |
Posts & Pages (Content)
| Admin Section | Table Affected | Key Column |
|---|---|---|
| New Post / Draft / Publish | wp_posts |
post_status, post_date
|
| Post Categories |
wp_terms, wp_term_taxonomy, wp_term_relationships
|
taxonomy = 'category' |
| Post Tags |
wp_terms, wp_term_taxonomy, wp_term_relationships
|
taxonomy = 'post_tag' |
| Featured Image | wp_postmeta |
meta_key = '_thumbnail_id' |
| Custom Fields | wp_postmeta |
Any custom meta_key
|
| Page Template | wp_postmeta |
meta_key = '_wp_page_template' |
| Revisions | wp_posts |
post_type = 'revision' |
| Media Uploads |
wp_posts, wp_postmeta
|
post_type = 'attachment' |
Users & Profiles
| Admin Section | Table Affected | Key Column |
|---|---|---|
| Add New User | wp_users |
user_login, user_email
|
| User Role | wp_usermeta |
meta_key = 'wp_capabilities' |
| Profile bio, URL | wp_usermeta |
meta_key = 'description', 'url'
|
| Application Passwords | wp_usermeta |
meta_key = '_application_passwords' |
| Sessions | wp_usermeta |
meta_key = 'session_tokens' |
Appearance β Menus
| Admin Section | Table Affected | Notes |
|---|---|---|
| Menu structure | wp_posts |
Each item = a row with post_type = 'nav_menu_item'
|
| Menu assignment | wp_term_taxonomy |
taxonomy = 'nav_menu' |
| Menu items order | wp_postmeta |
meta_key = '_menu_item_menu_item_parent' |
Comments
| Admin Section | Table Affected | Key Column |
|---|---|---|
| Approved comments | wp_comments |
comment_approved = '1' |
| Pending comments | wp_comments |
comment_approved = '0' |
| Spam | wp_comments |
comment_approved = 'spam' |
| Trash | wp_comments |
comment_approved = 'trash' |
| Akismet spam data | wp_commentmeta |
meta_key = 'akismet_*' |
π Bonus: How WordPress Uses Its Tables Together (A Query Walkthrough)
Let's trace what happens when WordPress loads a single blog post page (/?p=42):
-- Step 1: Get the post
SELECT * FROM wp_posts WHERE ID = 42 AND post_status = 'publish';
-- Step 2: Get all post meta (custom fields, featured image, SEO data)
SELECT meta_key, meta_value FROM wp_postmeta WHERE post_id = 42;
-- Step 3: Get post categories and tags
SELECT t.name, t.slug, tt.taxonomy
FROM wp_terms t
INNER JOIN wp_term_taxonomy tt ON t.term_id = tt.term_id
INNER JOIN wp_term_relationships tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
WHERE tr.object_id = 42;
-- Step 4: Get approved comments
SELECT * FROM wp_comments WHERE comment_post_ID = 42 AND comment_approved = '1';
-- Step 5: Get site options (runs on every request via autoload)
SELECT option_name, option_value FROM wp_options WHERE autoload = 'yes';
That's a minimum of 5 queries for a single page load β and that's before any plugin adds its own. This is why query caching and object caching are so important in WordPress performance tuning (which we'll cover in the next article).
π‘ Multisite: How Tables Scale
When WordPress Multisite is enabled, each subsite gets its own set of prefixed tables:
wp_posts β Site 1 (main)
wp_2_posts β Site 2
wp_3_posts β Site 3
wp_options β Site 1 (main)
wp_2_options β Site 2
wp_3_options β Site 3
But some tables are shared across the network:
| Shared Table | Purpose |
|---|---|
wp_users |
All users across all sites |
wp_usermeta |
All user metadata (capabilities per site stored here) |
wp_blogs |
Registry of all sites in the network |
wp_site |
Network-level site data |
wp_sitemeta |
Network-level settings |
wp_blog_versions |
DB version tracking per site |
wp_registration_log |
User registration log |
wp_signups |
Pending user/blog signups |
π§Ή Common Database Health Issues (And Which Tables to Check)
| Problem | Table to Inspect | What to Look For |
|---|---|---|
| Site slowing down | wp_options |
Bloated autoloaded options |
| Revisions eating storage | wp_posts |
Rows with post_type = 'revision'
|
| Orphaned metadata | wp_postmeta |
meta_key = '_edit_lock' with no matching post |
| Spam comments | wp_comments |
comment_approved = 'spam' piling up |
| Transient clutter | wp_options |
option_name LIKE '_transient_%' |
| User role issues | wp_usermeta |
Corrupted wp_capabilities serialized data |
π― Final Thoughts
WordPress's database schema is deceptively simple on the surface β just 12 tables. But once you understand what each table does, how they relate to each other, and how the admin panel maps directly to rows and columns, you start to see WordPress not as a blogging tool, but as a well-engineered content framework that's been refined over 20 years.
Key takeaways from this article:
-
wp_postsis the universal content container β not just posts, but pages, menus, attachments, and custom post types -
The EAV pattern (
postmeta,usermeta,options) makes WordPress extensible but requires careful query design -
wp_optionswithautoload = 'yes'runs on every request β keep it lean - The prefix system enables multisite scalability and adds a basic security layer
- Every admin UI action maps directly to a database operation you can inspect and optimize Understanding this foundation isn't just academic β it directly affects how fast your queries run, how you architect custom plugins, and how you protect your data.
π Next Up: DB Performance & Security in WordPress
Now that you know what the tables are, the next article dives into how to protect and optimize them:
- Indexing strategies for high-traffic WordPress sites
- Transient API β the right way to cache DB queries
- Query Monitor β profiling slow queries in real time
- Hardening β changing table prefixes, restricting DB permissions, and preventing SQL injection
- Backup strategies and point-in-time recovery
- WP_Query vs $wpdb β when to use each > π Save this article and follow along β the next one is where the real performance gains are.
Did this article help you understand WordPress's database structure better? Drop a comment below β especially if you've run into a tricky table-related bug in the wild. Those make the best war stories. π οΈ
Top comments (0)