mysql - expensive query takes down database server -- looking for ways to mitigate -


i have expensive query creates temporary table reporting purposes. having problem denial of service attack takes place 50+ of these queries pile (all same user). causes database server pretty taken down. query can take 1-10 seconds run , can have lot of rows in resulting temporary table.

i not sure if there easy way improve query performance without re-architecting entire reporting piece of application.

what solutions can use apply solve problem these queries piling , taking down server.

my questions are:

  1. can think of solution improving query performance (query below)

  2. does make sense place session limit of 1 query can run once , prevent pile up

  3. any other ideas on how prevent attack.

here query:

create temporary table phppos_sales_items_temp  (select phppos_sales.deleted deleted,phppos_sales.deleted_by deleted_by, sale_time, date(sale_time) sale_date,  phppos_sales_items.sale_id, comment,payment_type, customer_id, employee_id, phppos_items.item_id, null item_kit_id,  supplier_id, quantity_purchased, item_cost_price, item_unit_price, category, discount_percent, (item_unit_price*quantity_purchased-item_unit_price*quantity_purchased*discount_percent/100) subtotal,  phppos_sales_items.line line, serialnumber, phppos_sales_items.description description, (item_unit_price*quantity_purchased-item_unit_price*quantity_purchased*discount_percent/100)+(item_unit_price*quantity_purchased-item_unit_price*quantity_purchased*discount_percent/100)*(sum(case when cumulative != 1 percent else 0 end)/100) +(((item_unit_price*quantity_purchased-item_unit_price*quantity_purchased*discount_percent/100)*(sum(case when cumulative != 1 percent else 0 end)/100) + (item_unit_price*quantity_purchased-item_unit_price*quantity_purchased*discount_percent/100)) *(sum(case when cumulative = 1 percent else 0 end))/100) total, (item_unit_price*quantity_purchased-item_unit_price*quantity_purchased*discount_percent/100)*(sum(case when cumulative != 1 percent else 0 end)/100) +(((item_unit_price*quantity_purchased-item_unit_price*quantity_purchased*discount_percent/100)*(sum(case when cumulative != 1 percent else 0 end)/100) + (item_unit_price*quantity_purchased-item_unit_price*quantity_purchased*discount_percent/100)) *(sum(case when cumulative = 1 percent else 0 end))/100) tax, (item_unit_price*quantity_purchased-item_unit_price*quantity_purchased*discount_percent/100) - (item_cost_price*quantity_purchased) profit  phppos_sales_items  inner join phppos_sales on phppos_sales_items.sale_id=phppos_sales.sale_id  inner join phppos_items on phppos_sales_items.item_id=phppos_items.item_id  left outer join phppos_suppliers on phppos_items.supplier_id=phppos_suppliers.person_id  left outer join phppos_sales_items_taxes on phppos_sales_items.sale_id=phppos_sales_items_taxes.sale_id , phppos_sales_items.item_id=phppos_sales_items_taxes.item_id , phppos_sales_items.line=phppos_sales_items_taxes.line  sale_time between "2014-04-01 00:00:00" , "2014-04-30 23:59:59" , phppos_sales.location_id='1' , phppos_sales.store_account_payment=0  group sale_id, item_id, line)   union   (select phppos_sales.deleted deleted,phppos_sales.deleted_by deleted_by, sale_time, date(sale_time) sale_date,  phppos_sales_item_kits.sale_id, comment,payment_type, customer_id, employee_id, null item_id, phppos_item_kits.item_kit_id, '' supplier_id,  quantity_purchased, item_kit_cost_price, item_kit_unit_price, category, discount_percent,  (item_kit_unit_price*quantity_purchased-item_kit_unit_price*quantity_purchased*discount_percent/100) subtotal,  phppos_sales_item_kits.line line, '' serialnumber, phppos_sales_item_kits.description description,  (item_kit_unit_price*quantity_purchased-item_kit_unit_price*quantity_purchased*discount_percent/100)+(item_kit_unit_price*quantity_purchased-item_kit_unit_price*quantity_purchased*discount_percent/100)*(sum(case when cumulative != 1 percent else 0 end)/100) +(((item_kit_unit_price*quantity_purchased-item_kit_unit_price*quantity_purchased*discount_percent/100)*(sum(case when cumulative != 1 percent else 0 end)/100) + (item_kit_unit_price*quantity_purchased-item_kit_unit_price*quantity_purchased*discount_percent/100)) *(sum(case when cumulative = 1 percent else 0 end))/100) total,  (item_kit_unit_price*quantity_purchased-item_kit_unit_price*quantity_purchased*discount_percent/100)*(sum(case when cumulative != 1 percent else 0 end)/100) +(((item_kit_unit_price*quantity_purchased-item_kit_unit_price*quantity_purchased*discount_percent/100)*(sum(case when cumulative != 1 percent else 0 end)/100) + (item_kit_unit_price*quantity_purchased-item_kit_unit_price*quantity_purchased*discount_percent/100)) *(sum(case when cumulative = 1 percent else 0 end))/100) tax,  (item_kit_unit_price*quantity_purchased-item_kit_unit_price*quantity_purchased*discount_percent/100) - (item_kit_cost_price*quantity_purchased) profit  phppos_sales_item_kits  inner join phppos_sales on phppos_sales_item_kits.sale_id=phppos_sales.sale_id  inner join phppos_item_kits on phppos_sales_item_kits.item_kit_id=phppos_item_kits.item_kit_id  left outer join phppos_sales_item_kits_taxes on phppos_sales_item_kits.sale_id=phppos_sales_item_kits_taxes.sale_id , phppos_sales_item_kits.item_kit_id=phppos_sales_item_kits_taxes.item_kit_id , phppos_sales_item_kits.line=phppos_sales_item_kits_taxes.line  sale_time between "2014-04-01 00:00:00" , "2014-04-30 23:59:59" , phppos_sales.location_id='1' , phppos_sales.store_account_payment=0  group sale_id, item_kit_id, line)   order sale_id, line 

