Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtp (Exim 4.80) (envelope-from ) id 1Wfz0A-000840-Va for pgsql-performance@arkaria.postgresql.org; Thu, 01 May 2014 21:58:47 +0000 Received: from localhost ([127.0.0.1] helo=postgresql.org) by malur.postgresql.org with smtp (Exim 4.80) (envelope-from ) id 1Wfz0A-0007Ey-GS for pgsql-performance@arkaria.postgresql.org; Thu, 01 May 2014 21:58:46 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtp (Exim 4.80) (envelope-from ) id 1Wfz09-0007Es-Gi for pgsql-performance@postgresql.org; Thu, 01 May 2014 21:58:45 +0000 Received: from prod2.officenet.no ([195.159.87.246]) by magus.postgresql.org with esmtp (Exim 4.80) (envelope-from ) id 1Wfz05-0007ZX-RU for pgsql-performance@postgresql.org; Thu, 01 May 2014 21:58:45 +0000 Received: from localhost ([127.0.0.1] helo=prod2) by prod2.officenet.no with esmtp (Exim 4.76) (envelope-from ) id 1Wfz05-0006mH-3Y for pgsql-performance@postgresql.org; Thu, 01 May 2014 23:58:41 +0200 Date: Thu, 1 May 2014 23:58:40 +0200 (CEST) From: Andreas Joseph Krogh To: pgsql-performance@postgresql.org Message-ID: In-Reply-To: <5362C08D.8060008@fuzzy.cz> Subject: Re: Optimize query for listing un-read messages MIME-Version: 1.0 X-Mailer: OfficeNet Mail 1.9.0-SNAPSHOT X-Pg-Spam-Score: -1.9 (-) Content-Type: multipart/related; boundary="----=_Part_56_1943156868.1398981520889" 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 ------=_Part_55_1196583568.1398981520889 Content-Type: multipart/related; boundary="----=_Part_56_1943156868.1398981520889" ------=_Part_56_1943156868.1398981520889 Content-Type: multipart/alternative; boundary="----=_Part_57_64209921.1398981520906" ------=_Part_57_64209921.1398981520906 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable P=C3=A5 torsdag 01. mai 2014 kl. 23:45:49, skrev Tomas Vondra >: On 1.5.2014 23:19, Andreas Joseph Krogh wrote: > P=C3=A5 torsdag 01. mai 2014 kl. 23:02:13, skrev Pavel Stehule > >: > >=C2=A0 =C2=A0 =C2=A0 >=C2=A0 =C2=A0 =C2=A0 >=C2=A0 =C2=A0 =C2=A02014-05-01 22:30 GMT+02:00 Andreas Joseph Krogh =C2=A0 =C2=A0 =C2=A0>: > >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0P=C3=A5 torsdag 01. mai 2014 kl. 21:53:= 32, skrev Pavel Stehule >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0>: > >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A02014-05-01 21:39 GMT+02:0= 0 Andreas Joseph Krogh >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0>: > >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0P=C3=A5 tor= sdag 01. mai 2014 kl. 21:30:39, skrev Pavel >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0Stehule =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0>: > >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0Hello >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0[snip] >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0I had a perfect success on similar use case with >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0descent ordered partial index > >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=20 =C2=A0http://www.postgresql.org/docs/9.3/interactive/sql-createindex.html > >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0I'm not get= ting good performance. Are you able to craft >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0an example = using my schema and partial index? > >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0maybe some like >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0CREATE INDEX ON message_p= roperty (person_id, message_id) >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0WHERE pr.is_read >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0When I am thinking about = your schema, it is designed well, >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0but it is not index frien= dly, so for some fast access you >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0should to hold a cache (t= able) of unread messages > >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0Ah, that's what I was hoping to not hav= ing to do. In my system, >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0messages arrive all the time and having= to update a cache for >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0all new messages for all users seems me= ssy... Seems I could just >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0as well create a message_property for a= ll users when a new >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0message arrives, so I can INNER JOIN it= and get good >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0performance. But that table will quickl= y grow *very* large... > >=C2=A0 =C2=A0 =C2=A0 >=C2=A0 =C2=A0 =C2=A0What you need is a JOIN index, that is not possible i= n Postgres. >=C2=A0 =C2=A0 =C2=A0 >=C2=A0 =C2=A0 =C2=A0I afraid so some "ugly" solutions is necessary (when = you require >=C2=A0 =C2=A0 =C2=A0extra fast access). You need a index (small index) an= d it require >=C2=A0 =C2=A0 =C2=A0some existing set - you cannot do index on the differ= ence two sets. >=C2=A0 =C2=A0 =C2=A0 >=C2=A0 =C2=A0 =C2=A0I expect so some flag on the relation "message" - lik= e "it should >=C2=A0 =C2=A0 =C2=A0not be not read" can helps little bit - and can be us= ed in partial >=C2=A0 =C2=A0 =C2=A0index as conditions. Other possibility is some varian= t of >=C2=A0 =C2=A0 =C2=A0partitioning - you can divide a messages and users to= distinct sets >=C2=A0 =C2=A0 =C2=A0and then you decrease a number of possible combinatio= ns. > >=C2=A0 > Just curious: > Is such a JOIN index possible in other DBs, if so - which? > Can other DBs do index on difference between two sets? > Will PG ever have this, is it even possible? I'm not aware of such database, but maybe it's possible at least for some cases. But I'd expect that to significantly depend on the schema. And I'm not aware of any such effort in case of PostgreSQL, do don't hold your breath. IMHO the problem with your schema is that while each 'read' message has a matching row in message_property, 'undread' messages may or may not have a matching row. Is there a particular reason for that? =C2=A0 =C2=A0 = Yes. The=20 point is that maintaining a message_property pair for all messages for all= =20 users in the system imposes quite a maintainance-headache. As the schema is= now=20 any new message is per definition un-read, and when a user reads it then it= =20 gets an entry with is_read=3Dtrue in message_property. This table holds oth= er=20 properties too. This way I'm avoiding having to book-keep so much when a ne= w=20 message arrives and when a new user is created. A message in my system does= not=20 necessarily have only one recipient, it might have one, many or none, and m= ight=20 be visible to many. =C2=A0 If you could get rid of this, i.e. require that = each pair=20 (message, recipient) has a row in message_propery (irrespectedly whether the message was read or not), you can do this: CREATE INDEX message_unread_idx =C2=A0 =C2=A0 ON message_property(message_id, person_id) WHERE (NOT is_rea= d) and then simply do the query like this: SELECT =C2=A0 =C2=A0 m.id, =C2=A0 =C2=A0 prop.person_id, =C2=A0 =C2=A0 prop.is_read, =C2=A0 =C2=A0 m.subject FROM messages m JOIN message_property p ON (m.id =3D p.message_id) WHERE (NOT is_read) AND person_id =3D :pid and I'd expect this to use the partial index, and being much faster. =C2= =A0 I'm=20 aware of the performance-gain using such a plain JOIN-query. =C2=A0 Thanks = for your=20 feedback. =C2=A0 -- Andreas Jospeh Krogh CTO / Partner - Visena AS Mobile: = +47 909=20 56 963 andreas@visena.com www.visena.com=20 =C2=A0 ------=_Part_57_64209921.1398981520906 Content-Type: text/html;charset=UTF-8 Content-Transfer-Encoding: quoted-printable
P=C3=A5 torsdag 01. mai 2014 kl. 23:45:49, skrev Tomas Vondra <tv@fuzzy.cz>:
On = 1.5.2014 23:19, Andreas Joseph Krogh wrote:
> P=C3=A5 torsdag 01. mai 2014 kl. 23:02:13, skrev Pavel Stehule
> <pavel.stehule@gmail.com <mailto:pavel.stehule@gmail.com>>= :
>
>=C2=A0 =C2=A0 =C2=A0
>=C2=A0 =C2=A0 =C2=A0
>=C2=A0 =C2=A0 =C2=A02014-05-01 22:30 GMT+02:00 Andreas Joseph Krogh <= ;andreas@visena.com
>=C2=A0 =C2=A0 =C2=A0<mailto:andreas@visena.com>>:
>
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0P=C3=A5 torsdag 01. mai 2014 kl. 21:5= 3:32, skrev Pavel Stehule
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0<pavel.stehule@gmail.com <mailt= o:pavel.stehule@gmail.com>>:
>
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A02014-05-01 21:39 GMT+02= :00 Andreas Joseph Krogh
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0<andreas@visena.com = <mailto:andreas@visena.com>>:
>
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0P=C3=A5 t= orsdag 01. mai 2014 kl. 21:30:39, skrev Pavel
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0Stehule &= lt;pavel.stehule@gmail.com
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0<mailt= o:pavel.stehule@gmail.com>>:
>
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0Hello
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0[snip]
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0I had a perfect success on similar use case with
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0descent ordered partial index
>
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0http://www.postgresql.org/docs/9.3/interactive/sql-createindex.html >
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0I'm not g= etting good performance. Are you able to craft
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0an exampl= e using my schema and partial index?
>
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0maybe some like
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0CREATE INDEX ON message= _property (person_id, message_id)
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0WHERE pr.is_read
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0When I am thinking abou= t your schema, it is designed well,
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0but it is not index fri= endly, so for some fast access you
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0should to hold a cache = (table) of unread messages
>
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0Ah, that's what I was hoping to not h= aving to do. In my system,
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0messages arrive all the time and havi= ng to update a cache for
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0all new messages for all users seems = messy... Seems I could just
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0as well create a message_property for= all users when a new
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0message arrives, so I can INNER JOIN = it and get good
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0performance. But that table will quic= kly grow *very* large...
>
>=C2=A0 =C2=A0 =C2=A0
>=C2=A0 =C2=A0 =C2=A0What you need is a JOIN index, that is not possible= in Postgres.
>=C2=A0 =C2=A0 =C2=A0
>=C2=A0 =C2=A0 =C2=A0I afraid so some "ugly" solutions is nece= ssary (when you require
>=C2=A0 =C2=A0 =C2=A0extra fast access). You need a index (small index) = and it require
>=C2=A0 =C2=A0 =C2=A0some existing set - you cannot do index on the diff= erence two sets.
>=C2=A0 =C2=A0 =C2=A0
>=C2=A0 =C2=A0 =C2=A0I expect so some flag on the relation "message= " - like "it should
>=C2=A0 =C2=A0 =C2=A0not be not read" can helps little bit - and ca= n be used in partial
>=C2=A0 =C2=A0 =C2=A0index as conditions. Other possibility is some vari= ant of
>=C2=A0 =C2=A0 =C2=A0partitioning - you can divide a messages and users = to distinct sets
>=C2=A0 =C2=A0 =C2=A0and then you decrease a number of possible combinat= ions.
>
>=C2=A0
> Just curious:
> Is such a JOIN index possible in other DBs, if so - which?
> Can other DBs do index on difference between two sets?
> Will PG ever have this, is it even possible?

