Search Functionality in PHP: Building a Search That Does Not Kill the Database

12 min read 2,255 words
Search Functionality in PHP: Building a Search That Does Not Kill the Database featured image

Why Search Performance Becomes a Problem in PHP Applications

Search is one of those features that works perfectly during development and becomes a serious bottleneck once an application accumulates real data. A query that returns results in milliseconds when the database holds a few hundred rows can take several seconds once that number grows into thousands or tens of thousands.

The problem usually surfaces gradually. Users start noticing slower page loads. The database server begins consuming more CPU. Hosting providers send warnings about resource usage. In many cases, the underlying issue is a search implementation that was never designed to scale.

PHP applications typically rely on one of three approaches: basic pattern matching with LIKE queries, MySQL's built-in full-text search, or a dedicated search engine such as Elasticsearch. Each approach has its place, and choosing the right one depends on the data volume, feature requirements, and infrastructure constraints of the specific application.

For a deeper comparison between MySQL full-text search and Elasticsearch across different use cases, the article on Elasticsearch versus MySQL full-text search covers the practical differences in detail.

Using LIKE Queries for Basic Search

LIKE queries are the simplest way to add search to a PHP application. The syntax is straightforward, and most developers use it early in a project's lifecycle without giving it much thought.

SELECT * FROM products
WHERE title LIKE '%search_term%'
   OR description LIKE '%search_term%';

The percent symbols are wildcards. A query with a leading wildcard like '%term' matches anything ending in the search term. A trailing wildcard like 'term%' matches anything starting with it. Both wildcards together match the term appearing anywhere in the field.

The performance problem with LIKE queries stems from how MySQL processes them. When a column is indexed, MySQL can use the index to locate matching rows quickly. However, when a query contains a leading wildcard, the database engine cannot use the index at all. It performs a full table scan, reading every row in the table to find matches.

For a table with a few thousand rows, this is usually acceptable. For a table with hundreds of thousands of rows, it becomes a serious problem. The database must read and evaluate every single row on every search request.

When LIKE Queries Are Still Reasonable

LIKE queries are not inherently wrong. They make sense in specific situations:

  • Small datasets: Applications with fewer than 10,000 searchable rows often perform adequately with basic LIKE queries.
  • Infrequent searches: Admin panels or internal tools where search is used occasionally do not necessarily need full-text indexing.
  • Targeted searches: Queries that search a specific indexed column with a trailing wildcard, such as WHERE category LIKE 'electronics%', can use standard indexes effectively.

Adding sensible limits to search results helps manage performance even with LIKE queries. A query that returns 50 results will always perform better than one that attempts to return 50,000.

MySQL Full-Text Search: Built-In Indexing for PHP Applications

MySQL includes a built-in full-text search feature that performs significantly better than LIKE queries for most business application scenarios. It requires minimal configuration and works entirely within the database, which simplifies deployment and maintenance.

Setting Up a Full-Text Index

A full-text index must be created on the columns you want to search. MySQL supports full-text indexes on InnoDB tables (version 5.6 and later) and MyISAM tables.

ALTER TABLE articles
ADD FULLTEXT INDEX ft_title_content (title, content);

Once the index exists, searches use the MATCH and AGAINST syntax instead of LIKE.

SELECT id, title,
       MATCH(title, content) AGAINST('search query') AS relevance
FROM articles
WHERE MATCH(title, content) AGAINST('search query')
ORDER BY relevance DESC;

MySQL returns results ordered by relevance automatically. The relevance score reflects how closely each row matches the search query based on factors like term frequency and document frequency.

Full-Text Search Modes

MySQL offers different search modes that affect how queries are processed. Natural language mode is the default and treats the search query as a series of words, returning results based on how frequently those words appear in the indexed text. This mode is straightforward and works well for most applications.

Boolean mode provides more control using operators. A + before a word makes it required. A - makes it excluded. Phrases in quotes match exactly.

SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('+PHP -JavaScript "best practice"' IN BOOLEAN MODE);