explain:

+----------+--------+----------------------------------+--------------+---------+-----------------------------------------------------------------------------------------------------------+------+----------------------------------------------+ | id | select_type  | table                        | type   | possible_keys                    | key          | key_len | ref                                                                                                       | rows |                                        | +----+--------------+------------------------------+--------+----------------------------------+--------------+---------+-----------------------------------------------------------------------------------------------------------+------+----------------------------------------------+ |  1 | primary      | phppos_sales                 | range  | primary,location_id,sales_search | sales_search | 12      | null                                                                                                      |  113 | using where; using temporary; using filesort | |  1 | primary      | phppos_sales_items           | ref    | primary,item_id                  | primary      | 4       | pos.phppos_sales.sale_id                                                                                  |    1 |                                              | |  1 | primary      | phppos_items                 | eq_ref | primary                          | primary      | 4       | pos.phppos_sales_items.item_id                                                                            |    1 |                                              | |  1 | primary      | phppos_suppliers             | ref    | person_id                        | person_id    | 4       | pos.phppos_items.supplier_id                                                                              |    1 | using index                                  | |  1 | primary      | phppos_sales_items_taxes     | ref    | primary,item_id                  | primary      | 12      | pos.phppos_sales_items.sale_id,pos.phppos_sales_items.item_id,pos.phppos_sales_items.line                 |    1 |                                              | |  2 | union        | phppos_sales_item_kits       |    | primary,item_kit_id              | null         | null    | null                                                                                                      |    1 | using temporary; using filesort              | |  2 | union        | phppos_item_kits             | eq_ref | primary                          | primary      | 4       | pos.phppos_sales_item_kits.item_kit_id                                                                    |    1 |                                              | |  2 | union        | phppos_sales_item_kits_taxes | ref    | primary,item_id                  | primary      | 12      | pos.phppos_sales_item_kits.sale_id,pos.phppos_sales_item_kits.item_kit_id,pos.phppos_sales_item_kits.line |    1 |                                              | |  2 | union        | phppos_sales                 | eq_ref | primary,location_id,sales_search | primary      | 4       | pos.phppos_sales_item_kits.sale_id                                                                        |    1 | using                                  | | null | union result | <union1,2>                   |    | null                             | null         | null    | null                                                                                                      | null | using filesort                               | +----+--------------+------------------------------+--------+----------------------------------+--------------+---------+-----------------------------------------------------------------------------------------------------------+------+----------------------------------------------+ 

