【问题标题】:How to improve Mysql database performance without changing the db structure如何在不改变db结构的情况下提高Mysql数据库性能
【发布时间】:2014-12-26 01:52:43
【问题描述】:

我有一个已经在使用的数据库,我必须提高使用该数据库的系统的性能。

有 2 个主要查询在一个循环中运行了大约 1000 次,并且这些查询每个都与其他 3 个表进行内部连接。这反过来又使系统变得非常慢。
我实际上试图从循环中删除查询并只获取一次所有数据并在 PHP 中处理它。但这会增加内存 (RAM) 的负载,如果 2 个或更多客户端尝试使用系统,系统就会挂起。

删除过期数据后,表中仍有大量数据。
我已附上以下查询。
谁能帮我解决这个问题?

select * from inventory
where   (region_id = 38 or region_id = -1)
    and (tour_opp_id = 410 or tour_opp_id = -1)
    and room_plan_id = 141 and meal_plan_id = 1 and bed_type_id = 1 and hotel_id = 1059
    and  FIND_IN_SET(supplier_code, 'QOA,QTE,QM,TEST,TEST1,MQE1,MQE3,PERR,QKT')  
    and ( ('2014-11-14' between from_date and to_date) )
order by hotel_id desc ,supplier_code desc, region_id desc,tour_opp_id desc,inventory.inventory_id desc 


SELECT * ,pinfo.fri as pi_day_fri,pinfoadd.fri as pa_day_fri,pinfochld.fri as pc_day_fri
FROM `profit_markup`
    inner join profit_markup_info as pinfo on pinfo.profit_id = profit_markup.profit_markup_id
    inner join profit_markup_add_info as pinfoadd on pinfoadd.profit_id = profit_markup.profit_markup_id
    inner join profit_markup_child_info as pinfochld on pinfochld.profit_id = profit_markup.profit_markup_id
where  profit_markup.hotel_id = 1059 and (`booking_channel` = 1 or `booking_channel` = 2)
    and (`rate_region` = -1 or `rate_region` = 128)
    and ( ( period_from <= '2014-11-14' and period_to >= '2014-11-14' ) )
ORDER BY profit_markup.hotel_id DESC,supplier_code desc, rate_region desc,operators_list desc, profit_markup_id DESC

【问题讨论】:

  • 你能展示一下创建表吗?另外,你能展示你对队列的解释吗?还有什么是服务器规格?
  • 您为什么使用 * 而不是列出字段?另外,你能展示你是如何循环记录的吗?这些表格更新的程度如何(每铸造/秒/小时???)您使用“OR”而不是“IN”的任何原因?

标签: php mysql mysqli phpmyadmin mysql-workbench


【解决方案1】:

由于我们还没有看到您的SHOW CREATE TABLES; 和EXPLAIN EXTENDED 计划,因此很难给您1 个答案

但一般而言,关于您的查询“顺便说一句,我在下面重写了”

SELECT 
    hotel_id, supplier_code, region_id, tour_opp_id, inventory_id
FROM 
    inventory
WHERE 
    region_id IN (38, -1) 
    AND tour_opp_id IN (410, -1) 
    AND room_plan_id IN (141, 1) 
    AND bed_type_id IN (1, 1059)
    AND supplier_code IN ('QOA', 'QTE', 'QM', 'TEST', 'TEST1', 'MQE1', 'MQE3', 'PERR', 'QKT')  
    AND ('2014-11-14' BETWEEN from_date AND to_date )
