Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtp (Exim 4.84_2) (envelope-from ) id 1aqOES-00082n-Tm for pgsql-performance@arkaria.postgresql.org; Wed, 13 Apr 2016 17:05:37 +0000 Received: from localhost ([127.0.0.1] helo=postgresql.org) by malur.postgresql.org with smtp (Exim 4.84_2) (envelope-from ) id 1aqOES-0000Pg-GN for pgsql-performance@arkaria.postgresql.org; Wed, 13 Apr 2016 17:05:36 +0000 Received: from makus.postgresql.org ([2001:4800:1501:1::229]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1aqOCm-0005kz-3F for pgsql-performance@postgresql.org; Wed, 13 Apr 2016 17:03:52 +0000 Received: from mail-lf0-x229.google.com ([2a00:1450:4010:c07::229]) by makus.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.84_2) (envelope-from ) id 1aqOCj-0000Ck-6Y for pgsql-performance@postgresql.org; Wed, 13 Apr 2016 17:03:50 +0000 Received: by mail-lf0-x229.google.com with SMTP id c126so78522365lfb.2 for ; Wed, 13 Apr 2016 10:03:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:date:message-id:subject:from:to; bh=zOi5slOy7R92h/YYe9tLwxZ24xLsuGr+/iDC/8WrjAI=; b=ZRmc0Z2wPiNebIp1ctpV5PkMu4YXo7mNahMvfJPkM2sIivp+lQsBXuQGkcWG73NISc t6F0KKtAQiqL4PCRRKCbMqE0JpAann57iLRH0mSM1B51Npj1ktTLMBernYhL7MYQYsdU mlG2/NWPY4vHVXSi3ekq/ZDjzOepvBBP5BhhmFndvk2ROSjkoBbX/hQl6Of1CBQZzs5c BiQkSFBDEYxG68pfkoOVZcAwdent2cT8oVGaykyrIyTTYoyauEB9VUFtGiSff42PbY0V GD1LYpK0YHhrgj42Z7QGmn9As3Mn93JA+OK2HsGOjkZxQsP1coPYdhAAxUI9JeqoupiN zqyg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:date:message-id:subject:from:to; bh=zOi5slOy7R92h/YYe9tLwxZ24xLsuGr+/iDC/8WrjAI=; b=AiX9hdtWyyI41n25xJeaNby3zIMfFCcdb+5EYoXUdIGf35XO4qpndfEHo0gck8XTL7 WIAHXkAhw1uAA8vreUWqerHOWxX53RJByKkOC/sXyFW3UgPKfwScT/rwKtgJaU2raQSs 5ghSUu3GSgj0K8c+mMpUD3ARujRkxTV73ceXBYe1m3h3tea8hKIXsje+8YqQdxtxyima 5cDdCaxmi5xdhcGDGKFSeUarh6qQpbydvmU7S+oTQujxW4kJD3jKQiWo/IPf4mYmYy6a 8ytQBnt/TwDCtKdXCNQG68wWA9FD/oHBSq12L4d4KtaaY5FKmFzJOMkIMnfVtAAhKG+8 kvyQ== X-Gm-Message-State: AOPr4FUiq9akNXdOeYaH6IzpP6n6D449mqN3qZPtZE0XifgA6g1uqjeMEX0h/iitMGWEw74HGgMGVN+PIpMkaA== MIME-Version: 1.0 X-Received: by 10.112.227.71 with SMTP id ry7mr4439210lbc.78.1460567027397; Wed, 13 Apr 2016 10:03:47 -0700 (PDT) Received: by 10.25.207.74 with HTTP; Wed, 13 Apr 2016 10:03:47 -0700 (PDT) Date: Wed, 13 Apr 2016 13:03:47 -0400 Message-ID: Subject: Slow update on column that is part of exclusion constraint From: Adam Brusselback To: pgsql-performance@postgresql.org Content-Type: multipart/alternative; boundary=001a11347e8ed6c0aa053060c299 X-Pg-Spam-Score: -2.0 (--) List-Archive: List-Help: List-ID: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: X-Mailing-List: pgsql-performance Precedence: bulk Sender: pgsql-performance-owner@postgresql.org --001a11347e8ed6c0aa053060c299 Content-Type: text/plain; charset=UTF-8 Hey all, been running into some performance issues with one of my tables, and it seems to be centered around index maintenance. I have a table to store aggregated prices that are derived from sale data over a configurable period, and a function that runs periodically that inserts new prices if necessary, or "inactivates" the old record and inserts new ones. We use that price in calculations, and store the price we used for a specific calculation for audit purposes, so the old record cannot just be updated or deleted. We need a new record every time. So to do that we have an "active_range" column on the table, and that is used to set the periods that each specific price was in use for (important for audit). The issue seems to be the exclusion constraint we have on the table to ensure data consistency... It is ridiculously slow to insert / update. To update 200 records, it takes 45 seconds. To update 1500 rows takes about 3.5 min. To build the constraint/index fresh on the table (1,392,085 rows) it takes about 4 min. Looking at the schema, is there just a clearly better way to do this? I know uuid is not supported by GiST indexes yet, so casting to text isn't great, but necessary (as far as I know...) at the moment. The table definition looks like so: CREATE TABLE price_generated ( price_generated_id uuid NOT NULL DEFAULT gen_random_uuid(), product_id uuid NOT NULL, company_id uuid NOT NULL, date_range daterange NOT NULL, average_price numeric NOT NULL, average_price_delivered numeric NOT NULL, low_price numeric NOT NULL, low_price_delivered numeric NOT NULL, high_price numeric NOT NULL, high_price_delivered numeric NOT NULL, uom_type_id uuid NOT NULL, active_range tstzrange NOT NULL DEFAULT tstzrange(now(), NULL::timestamp with time zone), CONSTRAINT price_generated_pkey PRIMARY KEY (price_generated_id), CONSTRAINT price_generated_company_id_fkey FOREIGN KEY (company_id) REFERENCES public.company (company_id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION, CONSTRAINT price_generated_product_id_fkey FOREIGN KEY (product_id) REFERENCES public.product (product_id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION, CONSTRAINT price_generated_company_product_date_active_excl EXCLUDE USING gist ((company_id::text) WITH =, (product_id::text) WITH =, date_range WITH &&, active_range WITH &&) ); CREATE INDEX idx_price_generated_prod_comp_date ON price_generated USING btree (product_id, company_id, date_range); --001a11347e8ed6c0aa053060c299 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable
Hey all, been running into some performance issues with on= e of my tables, and it seems to be centered around index maintenance.
<= br>
I have a table to store aggregated prices that are derived fr= om sale data over a configurable period, and a function that runs periodica= lly that inserts new prices if necessary, or "inactivates" the ol= d record and inserts new ones.=C2=A0 We use that price in calculations, and= store the price we used for a specific calculation for audit purposes, so = the old record cannot just be updated or deleted. We need a new record ever= y time.