This query finds articles that contain PHP, do not contain JavaScript, and contain the exact phrase "best practice". Boolean mode is useful for applications that need precise control over search results.

Limitations to Consider

MySQL full-text search has practical limitations that matter in production environments. The minimum indexed word length is configurable but defaults to four characters in MyISAM and three or four in InnoDB depending on the server configuration. Shorter terms are ignored during indexing and search, which can be problematic for fields containing abbreviations or technical terms.

Full-text search works best with reasonable amounts of text. Searching a title and body field of an article or product description is appropriate. Searching a large text field containing thousands of words may produce less useful relevance scoring.

For many small to medium-sized PHP applications, MySQL full-text search provides an excellent balance of capability and simplicity. It handles product catalogs, article collections, and business directories well without adding external infrastructure.

When a Dedicated Search Engine Becomes Necessary

MySQL full-text search reaches its practical limits in specific scenarios. When an application outgrows what the database can efficiently handle, adding a dedicated search engine solves the performance and feature problems.

Signs that it is time to move beyond MySQL full-text search include the dataset growing beyond approximately 100,000 searchable rows, search query times becoming noticeably slow during peak usage, the need for advanced features like fuzzy matching or faceted filtering, or search becoming a critical path where sub-second response times are essential.

Dedicated search engines like Elasticsearch, Typesense, and Meilisearch each serve different needs. Elasticsearch handles very large datasets with distributed architecture and advanced analytics. Typesense is designed for simplicity with built-in typo tolerance. Meilisearch prioritises developer experience and near-instant search results.

Adding a search engine introduces operational complexity. The search index must be kept in sync with the primary database. The search service requires its own infrastructure and monitoring. This trade-off is worthwhile when the application genuinely needs the capabilities a dedicated engine provides.

Syncing Data Between MySQL and a Search Engine

The most reliable approach is to treat the database as the source of truth and push changes to the search index asynchronously. When a record is created or updated in MySQL, a background job sends the relevant data to the search engine. This keeps the search index eventually consistent without slowing down the main application request.

// In a Laravel job or similar background process
$article = Article::find($id);

$searchDocument = [
    'id' => $article->id,
    'title' => $article->title,
    'content' => $article->content,
    'published_at' => $article->published_at,
];

Elasticsearch::index([
    'index' => 'articles',
    'id' => $article->id,
    'body' => $searchDocument,
]);

Using a queue to process index updates prevents slow search engine operations from blocking user-facing requests. The application saves data to MySQL, dispatches an indexing job, and responds to the user immediately. The search engine receives the update a few milliseconds later.

Practical PHP Search Architecture

The best search architecture for a PHP application depends on realistic expectations rather than theoretical maximum capabilities. A well-chosen approach that is simple to maintain and extend will outperform a sophisticated setup that nobody understands well enough to operate reliably.

Starting with MySQL full-text search and migrating to a dedicated engine only when the data volume or feature requirements justify it is a practical strategy. Most small to medium-sized business applications never need Elasticsearch. A well-configured MySQL full-text index handles hundreds of thousands of rows comfortably.

The decision framework should consider the expected data volume, the expected query frequency, how results are presented to users, and the infrastructure available to run and maintain additional services.

Performance Considerations for High-Traffic Search

Applications with significant search traffic benefit from caching at the application layer. A search query that runs frequently with the same terms can be cached for a short period, reducing database load without noticeably affecting result freshness.

Pagination also matters. Returning 20 results per page with a limit of 20 in the query is far more efficient than returning 100 results and filtering in PHP. Proper pagination with stable sort ordering prevents duplicate or missed results when users navigate between pages.

Hosting and Infrastructure Constraints

The hosting environment influences which search approach is practical. Managed PHP hosting with limited database access may not support full-text indexes or external search engines. A VPS or cloud server provides more flexibility to run MySQL configurations tuned for search or to install dedicated search services.

Memory considerations matter for dedicated search engines. Elasticsearch requires a minimum of several gigabytes to run comfortably. Typesense is significantly more lightweight and runs adequately on servers with 2GB of RAM. The infrastructure budget and operational capacity to maintain additional services should factor into the decision.

