I Postgres-XL upprätthålls sekvenser i Global Transaction Manager (GTM) för att säkerställa att de tilldelas icke-konfliktiga värden när de inkrementeras från flera noder. Detta lägger till betydande overhead för en fråga som gör tusentals INSERT i en tabell med en seriell kolumn, ökar sekvensen en i taget och gör en nätverksresa till GTM, för varje INSERT.
Shaun Thomas i en nyligen publicerad blogg klagade på att INSERTs kör långsammare på Postgres-XL jämfört med vanilla PostgreSQL. Det finns redan ett sätt att förbättra prestanda för sekvenser, men det är uppenbarligen inte bra annonserat. Jag tänkte att det här är ett bra tillfälle att förklara anläggningen.
Postgres-XL tillhandahåller en användarinställbar GUC som heter sequence_range . Varje backend begär ett block av sekvensvärden som kontrolleras av denna GUC. Med tanke på att COPY populärt används för att massladda data i Postgres, åsidosätter Postgres-XL automatiskt denna GUC under COPY-drift och ställer in den på 1000, vilket dramatiskt förbättrar COPY-prestandan. Tyvärr, för vanliga INSERT, är standardvärdet 1 och om inte användaren uttryckligen anger sequence_range till ett rimligt högre värde blir INSERT-prestanda lidande. Här är ett exempel som använder samma exempelschema som användes av Shaun i hans blogginlägg.
CREATE TABLE sensor_log ( sensor_log_id SERIAL PRIMARY KEY, location VARCHAR NOT NULL, reading BIGINT NOT NULL, reading_date TIMESTAMP NOT NULL ) DISTRIBUTE BY HASH (sensor_log_id); postgres=# \timing Timing is on. postgres=# INSERT INTO sensor_log (location, reading, reading_date) SELECT s.id % 1000, s.id % 100, now() - (s.id || 's')::INTERVAL FROM generate_series(1, 40000) s(id); INSERT 0 40000 Time: 12067.911 ms postgres=# set sequence_range TO 1000; SET Time: 1.231 ms postgres=# INSERT INTO sensor_log (location, reading, reading_date) SELECT s.id % 1000, s.id % 100, now() - (s.id || 's')::INTERVAL FROM generate_series(1, 40000) s(id); INSERT 0 40000 Time: 397.406 ms
Så genom att ställa in sequence_range på rätt sätt till 1000 förbättrades prestandan för INSERT-frågan med nästan 30 gånger.
När den här funktionen lades till sattes standardvärdet för sequence_range GUC till 1 eftersom det kan lämna hål i sekvensvärdena. Men tittar vi på prestandaimplikationerna för ett mycket vanligt användningsfall, beslutade vi att öka standardvärdet till 1000 och detta har nu anslutits till XL9_5_STABLE-grenen av förvaret.
Det är viktigt att notera att medan ett högt värde på sequence_range kommer att förbättra prestandan för sekvenser och serier, den kan också lämna stora hål i sekvensintervallen eftersom sekvensintervallen cachelagras på backend-nivå. För att lösa det här problemet börjar Postgres-XL med det specificerade CACHE-parametervärdet som används vid tidpunkten för att skapa sekvenser och fördubblar det varje gång (begränsat av sequence_range) om sekvenser konsumeras i mycket hög hastighet.
Liknande förbättringar kan också uppnås genom att öka CACHE-parametervärdet för sekvensen så att en bit av sekvensvärden cachelagras på backend-nivån. Följande exempel visar hur man gör det för en seriell kolumn. Men sequence_range GUC tillhandahåller ett enkelt sätt att åsidosätta den globala standarden och säkerställer också att sekvenserna endast cachelagras när de ökar mycket snabbt.
postgres=# ALTER SEQUENCE sensor_log_sensor_log_id_seq CACHE 1000; ALTER SEQUENCE Time: 8.683 ms postgres=# SET sequence_range TO 1; SET Time: 2.341 ms postgres=# INSERT INTO sensor_log (location, reading, reading_date) SELECT s.id % 1000, s.id % 100, now() - (s.id || 's')::INTERVAL FROM generate_series(1, 40000) s(id); INSERT 0 40000 Time: 418.068 ms
Du kan välja någon av dessa tekniker för att förbättra prestandan. Fast nu när standardvärdet för sequence_range ändras till 1000, kanske inte många användare ser skillnaden i prestanda.