So to do that we have an "active_rang= e" column on the table, and that is =C2=A0used to set the periods that= each specific price was in use for (important for audit).

The issue seems to be the exclusion constraint we have on the tabl= e to ensure data consistency... It is ridiculously slow to insert / update.= =C2=A0 To update 200 records, it takes 45 seconds. To update 1500 rows take= s about 3.5 min. To build the constraint/index fresh on the table (1,392,08= 5 rows) it takes about 4 min.

Looking at the schem= a, is there just a clearly better way to do this? I know uuid is not suppor= ted by GiST indexes yet, so casting to text isn't great, but necessary = (as far as I know...) at the moment.

The table def= inition looks like so:
CREATE TABLE price_generated
(
=C2=A0 price_generated_id uuid NOT NULL DEFAULT gen_random_uu= id(),
=C2=A0 product_id uuid NOT NULL,
=C2=A0 company_i= d uuid NOT NULL,
=C2=A0 date_range daterange NOT NULL,
= =C2=A0 average_price numeric NOT NULL,
=C2=A0 average_price_deliv= ered numeric NOT NULL,
=C2=A0 low_price numeric NOT NULL,
=C2=A0 low_price_delivered numeric NOT NULL,
=C2=A0 high_price= numeric NOT NULL,
=C2=A0 high_price_delivered numeric NOT NULL,<= /div>
=C2=A0 uom_type_id uuid NOT NULL,
=C2=A0 active_range t= stzrange NOT NULL DEFAULT tstzrange(now(), NULL::timestamp with time zone),=
=C2=A0 CONSTRAINT price_generated_pkey PRIMARY KEY (price_genera= ted_id),
=C2=A0 CONSTRAINT price_generated_company_id_fkey FOREIG= N KEY (company_id)
=C2=A0 =C2=A0 =C2=A0 REFERENCES public.company= (company_id) MATCH SIMPLE
=C2=A0 =C2=A0 =C2=A0 ON UPDATE NO ACTI= ON ON DELETE NO ACTION,
=C2=A0 CONSTRAINT price_generated_product= _id_fkey FOREIGN KEY (product_id)
=C2=A0 =C2=A0 =C2=A0 REFERENCES= public.product (product_id) MATCH SIMPLE
=C2=A0 =C2=A0 =C2=A0 ON= UPDATE NO ACTION ON DELETE NO ACTION,
=C2=A0 CONSTRAINT price_ge= nerated_company_product_date_active_excl EXCLUDE=C2=A0
=C2=A0 USI= NG gist ((company_id::text) WITH =3D, (product_id::text) WITH =3D, date_ran= ge WITH &&, active_range WITH &&)
);
<= div>
CREATE INDEX idx_price_generated_prod_comp_date
=C2=A0 ON price_generated
=C2=A0 USING btree
=C2= =A0 (product_id, company_id, date_range);
--001a11347e8ed6c0aa053060c299--