Skip to content
advanced Phase 102 · More Trade-offs

SQL vs Search Engine

45m
1 problems
Topic Progress 0%

SQL vs Search Engine Overview

SQL Database (MySQL)

-- Full-text search in MySQL
SELECT * FROM catalog_product_entity
WHERE MATCH(name, description) AGAINST ('wireless headphones' IN BOOLEAN MODE);

-- Filtered search
SELECT p.*, pv.value AS name
FROM catalog_product_entity p
JOIN catalog_product_entity_varchar pv ON p.entity_id = pv.entity_id
WHERE pv.attribute_id = 71
  AND pv.value LIKE '%wireless%'
  AND p.entity_id IN (
    SELECT entity_id FROM catalog_product_entity_int
    WHERE attribute_id = 78 AND value > 0
  );

-- Complex query with joins
SELECT p.*, cpev.value AS name, cpei.value AS qty
FROM catalog_product_entity p
JOIN catalog_product_entity_varchar cpev ON p.entity_id = cpev.entity_id
JOIN catalog_product_entity_int cpei ON p.entity_id = cpei.entity_id
WHERE cpev.attribute_id = 71
  AND cpei.attribute_id = 78
  AND cpei.value > 0
ORDER BY cpev.value ASC
LIMIT 20;

Search Engine (Elasticsearch)

// Elasticsearch query
{
  "query": {
    "bool": {
      "must": [
        { "match": { "name": "wireless headphones" } },
        { "range": { "qty": { "gt": 0 } } }
      ],
      "filter": [
        { "term": { "status": 1 } }
      ]
    }
  },
  "sort": [{ "name": { "order": "asc" } }],
  "size": 20
}

Performance Characteristics

Response Time Comparison

Query Type MySQL Elasticsearch
Full-text search 50-500ms 5-50ms
Exact match 1-10ms 1-5ms
Range query 10-100ms 5-20ms
Faceted search 100-1000ms 10-50ms
Complex joins 50-500ms N/A (denormalized)

Indexing Strategy

// MySQL indexing
CREATE INDEX idx_product_name ON catalog_product_entity_varchar(value);
CREATE INDEX idx_product_qty ON catalog_product_entity_int(value);
CREATE INDEX idx_product_sku ON catalog_product_entity(sku);

// Elasticsearch indexing
{
  "mappings": {
    "properties": {
      "name": { "type": "text", "analyzer": "standard" },
      "sku": { "type": "keyword" },
      "qty": { "type": "integer" },
      "price": { "type": "float" },
      "categories": { "type": "keyword" },
      "description": { "type": "text", "analyzer": "html_strip" }
    }
  }
}

Resource Usage

MySQL:
├── CPU: High for complex queries
├── Memory: Buffer pool for caching
├── Disk: I/O bound for large datasets
└── Network: Multiple round trips

Elasticsearch:
├── CPU: Inverted index lookups
├── Memory: Field data cache
├── Disk: Segment-based storage
└── Network: Single query response

When to Use Each

Use SQL When:

// 1. Exact data retrieval
$product = $this->db->fetchRow(
    'SELECT * FROM catalog_product_entity WHERE sku = ?',
    [$sku]
);

// 2. Complex joins and aggregations
$report = $this->db->fetchAll(
    'SELECT category_id, COUNT(*) as count, AVG(price) as avg_price '
    . 'FROM catalog_product JOIN prices ON ... GROUP BY category_id'
);

// 3. Transactional operations
$this->db->beginTransaction();
$this->db->update('inventory', ['qty' => $newQty], 'product_id = ?', [$id]);
$this->db->insert('inventory_log', [...]);
$this->db->commit();

// 4. Reporting and analytics
$analytics = $this->db->fetchAll(
    'SELECT DATE(created_at) as date, SUM(total) as revenue '
    . 'FROM sales_order GROUP BY DATE(created_at)'
);

Use Search Engine When:

// 1. Full-text search with relevance
$query = new \Elasticsearch\DSL\Query\MatchQuery('name', 'wireless headphones');
$results = $this->searchClient->search(['query' => $query]);

// 2. Faceted search
$agg = new \Elasticsearch\DSL\Aggregation\TermsAggregation('categories');
$results = $this->searchClient->search([
    'aggs' => ['categories' => ['terms' => ['field' => 'categories']]]
]);

// 3. Autocomplete
$query = new \Elasticsearch\DSL\Query\MatchPhrasePrefixQuery('name', 'wire');
$results = $this->searchClient->search([
    'query' => $query,
    'size' => 5
]);