10 rows in set (0.00 sec)

create tables:

mysql> show create table phppos_sales_items; | table              | create table                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  | +--------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | phppos_sales_items | create table `phppos_sales_items` (   `sale_id` int(10) not null default '0',   `item_id` int(10) not null default '0',   `description` varchar(255) collate utf8_unicode_ci default null,   `serialnumber` varchar(255) collate utf8_unicode_ci default null,   `line` int(3) not null default '0',   `quantity_purchased` decimal(23,10) not null default '0.0000000000',   `item_cost_price` decimal(23,10) not null,   `item_unit_price` decimal(23,10) not null,   `discount_percent` int(11) not null default '0',   primary key (`sale_id`,`item_id`,`line`),   key `item_id` (`item_id`),   constraint `phppos_sales_items_ibfk_1` foreign key (`item_id`) references `phppos_items` (`item_id`),   constraint `phppos_sales_items_ibfk_2` foreign key (`sale_id`) references `phppos_sales` (`sale_id`) ) engine=innodb default charset=utf8 collate=utf8_unicode_ci | 1 row in set (0.01 sec)  mysql> show create table phppos_sales; +--------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | table        | create table                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | +--------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | phppos_sales | create table `phppos_sales` (   `sale_time` timestamp not null default current_timestamp,   `customer_id` int(10) default null,   `employee_id` int(10) not null default '0',   `comment` text collate utf8_unicode_ci not null,   `show_comment_on_receipt` int(1) not null default '0',   `sale_id` int(10) not null auto_increment,   `payment_type` varchar(255) collate utf8_unicode_ci default null,   `cc_ref_no` varchar(255) collate utf8_unicode_ci not null,   `auth_code` varchar(255) collate utf8_unicode_ci default '',   `deleted_by` int(10) default null,   `deleted` int(1) not null default '0',   `suspended` int(1) not null default '0',   `store_account_payment` int(1) not null default '0',   `location_id` int(11) not null,   primary key (`sale_id`),   key `customer_id` (`customer_id`),   key `employee_id` (`employee_id`),   key `deleted` (`deleted`),   key `location_id` (`location_id`),   key `phppos_sales_ibfk_4` (`deleted_by`),   key `sales_search` (`location_id`,`store_account_payment`,`sale_time`,`sale_id`),   constraint `phppos_sales_ibfk_3` foreign key (`location_id`) references `phppos_locations` (`location_id`),   constraint `phppos_sales_ibfk_4` foreign key (`deleted_by`) references `phppos_employees` (`person_id`),   constraint `phppos_sales_ibfk_1` foreign key (`employee_id`) references `phppos_employees` (`person_id`),   constraint `phppos_sales_ibfk_2` foreign key (`customer_id`) references `phppos_customers` (`person_id`) ) engine=innodb default charset=utf8 collate=utf8_unicode_ci | 1 row in set (0.00 sec)  mysql> show create table phppos_items; +--------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | table        | create table                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | +--------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | phppos_items | create table `phppos_items` (   `name` varchar(255) collate utf8_unicode_ci not null,   `category` varchar(255) collate utf8_unicode_ci not null,   `supplier_id` int(11) default null,   `item_number` varchar(255) collate utf8_unicode_ci default null,   `product_id` varchar(255) collate utf8_unicode_ci default null,   `description` varchar(255) collate utf8_unicode_ci not null,   `tax_included` int(1) not null default '0',   `cost_price` decimal(23,10) not null,   `unit_price` decimal(23,10) not null,   `promo_price` decimal(23,10) default null,   `start_date` date default null,   `end_date` date default null,   `reorder_level` decimal(23,10) default null,   `item_id` int(10) not null auto_increment,   `allow_alt_description` tinyint(1) not null,   `is_serialized` tinyint(1) not null,   `image_id` int(10) default null,   `override_default_tax` int(1) not null default '0',   `is_service` int(1) not null default '0',   `deleted` int(1) not null default '0',   primary key (`item_id`),   unique key `item_number` (`item_number`),   unique key `product_id` (`product_id`),   key `phppos_items_ibfk_1` (`supplier_id`),   key `name` (`name`),   key `category` (`category`),   key `deleted` (`deleted`),   key `phppos_items_ibfk_2` (`image_id`),   constraint `phppos_items_ibfk_1` foreign key (`supplier_id`) references `phppos_suppliers` (`person_id`),   constraint `phppos_items_ibfk_2` foreign key (`image_id`) references `phppos_app_files` (`file_id`) ) engine=innodb default charset=utf8 collate=utf8_unicode_ci | 1 row in set (0.00 sec)  mysql> show create table phppos_suppliers; +------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | table            | create table                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | +------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | phppos_suppliers | create table `phppos_suppliers` (   `person_id` int(10) not null,   `company_name` varchar(255) collate utf8_unicode_ci not null,   `account_number` varchar(255) collate utf8_unicode_ci default null,   `deleted` int(1) not null default '0',   unique key `account_number` (`account_number`),   key `person_id` (`person_id`),   key `deleted` (`deleted`),   constraint `phppos_suppliers_ibfk_1` foreign key (`person_id`) references `phppos_people` (`person_id`) ) engine=innodb default charset=utf8 collate=utf8_unicode_ci | 1 row in set (0.00 sec)  mysql> show create table phppos_sales_items_taxes; +--------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | table                    | create table                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | +--------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | phppos_sales_items_taxes | create table `phppos_sales_items_taxes` (   `sale_id` int(10) not null,   `item_id` int(10) not null,   `line` int(3) not null default '0',   `name` varchar(255) collate utf8_unicode_ci not null,   `percent` decimal(15,3) not null,   `cumulative` int(1) not null default '0',   primary key (`sale_id`,`item_id`,`line`,`name`,`percent`),   key `item_id` (`item_id`),   constraint `phppos_sales_items_taxes_ibfk_1` foreign key (`sale_id`) references `phppos_sales_items` (`sale_id`),   constraint `phppos_sales_items_taxes_ibfk_2` foreign key (`item_id`) references `phppos_items` (`item_id`) ) engine=innodb default charset=utf8 collate=utf8_unicode_ci | 1 row in set (0.00 sec) 