I'm not aware of such database, but maybe it's possible at least for
some cases. But I'd expect that to significantly depend on the schema.
And I'm not aware of any such effort in case of PostgreSQL, do don't
hold your breath.

IMHO the problem with your schema is that while each 'read' message has
a matching row in message_property, 'undread' messages may or may not
have a matching row. Is there a particular reason for that?
=C2= =A0
=C2= =A0
Yes= . The point is that maintaining a message_property pair for all messages fo= r all users in the system imposes quite a maintainance-headache. As the sch= ema is now any new message is per definition un-read, and when a user reads= it then it gets an entry with is_read=3Dtrue in message_property. This tab= le holds other properties too. This way I'm avoiding having to book-keep so= much when a new message arrives and when a new user is created. A message = in my system does not necessarily have only one recipient, it might have on= e, many or none, and might be visible to many.
=C2= =A0
If = you could get rid of this, i.e. require that each pair (message,
recipient) has a row in message_propery (irrespectedly whether the
message was read or not), you can do this:

CREATE INDEX message_unread_idx
=C2=A0 =C2=A0 ON message_property(message_id, person_id) WHERE (NOT is_read= )

and then simply do the query like this:

SELECT
=C2=A0 =C2=A0 m.id,
=C2=A0 =C2=A0 prop.person_id,
=C2=A0 =C2=A0 prop.is_read,
=C2=A0 =C2=A0 m.subject
FROM messages m JOIN message_property p ON (m.id =3D p.message_id)
WHERE (NOT is_read) AND person_id =3D :pid

and I'd expect this to use the partial index, and being much faster.
=C2=A0
I'm aware of the performance-gain using such a plain JOIN-query.
=C2=A0
Thanks for your feedback.
=C2=A0
--
Andrea= s Jospeh Krogh
CTO / Partner<= /span> - Visena AS
Mobile: +47 90= 9 56 963
3D""
=C2=A0
------=_Part_57_64209921.1398981520906-- ------=_Part_56_1943156868.1398981520889 Content-Type: image/png Content-Transfer-Encoding: base64 Content-Disposition: inline Content-ID: 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 ------=_Part_56_1943156868.1398981520889-- ------=_Part_55_1196583568.1398981520889--