// 4. Complex filtering with performance
{
  "query": {
    "bool": {
      "must": [{ "match": { "name": "phone" } }],
      "filter": [
        { "range": { "price": { "gte": 100, "lte": 500 } } },
        { "term": { "in_stock": true } }
      ]
    }
  }
}

Hybrid Approach

Data Flow

Product Save:
├── 1. Save to MySQL (source of truth)
├── 2. Index to Elasticsearch
└── 3. Update cache

Product Read (search):
├── 1. Query Elasticsearch (fast)
├── 2. Get product IDs
└── 3. Load details from MySQL/Cache

Product Read (direct):
├── 1. Check Redis cache
├── 2. Query MySQL
└── 3. Update cache

Implementation

// Product repository with hybrid approach
class ProductRepository
{
    public function search($query)
    {
        // Search in Elasticsearch (fast)
        $searchResults = $this->searchClient->search([
            'query' => ['match' => ['name' => $query]],
            'size' => 20
        ]);
        
        $productIds = array_column($searchResults['hits']['hits'], '_id');
        
        // Load full data from MySQL
        $products = $this->productCollection->create()
            ->addFieldToFilter('entity_id', ['in' => $productIds])
            ->load();
        
        return $products;
    }
    
    public function getById($id)
    {
        // Direct access - MySQL/Cache
        $cached = $this->cache->load('product_' . $id);
        if ($cached) return unserialize($cached);
        
        $product = $this->resourceModel->load($id);
        $this->cache->save(serialize($product), 'product_' . $id);
        return $product;
    }
}

Consistency

// Index synchronization
public function saveProduct($product)
{
    // Save to MySQL
    $this->resourceModel->save($product);
    
    // Index to Elasticsearch
    $this->searchClient->index([
        'index' => 'catalog_product',
        'id' => $product->getId(),
        'body' => $product->toArray()
    ]);
    
    // Invalidate cache
    $this->cache->remove('product_' . $product->getId());
}

// Handle sync failures
public function saveProductWithRetry($product)
{
    $this->resourceModel->save($product);
    
    try {
        $this->searchClient->index([...]);
    } catch (\Exception $e) {
        $this->logger->error('Index failed: ' . $e->getMessage());
        $this->queue->sendMessage('product.reindex', ['id' => $product->getId()]);
    }
}

Practice Problems

0 / 1 solved
Search Architecture Design

Design search architecture for 1M products with full-text search, faceted filtering, and autocomplete.

Solution
// Architecture:
// 1. MySQL: Source of truth, exact lookups
// 2. Elasticsearch: Search, facets, autocomplete
// 3. Redis: Cache hot search results
// 4. Sync: On save, update ES + invalidate cache
// 5. Fallback: MySQL full-text when ES down
// 6. Queue: Reindex failed documents

Quiz

1. When should you query SQL instead of Elasticsearch?

Question 1 options

2. What is the performance advantage of Elasticsearch?

Question 2 options

3. What is the hybrid approach?

Question 3 options

4. How to handle Elasticsearch indexing failures?

Question 4 options

Flashcards

Question

MySQL best for?

Answer

Exact matches, transactions, complex joins, reporting

Question

Elasticsearch best for?

Answer

Full-text search, faceted search, autocomplete, aggregations

Question

Hybrid approach?

Answer

MySQL source of truth, Elasticsearch for search

Question

Data flow on save?

Answer

MySQL → Elasticsearch → Cache invalidate

Question

Search fallback?

Answer

MySQL full-text search when Elasticsearch unavailable

Revision Notes

Key Takeaways

  • 1. MySQL: Exact matches, transactions, complex joins, reporting
  • 2. Elasticsearch: Full-text search, faceted search, autocomplete
  • 3. Hybrid: MySQL as source of truth, Elasticsearch for search
  • 4. Performance: Elasticsearch 10-100x faster for search
  • 5. Consistency: Index on save, queue for retry on failure

Interview Tips

  • Compare performance characteristics
  • Explain hybrid architecture benefits
  • Discuss consistency strategies
  • Know when to use each system

Cheat Sheet

SQL vs Search

  • MySQL: Exact, transactions, joins
  • ES: Full-text, facets, autocomplete
  • Hybrid: MySQL truth, ES search
  • Save: MySQL → ES → Cache
  • ES 10-100x faster for search
  • Fallback: MySQL full-text