Common Search Implementation Mistakes

Several recurring mistakes cause search performance problems in PHP applications. Identifying and avoiding them early saves significant debugging time later.

Searching without appropriate indexes forces the database to scan entire tables on every query. This is tolerable at small scale but becomes catastrophic as data grows. Ensuring that frequently searched columns are properly indexed, whether with standard indexes for exact matches or full-text indexes for natural language search, is fundamental.

Unrestricted result sets create unnecessary load. A search without a LIMIT clause attempts to return every matching row, consuming memory and network bandwidth. Most user interfaces cannot display more than 20 or 50 results on a page anyway. Applying reasonable limits improves both performance and user experience.

Synchronous search indexing during write operations slows down every save. When a record is updated, synchronously sending that update to a search engine adds latency to the user request and creates a dependency between the main application and the search service. Using background jobs for indexing keeps write operations fast and resilient to search engine downtime.

Ignoring search performance until users complain means reacting to problems instead of preventing them. Monitoring average search query times and setting up alerts for unusual latency helps catch performance degradation before it becomes a user-facing problem.

Building Search That Scales Incrementally

The practical approach to search in PHP applications is incremental scaling. Start with the simplest solution that meets current requirements. Measure performance as the application grows. Upgrade to more sophisticated solutions only when the existing approach begins to show its limits.

MySQL full-text search handles most small to medium-sized application requirements without external dependencies. When the dataset grows large enough that full-text queries slow down, or when requirements expand to include features that MySQL cannot provide, adding a dedicated search engine completes the transition. The key is knowing when that threshold has been reached rather than guessing at future needs.

If you are evaluating search options for a PHP application and want a practical assessment of what approach fits the current scale, preparing a few details about the data volume, expected query frequency, and required features helps frame the conversation.

Related practical reading

These related guides can help you connect this topic with the wider website, server, security, and support decisions around it.

Frequently Asked Questions

What is the performance difference between LIKE queries and full-text search in MySQL?
LIKE queries with leading wildcards perform full table scans, reading every row to find matches. On a table with 10,000 rows, this might take 50 to 500 milliseconds depending on the server and data size. MySQL full-text search uses an inverted index that locates matching documents directly, typically reducing query time to 10 to 100 milliseconds for similar data volumes. The difference becomes more pronounced as the dataset grows.
Can MySQL full-text search replace Elasticsearch for most applications?
For many applications, yes. MySQL full-text search handles natural language queries, relevance ranking, and boolean operators without additional infrastructure. Elasticsearch becomes necessary for very large datasets, distributed search across multiple sources, advanced features like fuzzy matching and faceted filtering, or sub-50-millisecond response times at high query volumes. The additional complexity of running Elasticsearch is only justified when those capabilities are genuinely needed.
How do I decide between MySQL full-text search and a dedicated search engine?
Consider the dataset size, query volume, required features, and operational capacity. MySQL full-text search works well for applications with fewer than 100,000 searchable rows and straightforward search needs. A dedicated search engine becomes appropriate when dataset size or query volume exceeds what MySQL handles comfortably, or when features like typo tolerance, faceted search, or instant suggestions are required. The hosting environment and available infrastructure also influence the decision.
What is a reasonable search response time expectation for PHP applications?
Aim for search queries to complete in under 200 milliseconds for a good user experience. MySQL full-text search typically achieves 10 to 100 milliseconds for indexed queries. Elasticsearch usually responds in 5 to 50 milliseconds for cached results. LIKE queries on large tables can take several seconds, which indicates the need for optimisation or a different approach.
How often should I monitor search performance?
Regular monitoring helps catch degradation before it affects users significantly. Tracking average query time, slow query count, and database CPU usage provides useful signals. If search queries start averaging above 200 milliseconds or slow query counts increase, it is worth investigating whether the current approach needs optimisation or whether data growth has pushed the application beyond what the existing setup handles comfortably.