hmm, might try writing query along these lines:

select sale_item.deleted, sale_item.deleted_by,        sale_item.sale_time, sale_item.sale_date,        sale_item.comment,        sale_item.payment_type,        sale_item.customer_id,        sale_item.employee_id,        sale_item.category,        sale_item.sale_id, sale_item.item_id, null item_kit_id, sale_item.line,         sale_item.supplier_id,        sale_item.serialnumber, sale_item.description,        sale_item.quantity_purchased, sale_item.item_cost_price, sale_item.item_unit_price,        sale_item.discount_percent,        sale_item.linesubtotal,        sale_item.linesubtotal * coalesce(tax.non_cumulative, 0) + (sale_item.linesubtotal * coalesce(tax.non_cumulative, 0) + sale_item.non_cumulative) * coalesce(tax.cumulative, 0) linetax,        sale_item.linesubtotal + (sale_item.linesubtotal * coalesce(tax.non_cumulative, 0) + (sale_item.linesubtotal * coalesce(tax.non_cumulative, 0) + sale_item.non_cumulative) * coalesce(tax.cumulative, 0)) linetotal,        sale_item.linesubtotal - (sale_item.item_cost_price * sale_item.quantity_purchased) profit  (select sale.deleted, sale.deleted_by,              sale.sale_time, date(sale.sale_time) sale_date,              sale.comment,              sale.payment_type,              sale.customer_id,              sale.employee_id,              item.category,              sale_item.sale_id, sale_item.item_id, null item_kit_id, sale_item.line,               sale_item.supplier_id,              sale_item.serialnumber, sale_item.description,              sale_item.quantity_purchased, sale_item.item_cost_price, sale_item.item_unit_price,              sale_item.discount_percent,              (sale_item.item_unit_price * sale_item.quantity_purchased) - (sale_item.item_unit_price * sale_item.quantity_purchased * sale_item.discount_percent / 100) linesubtotal                        phppos_sales_items sale_item       join phppos_sales sale         on sale.sale_id = sale_item.sale_id            , sale.sale_time >= timestamp('2014-04-01')            , sale.sale_time < timestampadd(month, 1, '2014-04-01')            , sale.location_id = 1            , sale.store_account_payment = 0) sale_item  left join (select tax.sale_id, tax.item_id, tax.line,                   sum(case when tax.cumulative = 1 tax.percent else 0 end) cumulative,                   sum(case when tax.cumulative <> 1 tax.percent else 0 end) non_cumulative            phppos_sales_item_taxes tax            join phppos_sales sale              on sale.sale_id = tax.sale_id                 , sale.sale_time >= timestamp('2014-04-01')                 , sale.sale_time < timestampadd(month, 1, '2014-04-01')                 , sale.location_id = 1                 , sale.store_account_payment = 0            group tax.sale_id, tax.item_id, tax.line) tax        on tax.sale_id = sale_item.sale_id           , tax.item_id = sale_item.sale_id           , tax.line =sale_item.line  

