Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtp (Exim 4.84_2) (envelope-from ) id 1chytt-0007yz-MN for pgsql-performance@arkaria.postgresql.org; Sun, 26 Feb 2017 13:30:09 +0000 Received: from localhost ([127.0.0.1] helo=postgresql.org) by malur.postgresql.org with smtp (Exim 4.84_2) (envelope-from ) id 1chyts-0007AQ-Jv for pgsql-performance@arkaria.postgresql.org; Sun, 26 Feb 2017 13:30:08 +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 1chysB-0005AC-Gx for pgsql-performance@postgresql.org; Sun, 26 Feb 2017 13:28:23 +0000 Received: from shout01.mail.de ([2001:868:100:600::216]) by makus.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1chys4-0002Bv-Dz for pgsql-performance@postgresql.org; Sun, 26 Feb 2017 13:28:22 +0000 Received: from postfix03.mail.de (postfix03.bt.mail.de [10.0.121.127]) by shout01.mail.de (Postfix) with ESMTP id 0852C3FF78 for ; Sun, 26 Feb 2017 14:28:12 +0100 (CET) Received: from smtp02.mail.de (smtp02.bt.mail.de [10.0.121.212]) by postfix03.mail.de (Postfix) with ESMTP id E2C3940302 for ; Sun, 26 Feb 2017 14:28:11 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=mail.de; s=mailde201610; t=1488115691; bh=Mh7NZCgwcJoo7B9dg77TxREV7f9x/9hFTQ73waC1Imw=; h=To:From:Subject:Date:From; b=jcmHEH1HejksmOx2SlFCX3a7oIFH4Fkj0hdCccAJ+PgdHitPSEs6D5ECj0gRzOYat GFiweNyRHeDbfOkiTQxEAGRmJwODJ2/wbkr6deKphypLU1WCNBGrc88nIMzgMIzcZd 5HKAfG5zRwbZLrmY81oCOxaIlS7QLMOjP+9ZNL90= Received: from [127.0.0.1] (localhost [127.0.0.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by smtp02.mail.de (Postfix) with ESMTPSA id 7A1AF80492 for ; Sun, 26 Feb 2017 14:28:11 +0100 (CET) To: pgsql-performance@postgresql.org From: "Sven R. Kunze" Subject: Speeding up JSON + TSQUERY + GIN Message-ID: <2260e4e6-89ae-90dc-278c-81243b3c3510@mail.de> Date: Sun, 26 Feb 2017 14:28:10 +0100 MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="------------F0BBF301F859232548E85F09" X-purgate: clean X-purgate: This mail is considered clean (visit http://www.eleven.de for further information) X-purgate-type: clean X-purgate-Ad: Categorized by eleven eXpurgate (R) http://www.eleven.de X-purgate: This mail is considered clean (visit http://www.eleven.de for further information) X-purgate: clean X-purgate-size: 7385 X-purgate-ID: 154282::1488115691-00004FF0-739BDED4/0/0 X-Pg-Spam-Score: -2.7 (--) 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 This is a multi-part message in MIME format. --------------F0BBF301F859232548E85F09 Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit Hello everyone, I am currently evaluating the possibility of using PostgreSQL for storing and querying jsonb+tsvector queries. Let's consider this setup: create table docs (id serial primary key, meta jsonb); # generate 10M entries, cf. appendix create index docs_meta_idx ON docs using gin (meta jsonb_path_ops); create index docs_name_idx ON docs using gin (to_tsvector('english', meta->>'name')); create index docs_address_idx ON docs using gin (to_tsvector('english', meta->>'address')); Testing around with some smaller datasets, functionality-wise it's great. However increasing to 10M, things tend to slow down (using PostgreSQL 9.5): explain analyze select id from docs where meta @> '{"age": 20}'; Planning time: 0.121 ms Execution time: 4873.507 ms explain analyze select id from docs where meta @> '{"age": 20}'; Planning time: 0.122 ms Execution time: 206.289 ms explain analyze select id from docs where meta @> '{"age": 30}'; Planning time: 0.109 ms Execution time: 7496.886 ms explain analyze select id from docs where meta @> '{"age": 30}'; Planning time: 0.114 ms Execution time: 1169.649 ms explain analyze select id from docs where to_tsvector('english', meta->>'name') @@ to_tsquery('english', 'john'); Planning time: 0.179 ms Execution time: 10109.375 ms explain analyze select id from docs where to_tsvector('english', meta->>'name') @@ to_tsquery('english', 'john'); Planning time: 0.188 ms Execution time: 238.854 ms Using "select pg_prewarm('docs');" and on any of the indexes doesn't help either. After a "systemctl stop postgresql.service && sync && echo 3 > /proc/sys/vm/drop_caches && systemctl start postgresql.service" the age=20, 30 or name=john queries are slow again. Is there a way to speed up or to warm up things permanently? Regards, Sven Appendix I: example json: {"age": 20, "name": "Michelle Hernandez", "birth": "1991-08-16", "address": "94753 Tina Bridge Suite 318\\nEmilyport, MT 75302"} Appendix II: The Python script to generate fake json data. Needs "pip install faker". >>> python fake_json.py > test.json # generates 2M entries; takes some time >>> cat test.json | psql -c 'copy docs (meta) from stdin' >>> cat test.json | psql -c 'copy docs (meta) from stdin' >>> cat test.json | psql -c 'copy docs (meta) from stdin' >>> cat test.json | psql -c 'copy docs (meta) from stdin' >>> cat test.json | psql -c 'copy docs (meta) from stdin' -- fake_json.py -- import faker, json; fake = faker.Faker(); for i in range(2*10**6): print(json.dumps({"name": fake.name(), "birth": fake.date(), "address": fake.address(), "age": fake.random_int(0,100)}).replace('\\n', '\\\\n')) --------------F0BBF301F859232548E85F09 Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: 8bit Hello everyone,

I am currently evaluating the possibility of using PostgreSQL for storing and querying jsonb+tsvector queries. Let's consider this setup:

create table docs (id serial primary key, meta jsonb);
# generate 10M entries, cf. appendix
create index docs_meta_idx ON docs using gin (meta jsonb_path_ops);
create index docs_name_idx ON docs using gin (to_tsvector('english', meta->>'name'));
create index docs_address_idx ON docs using gin (to_tsvector('english', meta->>'address'));


Testing around with some smaller datasets, functionality-wise it's great. However increasing to 10M, things tend to slow down (using PostgreSQL 9.5):


explain analyze select id from docs where meta @> '{"age": 20}';
 Planning time: 0.121 ms
 Execution time: 4873.507 ms

explain analyze select id from docs where meta @> '{"age": 20}';
 Planning time: 0.122 ms
 Execution time: 206.289 ms



explain analyze select id from docs where meta @> '{"age": 30}';
 Planning time: 0.109 ms
 Execution time: 7496.886 ms

explain analyze select id from docs where meta @> '{"age": 30}';
 Planning time: 0.114 ms
 Execution time: 1169.649 ms



explain analyze select id from docs where to_tsvector('english', meta->>'name') @@ to_tsquery('english', 'john');
 Planning time: 0.179 ms
 Execution time: 10109.375 ms

explain analyze select id from docs where to_tsvector('english', meta->>'name') @@ to_tsquery('english', 'john');
 
Planning time: 0.188 ms
 Execution time: 238.854 ms


Using "select pg_prewarm('docs');" and on any of the indexes doesn't help either.
After a "systemctl stop postgresql.service && sync && echo 3 > /proc/sys/vm/drop_caches && systemctl start postgresql.service" the age=20, 30 or name=john queries are slow again.


Is there a way to speed up or to warm up things permanently?


Regards,
Sven


Appendix I:

example json:

{"age": 20, "name": "Michelle Hernandez", "birth": "1991-08-16", "address": "94753 Tina Bridge Suite 318\\nEmilyport, MT 75302"}



Appendix II:


The Python script to generate fake json data. Needs "pip install faker".

>>> python fake_json.py > test.json  # generates 2M entries; takes some time
>>> cat test.json | psql -c 'copy docs (meta) from stdin'
>>> cat test.json | psql -c 'copy docs (meta) from stdin'
>>> cat test.json | psql -c 'copy docs (meta) from stdin'
>>> cat test.json | psql -c 'copy docs (meta) from stdin'
>>> cat test.json | psql -c 'copy docs (meta) from stdin'


-- fake_json.py --

import faker, json;
fake = faker.Faker();
for i in range(2*10**6):
    print(json.dumps({"name": fake.name(), "birth": fake.date(), "address": fake.address(), "age": fake.random_int(0,100)}).replace('\\n', '\\\\n'))

--------------F0BBF301F859232548E85F09--