I have a single database query which runs when a user searches a product on my site.
I have increased the Digital Ocean Droplet size from 1GB to 4GB package but still the restuls are not satisfactory.
While doing the tests with BlazeMeter and running myTop i came up with the below analysis.
The error rate is decreased but still the average respose is too slow.
Below is the query which i managed to get using myTop command. Can some one help me top optimize it ? Thanks in advance .
SELECT *
FROM (
SELECT p.*,
m.logo as market_logo,
m.slug as market_slug,
c.id as cat_id,
c.slug as cat_slug,
c.name as category_name,
c.name_ar as category_name_ar,
sub.slug as sub_slug,
sub.id as subcat_id,
sub.name as subcategory_name,
sub.name_ar as subcategory_name_ar,
f.desc as descp_en,
f.desc_ar as descp_ar
FROM category as c,
sub_category as sub,
promotion AS p,
flyer AS f,
market as m
WHERE c.id=p.category AND
sub.id=p.sub_category AND
f.market_id=m.id AND
f.id=p.flyer_id AND
f.active=1 AND 1 AND 1 AND 1 AND 1 AND
p.product_name_en LIKE '%a%'
ORDER BY
p.price ASC
) DUMMY_ALIAS1
UNION
SELECT *
FROM (
SELECT p.*,
m.logo as market_logo,
m.slug as market_slug,
c.id as cat_id,
c.slug as cat_slug,
c.name as category_name,
c.name_ar as category_name_ar,
sub.slug as sub_slug,
sub.id as subcat_id,
sub.name as subcategory_name,
sub.name_ar as subcategory_name_ar,
f.desc as descp_en,
f.desc_ar as descp_ar
FROM category as c,
sub_category as sub,
promotion AS p,
flyer AS f,
market as m
WHERE c.id=p.category AND
sub.id=p.sub_category AND
f.market_id=m.id AND
f.id=p.flyer_id AND
f.active=1 AND 1 AND 1 AND 1 AND 1 AND
sub.name LIKE '%a%'
ORDER BY
p.price ASC
) DUMMY_ALIAS2
UNION
SELECT *
FROM (
SELECT p.*,
m.logo as market_logo,
m.slug as market_slug,
c.id as cat_id,
c.slug as cat_slug,
c.name as category_name,
c.name_ar as category_name_ar,
sub.slug as sub_slug,
sub.id as subcat_id,
sub.name as subcategory_name,
sub.name_ar as subcategory_name_ar,
f.desc as descp_en,
f.desc_ar as descp_ar
FROM category as c,
sub_category as sub,
promotion AS p,
flyer AS f,
market as m
WHERE c.id=p.category AND
sub.id=p.sub_category AND
f.market_id=m.id AND
f.id=p.flyer_id AND
f.active=1 AND 1 AND 1 AND 1 AND 1 AND
p.product SOUNDS LIKE '%a%'
ORDER BY
p.price ASC
) DUMMY_ALIAS3
This is the output of EXPLAIN:
*** row 1 ***
table: <derived2>
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 4525
Extra: NULL
*** row 2 ***
table: p
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 240072
Extra: Using where; Using filesort
*** row 3 ***
table: sub
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: mywebsite.p.sub_category
rows: 1
Extra: Using where
*** row 4 ***
table: f
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: mywebsite.p.flyer_id
rows: 1
Extra: Using where
*** row 5 ***
table: m
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: mywebsite.f.market_id
rows: 1
Extra: NULL
*** row 6 ***
table: c
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: mywebsite.p.category
rows: 1
Extra: Using where
*** row 7 ***
table: <derived4>
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 2925
Extra: NULL
*** row 8 ***
table: p
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 240072
Extra: Using filesort
*** row 9 ***
table: sub
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: mywebsite.p.sub_category
rows: 1
Extra: Using where
*** row 10 ***
table: f
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: mywebsite.p.flyer_id
rows: 1
Extra: Using where
*** row 11 ***
table: m
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: mywebsite.f.market_id
rows: 1
Extra: NULL
*** row 12 ***
table: c
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: mywebsite.p.category
rows: 1
Extra: Using where
*** row 13 ***
table: <derived6>
type: system
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: NULL
Extra: const row not found
*** row 14 ***
table: p
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 240072
Extra: Using where; Using filesort
*** row 15 ***
table: sub
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: mywebsite.p.sub_category
rows: 1
Extra: Using where
*** row 16 ***
table: f
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: mywebsite.p.flyer_id
rows: 1
Extra: Using where
*** row 17 ***
table: m
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: mywebsite.f.market_id
rows: 1
Extra: NULL
*** row 18 ***
table: c
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: mywebsite.p.category
rows: 1
Extra: Using where
*** row 19 ***
table: <union1,3,5>
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: NULL
Extra: NULL
Best Answer
You need to be able to index your queries otherwise it's always going to be slow and scaling the server will not really help much due to the amount of data it has to read on every full table scan.
Option #1
I would suggest (if you want to keep everything in MySQL) to build a term index:
1) you need two more tables
2) Populate your data and keep it up to date
This can be done from your application, cronjob or trigger in mysql. Whichever you feel safe with.
3) Change your query to something like this
You can also include
sum(num)
which will give you the possibility to order by relevance.To read more about this you can take a look on a benchmark I did with MySQL Full text search alternatives: http://charlesnagy.info/it/mysql/full-text-search-in-mysql
Option #2
You can use some search engines which provides great full text search capabilities out of the box like Solr or Elasticsearch.
A very simple solution for what you want is to use the facet and facet filter with parameters like this:
More on this and how to set it up with Nginx: Typeahead and autosuggest with pure Solr and Nginx
Option #3
You can use also redis for prefix match using ZRANGEBYLEX. That is quite limited compared to the previous options but also a way to implement autosuggest.