moved several columns organizational purposes. should have no large effect on processing time.

i removed reference phppos_suppliers as:

  1. you don't use columns table
  2. it's left join, meaning don't require rows exist there.

i moved group by new subquery, because phppos_sales_item_taxes table have duplicate rows given criteria. included reference phppos_sales because i'm not sure if mysql's optimizer (or any, really) smart enough push citeria down.

the main part of query has been moved subquery wouldn't need type formula linesubtotal multiple times. i've used same formulas throughout, there simplified versions available:

sale_item.item_unit_price * sale_item.quantity_purchased * (1 - (sale_item.discount_percent / 100)) linesubtotal    sale_item.linesubtotal * coalesce(tax.non_cumulative + tax.cumulative + tax.non_cumulative * tax.cumulative, 0) tax 

.... may have run these accounting, though, tend (understandably) touchy order of operations. may result in faster runtime doubt it; simplification of terms more readable.

you didn't provide table layouts other half of query, presume it's similar. related modification left exercise reader.


general mitigation strategies

beyond potential speedup changing query might have, there's number of things curtail problem:

  1. in application layer, force query (and possibly others) go through job submission process results can retrieved later. new copy of query can't run until previous 1 completes. assume php has existing library this. throttling submission in general may need.
  2. the data being retrieved appears amenable caching - store prior recent processed sale_date, , new information on-the-fly (although transformation isn't that different original - however, not doing more joins may help).
  3. disallow queries on current processing timespan. should keep system attempting access rows haven't been committed yet, , potentially away index pages under modification. sort of trick works best if storage laid out take advantage of concurrent i/o.

Comments

Popular posts from this blog

c++ - How to add Crypto++ library to Qt project -

jQuery Mobile app not scrolling in Firefox -

How to use vim as editor in Matlab GUI -