ORDER BY 
    hotel_id DESC, supplier_code DESC, region_id DESC, tour_opp_id DESC, inventory_id DESC
  1. 不要使用* 来获取所有列。您应该列出您真正需要的列。使用* 只是编写查询的一种懒惰方式。限制列将限制正在选择的数据大小。

  2. 清单中的记录多久更新/插入/删除?如果不是太频繁,那么您可以考虑使用SQL_CACHE。但是,如果您使用它并且经常更新库存表,那么缓存查询会给您带来问题。此外,要使用查询缓存,您必须检查服务器上query_cache_type 的值。 SHOW GLOBAL VARIABLES LIKE 'query_cache_type';。如果设置为“0”,则缓存功能被禁用,SQL_CACHE 将被忽略。如果它设置为 1,那么服务器将缓存所有查询,除非你告诉它不要使用NO_SQL_CACHE。如果该选项设置为 2,那么 MySQL 将仅在使用 SQL_CACHE 子句的地方缓存查询。 here is documentation about query_cache_type

  3. 如果您按此顺序在以下列上具有索引,它将对您有所帮助(hotel_id, supplier_code, region_id, tour_opp_id, inventory_id)

    ALTER TABLE inventory 
    ADD INDEX (hotel_id, supplier_code, region_id, tour_opp_id, inventory_id);
    
  4. 如果可能,请在您的服务器上增加 sort_buffer_size,因为您在这里发出的问题很可能是您的排序过多。

至于第二个查询“顺便说一句,我在下面重写了”

SELECT 
    *, pinfo.fri as pi_day_fri,
    pinfoadd.fri as pa_day_fri, 
    pinfochld.fri as pc_day_fri
FROM
    profit_markup
INNER JOIN 
    profit_markup_info AS pinfo ON pinfo.profit_id = profit_markup.profit_markup_id
INNER JOIN 
    profit_markup_add_info AS pinfoadd ON pinfoadd.profit_id = profit_markup.profit_markup_id
INNER JOIN 
    profit_markup_child_info AS pinfochld ON pinfochld.profit_id = profit_markup.profit_markup_id
WHERE  
    profit_markup.hotel_id = 1059 
    AND booking_channel IN (1, 2) 
    AND rate_region IN (-1, 128) 
    AND period_from <= '2014-11-14' 
    AND period_to >= '2014-11-14'
ORDER BY 
    profit_markup.hotel_id DESC, supplier_code DESC, rate_region DESC,
    operators_list DESC, profit_markup_id DESC
  1. 再次从查询中删除*

  2. 确保以下列具有相同的类型/排序规则和相同的大小。 pinfo.profit_id, profit_markup.profit_markup_id, pinfoadd.profit_id, pinfochld.profit_id 并且每个人都必须在每个表上都有一个索引。如果列具有不同的类型,那么 MySQL 每次都必须转换数据以加入记录。即使您有索引,它也会变慢。此外,如果这些列是字符类型(即 VARCHAR()),请确保它们属于 CHAR(),排序规则为 latin1_general_ci,因为这样查找 ID 会更快,但如果您使用的是 INT(),那就更好了.

  3. 使用我为上一个查询列出的第三个和第四个技巧

  4. 尝试使用 STRAIGHT_JOIN “你必须知道你在这里做什么,否则它会咬你!”这是一个关于这个When to use STRAIGHT_JOIN with MySQL

  5. 的好帖子

我希望这会有所帮助。

【讨论】:

    【解决方案2】:

    对于第一个查询,除了用列名替换 * 之外,我不确定您是否可以做很多事情(假设您已经索引了要排序的字段)(不要指望这会大大提高性能) .

    对于第二个查询,在您通过循环并输入选择参数之前,您可以创建一个视图,其中包含所有已连接和排序的表,然后制作一个准备好的语句以从视图中选择并在循环中绑定参数。

    另外,如果你的php服务器和数据库服务器在两个不同的地方,最好通过数据库中的存储过程进行选择。

    (如果没有任何结果,那么 memcache 就是要走的路......虽然我个人从未这样做过)

    【讨论】:

      【解决方案3】:

      在这里你提高了查询性能而不是数据库性能。

      对于这两个查询,首先检查 WHERE 和 ON(Join) 子句列上的索引是否可用,如果缺少索引,则必须添加索引以提高查询性能。

      在创建索引之前检查解释平面。

      如果可能的话,请告诉我这两个查询的解释平面,这将对我们有所帮助。

      【讨论】:

        猜你喜欢
        • 2010-10-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-29
        • 2023-03-08
        • 2015-07-01
        • 1970-01-01
        • 2010-11-03
        • 1970-01-01
        相关资源
        最近更新 更多