在postgreSQL中启用查询缓存以提高性能

时间:2016-01-01 05:26:37

标签: python database postgresql caching

我的应用程序非常密集数据库,因此我尝试减少数据库的负载。我使用PostgreSQL作为rdbms而python是编程语言。 为了减少负载,我已经在应用程序中使用了缓存机制。我使用的缓存类型是服务器缓存,浏览器缓存。 目前,我正在调整PostgreSQL查询缓存,以使其符合在服务器上运行的查询的特性。

问题:

  1. 是否可以在每个数据库级别上微调查询缓存?
  2. 是否可以基于每个表微调查询缓存?
  3. 请提供在PostgreSQL中学习查询缓存的教程。

2 个答案:

答案 0 :(得分:1)

调优PostgreSQL不仅限于调优缓存。实际上,主要的高级内容是“共享缓冲区”(将其视为主要数据和索引缓存)和work_mem。

共享缓冲区有助于读写。您想给它一个合适的大小,但是它是针对整个集群的..而且您不能真正地针对每个表或特别是查询对其进行调整。重要的是,它并没有真正存储查询结果。它正在存储表,索引和其他数据。在符合ACID的数据库中,缓存查询结果不是非常有效或有用。

“ work_mem”用于对内存中的查询结果进行排序,而不必求助于写入磁盘。根据您的查询,该区域可能与缓冲区高速缓存一样重要,并且更易于调整。在运行需要进行更大排序的查询之前,可以发出set命令,例如“ SET work_mem ='256MB';”

正如其他人所建议的那样,您可以使用“解释”来找出查询运行缓慢的原因。我个人建议学习postgresql用来获取数据的“访问路径”。这比简单地考虑“缓存结果”要复杂得多,说实话更好地利用资源。

您还可以诚实地通过数据设计以及使用分区,功能索引和其他技术等功能来进行很多改进。

另一件事是,您可以通过编写更好的查询来获得更好的性能。“ with”子句之类的内容可能会阻止postgres的优化器完全优化查询。 优化器本身也具有可以调整的参数-这样,数据库在执行查询之前将花费更多(或更少)的时间来优化查询。.

您还可以使用某些技术来编写查询以帮助优化程序。一种这样的技术是使用绑定变量(冒号变量)---这将导致优化器一遍又一遍地传递相同的查询,并传递不同的数据。这样一来,不必反复评估结构。 。查询计划可以通过这种方式进行缓存。

在没有看到您的某些查询,表和索引设计以及说明计划的情况下,很难提出具体建议。

通常,您需要查找性能不如您期望的查询,并弄清楚争用在哪里。可能是磁盘访问,但是,原因最终是最重要的部分。它是否必须进入磁盘才能进行排序?是否在内部选择了一条获取数据的错误路径,以便它正在读取可以在查询过程中更早消除的数据...我从事Oracle认证的DBA已有20多年了,PostgreSQL肯定与众不同但是,在诊断查询的性能问题时,会使用许多相同的技术。尽管您实际上不能提供提示,但是您仍然可以重写查询或调整某些参数以获得更好的性能。通常,从长远来看,我发现postgresql更加容易调整。如果您可以提供一些具体信息,例如查询和解释信息,我很乐意为您提供具体建议。不过,令人遗憾的是,“缓存调整”很可能会为您提供所需的速度。

答案 1 :(得分:-1)

我开发了一个用于缓存结果的系统,以加快从基于Web的解决方案中查询的结果。我在下面重复了它的作用:

以下是通用缓存处理表和函数。

CREATE TABLE cached_results_headers (
  cache_id serial NOT NULL PRIMARY KEY,
  date timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
  last_access timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
  relid regclass NOT NULL,
  query text NOT NULL,
  rows int NOT NULL DEFAULT 0
  );
CREATE INDEX ON cached_results_headers (relid, md5(query));

CREATE TABLE cached_results (
  cache_id int NOT NULL,
  row_no int NOT NULL  
  );

CREATE OR REPLACE FUNCTION f_get_cached_results_header (p_cache_table text, p_source_relation regclass, p_query text, p_max_lifetime interval, p_clear_old_data interval) RETURNS cached_results_headers AS $BODY$
DECLARE
  _cache_id int;
  _rows int;
BEGIN
  IF p_clear_old_data IS NOT NULL THEN
    DELETE FROM cached_results_headers WHERE date < CURRENT_TIMESTAMP - p_clear_old_data;
  END IF;

  _cache_id := cache_id FROM cached_results_headers WHERE relid = p_source_relation AND md5(query) = md5(p_query) AND query = p_query AND date > CURRENT_TIMESTAMP - p_max_lifetime;
  IF _cache_id IS NULL THEN
    INSERT INTO cached_results_headers (relid, query) VALUES (p_source_relation, p_query) RETURNING cache_id INTO _cache_id;
    EXECUTE $$ INSERT INTO $$||p_cache_table||$$ SELECT $1, row_number() OVER (), r.r FROM ($$||p_query||$$) r $$ USING _cache_id;
    GET DIAGNOSTICS _rows = ROW_COUNT;
    UPDATE cached_results_headers SET rows = _rows WHERE cache_id = _cache_id;
  ELSE
    UPDATE cached_results_headers SET last_access = CURRENT_TIMESTAMP;
  END IF;
  RETURN (SELECT h FROM cached_results_headers h WHERE cache_id = _cache_id);
END;
$BODY$ LANGUAGE PLPGSQL SECURITY DEFINER;

以下是如何使用上面的表和函数的示例,用于在整数值范围内选择具有字段my_view的名为key的给定视图。您将根据您的特定需求替换以下所有内容,并将my_view替换为表,视图或函数。也可以根据需要替换过滤参数。

CREATE VIEW my_view AS SELECT ...; -- create a query with your data, with one of the integer columns in the result as "key" to filter by

CREATE TABLE cached_results_my_view (
  row my_view NOT NULL,
  PRIMARY KEY (cache_id, row_no),
  FOREIGN KEY (cache_id) REFERENCES cached_results_headers ON DELETE CASCADE
  ) INHERITS (cached_results);

CREATE OR REPLACE FUNCTION f_get_my_view_cached_rows (p_filter1 int, p_filter2 int, p_row_from int, p_row_to int) RETURNS SETOF my_view AS $BODY$
DECLARE
  _cache_id int;
BEGIN
  _cache_id := cache_id 
    FROM f_get_cached_results_header('cached_results_my_view', 'my_view'::regclass,
                                     'SELECT r FROM my_view r WHERE key BETWEEN '||p_filter1::text||' AND '||p_filter2::text||' ORDER BY key',
                                     '15 minutes'::interval, '1 day'::interval); -- cache for 15 minutes max since creation time; delete all cached data older than 1 day old

  RETURN QUERY
    SELECT (row).*
    FROM cached_results_my_view
    WHERE cache_id = _cache_id AND row_no BETWEEN p_row_from AND p_row_to
    ORDER BY row_no;
END;
$BODY$ LANGUAGE PLPGSQL;

示例:从key BETWEEN 30044 AND 10610679过滤的缓存my_view结果中检索1到2000之间的行。第一次运行,查询结果将缓存到表cached_results_my_view中,并返回前2000条记录。不久之后再次运行它,结果将直接从表cached_results_my_view中检索而不执行查询。

SELECT * FROM f_get_my_view_cached_rows(30044, 10610679, 1, 2000);