SaaS ਐਪ ਲਈ PostgreSQL ਇੰਡੈਕਸ: ਫਿਲਟਰ, search, JSONB ਅਤੇ ਐਰੇ ਵਰਗੇ ਅਸਲ ਕਵੈਰੀ ਆਕਾਰਾਂ ਦੇ ਆਧਾਰ 'ਤੇ B-tree, GIN ਜਾਂ GiST ਚੁਣੋ।

ਇੱਕ ਇੰਡੈਕਸ ਇਹ ਬਦਲ ਦਿੰਦਾ ਹੈ ਕਿ PostgreSQL ਰਿਕਾਰਡ ਕਿਵੇਂ ਲੱਭਦਾ ਹੈ। ਇੰਡੈਕਸ ਨਾ ਹੋਣ ਤੇ ਡੇਟਾਬੇਸ ਨੂੰ ਅਕਸਰ ਪੂਰੇ ਟੇਬਲ ਦਾ ਬਹੁਤ ਹਿੱਸਾ ਪੜ੍ਹਨਾ ਪ ੇਂਦਾ ਹੈ (sequential scan) ਅਤੇ ਫਿਰ ਬਹੁਤ ਕੁਝ ਬਿਨਾਂ-ਲੋੜ ਦੇ ਰਿੱਜੈਕਟ ਕਰਦੇ ਹੋਏ। ਸਹੀ ਇੰਡੈਕਸ ਨਾਲ, ਇਹ ਸਿੱਧਾ ਮਿਲਦੇ ਹੋਏ ਰੋਜ਼ਾਂ ਤੇ ਜਾ ਸਕਦਾ ਹੈ (index lookup) ਅਤੇ ਫਿਰ ਸਿਰਫ਼ ਉਹੀ ਰਿਕਾਰਡ ਫੈਚ ਕਰਦਾ ਹੈ ਜੋ ਲੋੜੀਦੇ ਹਨ।
SaaS ਵਿੱਚ ਤੁਸੀਂ ਇਹ ਜ਼ਲਦੀ ਮਹਿਸੂਸ ਕਰਦੇ ਹੋ ਕਿਉਂਕਿ ਰੋਜ਼ਾਨਾ ਸਕਰੀਨਾਂ ਕਾਫੀ ਕਵੈਰੀ-ਭਾਰੀ ਹੁੰਦੀਆਂ ਹਨ। ਇਕ single click ਕਈ ਨਿਰਣਾਇਕ ਪੜ੍ਹਾਈਆਂ ਚਲਾ ਸਕਦੀ ਹੈ: ਲਿਸਟ ਪੇਜ, ਇਕ ਕੁੱਲ ਗਿਣਤੀ, ਕੁਝ ਡੈਸ਼ਬੋਰਡ ਕਾਰਡ ਅਤੇ ਇੱਕ search box। ਜਦੋਂ ਟੇਬਲ ਹਜ਼ਾਰਾਂ ਤੋਂ ਲੈ ਕੇ ਮਿਲੀਅਨ ਰਿਕਾਰਡਾਂ ਤੱਕ ਵਧਦੀ ਹੈ, ਪਹਿਲਾਂ ਜੋ ਤੁਰੰਤ ਲੱਗਦਾ ਸੀ ਓਹੀ ਕਵੈਰੀ ਹੋਣ ਤੇ ਦੇਰੇ ਹੋ ਸਕਦੀ ਹੈ।
ਇੱਕ ਆਮ ਉਦਾਹਰਣ Orders ਦਾ ਪੇਜ ਹੈ ਜੋ status ਅਤੇ date ਨਾਲ filter ਹੁੰਦਾ ਹੈ, newest ਪਹਿਲਾਂ sort ਹੁੰਦਾ ਹੈ ਅਤੇ pagination ਹੁੰਦੀ ਹੈ। ਜੇ PostgreSQL ਨੂੰ ਪੂਰੇ orders ਟੇਬਲ ਨੂੰ ਸਕੈਨ ਕਰਨਾ ਪੈਂਦਾ ਹੈ ਤਾਂ ਹਰ page load ਵਧੇਰੇ ਕੰਮ ਕਰਦਾ ਹੈ। ਇੱਕ ਵਧੀਆ ਇੰਡੈਕਸ ਇਸਨੂੰ ਸਹੀ ਡੇਟਾ ਵਾਲੇ ਹਿੱਸੇ ਤੇ ਤੇਜ਼ ਜੰਪ ਵਿੱਚ ਬਦਲ ਦੇਂਦਾ ਹੈ।
ਇੰਡੈਕਸ ਮੁਫ਼ਤ ਨਹੀਂ ਹੁੰਦੇ। ਹਰ ਇੱਕ ਇੰਡੈਕਸ ਕੁਝ ਖਾਸ ਕਵੈਰੀਆਂ ਲਈ ਤੇਜ਼ ਪੜ੍ਹਾਈ ਖਰੀਦਦਾ ਹੈ, ਪਰ ਇਸ ਨਾਲ ਲਿਖਣ (INSERT/UPDATE/DELETE) ਦੀਆਂ ਓਪਰੇਸ਼ਨਾਂ ਥੋੜ੍ਹ੍ਹੀਆਂ ਹੌਲੀ ਹੋ ਜਾਂਦੀਆਂ ਹਨ (ਕਿਉਂਕਿ ਇੰਡੈਕਸ ਵੀ ਅਪਡੇਟ ਹੋਣੀ ਪੈਂਦੀ ਹੈ) ਅਤੇ ਸਟੋਰੇਜ ਦੀ ਲੋੜ ਵੱਧਦੀ ਹੈ (ਅਤੇ cache 'ਤੇ ਹੋਰ ਦਬਾਅ ਆਉਂਦਾ ਹੈ)। ਇਸ ਕਾਰਨ ਤੁਹਾਨੂੰ ਹਮੇਸ਼ਾਂ ਹਕੀਕਤੀ ਕਵੈਰੀ ਪੈਟਰਨ ਤੋਂ ਸ਼ੁਰੂ ਕਰਨਾ ਚਾਹੀਦਾ ਹੈ, ਨਾ ਕਿ ਸਿੱਧੇ ਤੌਰ 'ਤੇ ਕਿਸੇ ਇੰਡੈਕਸ ਟਾਈਪ ਤੋਂ।
ਇੱਕ ਸਧਾਰਣ ਨਿਯਮ ਜੋ ਬੇਕਾਰ ਕੰਮ ਤੋਂ ਬਚਾਉਂਦਾ ਹੈ: ਕੇਵਲ ਉਹੀ ਇੰਡੈਕਸ ਸ਼ਾਮਿਲ ਕਰੋ ਜਦੋਂ ਤੁਸੀਂ ਇੱਕ ਖਾਸ, ਅਕਸਰ ਚੱਲ ਰਹੀ ਕਵੈਰੀ ਦੱਸ ਸਕਦੇ ਹੋ ਜੋ ਉਹ ਤੇਜ਼ ਕਰੇਗੀ। ਜੇ ਤੁਸੀਂ chat-driven builder ਵਰਤਦੇ ਹੋ ਜਿਵੇਂ Koder.ai, ਤਾਂ ਇਹ ਤੁਹਾਡੀ ਲਿਸਟ ਪੇਜਾਂ ਅਤੇ ਡੈਸ਼ਬੋਰਡਾਂ ਦੇ ਪਿੱਛੇ ਆਉਂਦੇ SQL ਨੂੰ ਕੈਪਚਰ ਕਰਨ ਵਿੱਚ ਮਦਦ ਕਰਦਾ ਹੈ ਅਤੇ ਉਸਨੂੰ ਤੁਹਾਡੇ ਇੰਡੈਕਸ ਦੀ ਚੈਕਲਿਸਟ ਬਣਾਉਣਾ ਵਰਗਾ ਬਣਾਉਂਦਾ ਹੈ।
ਬਹੁਤ ਸਾਰੀ ਇੰਡੈਕਸਿੰਗ ਦੀ ਘਲਤਫ਼ਹਮੀ ਤਦ ਦੂਰ ਹੋ ਜਾਦੀ ਹੈ ਜਦੋਂ ਤੁਸੀਂ ਫੀਚਰਾਂ (JSON, search, arrays) ਦੀ ਸੋਚ ਛੱਡ ਕੇ ਕਵੈਰੀ ਆਕਾਰ 'ਤੇ ਧਿਆਨ ਦੇਂਦੇ ਹੋ: WHERE clause ਕੀ ਕਰ ਰਿਹਾ ਹੈ, ਅਤੇ ਨਤੀਜੇ ਕਿਵੇਂ order ਹੋਣੇ ਚਾਹੀਦੇ ਹਨ?
ਜਦੋਂ ਤੁਹਾਡੀ ਕਵੈਰੀ ਆਮ comparisons ਵਰਗੀ ਲੱਗਦੀ ਹੈ ਅਤੇ ਤੁਸੀਂ sort order ਦੀ ਪਰਵਾਹ ਕਰਦੇ ਹੋ, ਤਾਂ B-tree ਵਰਤੋਂ। ਇਹ equality, ranges ਅਤੇ joins ਲਈ ਮਜਬੂਤ ਹੈ।
ਉਦਾਹਰਨ ਆਕਾਰ: tenant_id = ?, status = 'active', created_at \u003e= ?, users.id = orders.user_id ਨਾਲ joining, ਜਾਂ ORDER BY created_at DESC ਨਾਲ “latest first” ਦਿਖਾਉਣਾ।
GIN (Generalized Inverted Index) ਉਸ ਵੇਲੇ ਚੰਗਾ ਕੰਮ ਕਰਦਾ ਹੈ ਜਦੋਂ ਇਕ ਕਾਲਮ ਵਿੱਚ ਕਈ items ਹੁੰਦੇ ਹਨ ਅਤੇ ਤੁਸੀਂ ਪੁੱਛਦੇ ਹੋ, “ਕੀ ਇਹ X ਰੱਖਦਾ ਹੈ?” ਇਹ JSONB keys, array elements ਅਤੇ full-text vectors ਨਾਲ ਆਮ ਹੈ।
ਉਦਾਹਰਨ ਆਕਾਰ: metadata @\u003e {'plan':'pro'} (JSONB), tags @\u003e ARRAY['urgent'], ਜਾਂ to_tsvector(body) @@ plainto_tsquery('reset password')।
GiST (Generalized Search Tree) ਉਹਨਾਂ ਸਵਾਲਾਂ ਲਈ ਫਿੱਟ ਹੁੰਦਾ ਹੈ ਜੋ distance ਜਾਂ overlap ਬਾਰੇ ਹੁੰਦੇ ਹਨ, ਜਿੱਥੇ values ranges ਜਾਂ shapes ਵਰਗੇ ਵਿਹਾਰ ਕਰਦੇ ਹਨ। ਇਹ ਅਕਸਰ range types, geometric data ਅਤੇ ਕੁਝ “closest match” searches ਲਈ ਵਰਤਿਆ ਜਾਂਦਾ ਹੈ।
ਉਦਾਹਰਨ ਆਕਾਰ: range ਕਾਲਮਾਂ ਨਾਲ overlapping time windows, similarity-style searches (ਉਦਾਹਰਨ ਤੌਰ ਤੇ trigram operators ਨਾਲ), ਜਾਂ spatial queries (ਜੇ ਤੁਸੀਂ PostGIS ਵਰਤਦੇ ਹੋ)।
ਇੱਕ ਪ੍ਰਾਇਕਟੀਕਲ ਤਰੀਕਾ:
ਇੰਡੈਕਸ ਪੜ੍ਹਾਈ ਨੂੰ ਤੇਜ਼ ਕਰਦੇ ਹਨ, ਪਰ ਲਿਖਣ ਸਮੇਂ ਅਤੇ ਡਿਸਕ ਦੀ ਕੀਮਤ ਆਉਂਦੀ ਹੈ। SaaS ਵਿੱਚ ਇਹ ਟਰੇਡ-ਆਫ਼ events, sessions ਅਤੇ activity logs ਵਰਗੀਆਂ hot tables 'ਤੇ ਸਭ ਤੋਂ ਜ਼ਿਆਦਾ ਮਹੱਤਵ ਰੱਖਦਾ ਹੈ।
ਜ਼ਿਆਦਾਤਰ SaaS ਲਿਸਟ ਸਕਰੀਨਾਂ ਦਾ ਆਕਾਰ ਇੱਕੋ-ਜਿਹਾ ਹੁੰਦਾ ਹੈ: ਇਕ tenant boundary, ਕੁਝ ਫਿਲਟਰ ਅਤੇ ਇੱਕ ਪੇਸ਼ ਕੀਤੀ ਹੋਈ sort। B-tree ਇਥੇ ਡਿਫ਼ਾਲਟ ਚੋਣ ਹੁੰਦਾ ਹੈ ਅਤੇ ਇਹ ਆਮ ਤੌਰ 'ਤੇ ਰੱਖਣ ਲਈ ਸਭ ਤੋਂ ਸਸਤਾ ਹੁੰਦਾ ਹੈ।
ਆਮ ਪੈਟਰਨ ਹੈ WHERE tenant_id = ? ਨਾਲ ਫਿਲਟਰਾਂ ਜਿਵੇਂ status = ?, user_id = ?, ਅਤੇ ਇੱਕ time range ਜਿਵੇਂ created_at \u003e= ?। composite B-tree ਇੰਡੈਕਸਾਂ ਲਈ, equality filters ਨੂੰ ਪਹਿਲਾਂ ਰੱਖੋ (ਉਹ ਕਾਲਮ ਜਿਨ੍ਹਾਂ ਨੂੰ ਤੁਸੀਂ = ਨਾਲ ਮੇਚ ਕਰਦੇ ਹੋ), ਫਿਰ ਉਹ ਕਾਲਮ ਜੋ ਤੁਸੀਂ sort ਕਰਦੇ ਹੋ।
ਅਸਲ ਯਮ:
tenant_id ਨਾਲ ਸ਼ੁਰੂ ਕਰੋ।= ਫਿਲਟਰਾਂ ਨੂੰ ਅਗੇ ਰੱਖੋ (ਅਕਸਰ status, user_id)।ORDER BY ਕਾਲਮ ਨੂੰ ਆਖਿਰ ਰੱਖੋ (ਅਕਸਰ created_at ਜਾਂ id)।INCLUDE ਵਰਤੋ ਬਿਨਾਂ key ਨੂੰ ਵੱਡਾ ਕੀਤੇ।ਇੱਕ 현실ੀ ਉਦਾਹਰਨ: Tickets ਪੇਜ ਜੋ newest items ਪਹਿਲਾਂ ਦਿਖਾਉਂਦਾ ਹੈ ਅਤੇ status ਨਾਲ filter ਕਰਦਾ ਹੈ।
-- Query
SELECT id, status, created_at, title
FROM tickets
WHERE tenant_id = $1
AND status = $2
ORDER BY created_at DESC
LIMIT 50;
-- Index
CREATE INDEX tickets_tenant_status_created_at_idx
ON tickets (tenant_id, status, created_at DESC)
INCLUDE (title);
ਉਹ ਇੰਡੈਕਸ ਫਿਲਟਰ ਅਤੇ sort ਦੋਹਾਂ ਨੂੰ support ਕਰਦਾ ਹੈ, ਇਸ ਲਈ Postgres ਵੱਡੇ result set ਨੂੰ sort ਕਰਨ ਤੋਂ ਬਚ ਸਕਦਾ ਹੈ। INCLUDE (title) ਹਿੱਸਾ list page ਨੂੰ ਘੱਟ table pages ਛੁਹਣ ਵਿੱਚ ਸਹਾਇਤਾ ਕਰਦਾ ਹੈ, ਜਦਕਿ ਇੰਡੈਕਸ keys ਫਿਲਟਰਿੰਗ ਅਤੇ ordering 'ਤੇ ਕੇਂਦਰਤ ਰਹਿੰਦੇ ਹਨ।
Time ranges ਲਈ ਵੀ ਇੱਕੋ ਹੀ ਆਈਡੀਆ ਲਾਗੂ ਹੁੰਦੀ ਹੈ:
SELECT id, created_at
FROM events
WHERE tenant_id = $1
AND created_at \u003e= $2
AND created_at \u003c $3
ORDER BY created_at DESC
LIMIT 100;
CREATE INDEX events_tenant_created_at_idx
ON events (tenant_id, created_at DESC);
Pagination ਉਹ ਜਗ੍ਹਾ ਹੈ ਜਿੱਥੇ ਬਹੁਤ ਸਾਰੇ SaaS ਐਪਸ ਸੈਲੋਂ ਹੋ ਜਾਂਦੇ ਹਨ। Offset pagination (OFFSET 50000) ਡੇਟਾਬੇਸ ਨੂੰ ਬਹੁਤ ਸਾਰੀਆਂ rows ਦੇ ਰਾਹੋਂ ਲੰਘਣ 'ਤੇ ਮਜ਼ਬੂਰ ਕਰਦਾ ਹੈ। Seek pagination last seen sort key ਵਰਤ ਕੇ ਤੇਜ਼ ਰਹਿੰਦੀ ਹੈ:
SELECT id, created_at
FROM tickets
WHERE tenant_id = $1
AND created_at \u003c $2
ORDER BY created_at DESC
LIMIT 50;
ਸਹੀ B-tree ਇੰਡੈਕਸ ਨਾਲ ਇਹ ਟੇਬਲ ਵੱਡੀ ਹੋਣ ਦੇ ਬਾਵਜੂਦ ਵੀ ਤੇਜ਼ ਰਹਿੰਦਾ ਹੈ।
ਜ਼ਿਆਦਾਤਰ SaaS ਐਪਸ multi-tenant ਹੁੰਦੀਆਂ ਹਨ: ਹਰ ਕਵੈਰੀ ਨੂੰ ਇੱਕ tenant ਦੇ ਅੰਦਰ ਰਹਿਣਾ ਪੈਂਦਾ ਹੈ। ਜੇ ਤੁਹਾਡੇ ਇੰਡੈਕਸ tenant_id ਨਹੀਂ ਰੱਖਦੇ, PostgreSQL ਫਿਰ ਵੀ ਰਿਕਾਰਡ ਤੇਜ਼ੀ ਨਾਲ ਲੱਭ ਸਕਦਾ ਹੈ, ਪਰ ਅਕਸਰ ਇਹ ਲੋੜ ਤੋਂ ਬੇਸ਼umar ਇੰਡੈਕਸ ਐਂਟਰੀਜ਼ ਸਕੈਨ ਕਰਦਾ ਹੈ। Tenant-aware indexes ਹਰ ਟੇਨੈਂਟ ਦੇ ਡੇਟਾ ਨੂੰ ਇੰਡੈਕਸ ਦੇ ਅੰਦਰ ਇਕੱਠੇ ਰੱਖਦੇ ਹਨ ਤਾਂ ਕਿ ਆਮ ਸਕਰੀਨਾਂ ਤੇਜ਼ ਅਤੇ ਪ੍ਰੀਡਿਕਟੇਬਲ ਰਹਿਣ।
ਇੱਕ ਸਧਾਰਨ ਨਿਯਮ: ਜਦੋਂ ਕਵੈਰੀ ਹਮੇਸ਼ਾਂ tenant ਨਾਲ ਫਿਲਟਰ ਹੁੰਦੀ ਹੈ ਤਾਂ ਇੰਡੈਕਸ ਵਿੱਚ tenant_id ਨੂੰ ਪਹਿਲਾਂ ਰੱਖੋ। ਫਿਰ ਜ਼ਿਆਦਾ ਵਰਤੇ ਜਾਂਦੇ ਫਿਲਟਰ ਜਾਂ sort ਕਾਲਮ ਨੂੰ ਜੋੜੋ।
ਉੱਚ-ਅਸਰ ਵਾਲੇ, ਨਿਰਾਲੇ ਇੰਡੈਕਸ ਅਕਸਰ ਇਸ ਤਰ੍ਹਾਂ ਦੇਖਾਈ ਦੇਂਦੇ ਹਨ:
(tenant_id, created_at) ਰੀਸੈਂਟ ਆਈਟਮ ਲਿਸਟਾਂ ਅਤੇ cursor pagination ਲਈ(tenant_id, status) status filters (Open, Paid, Failed) ਲਈ(tenant_id, user_id) “ਇਸ user ਦੇ ਮਾਲਕ” ਵਾਲੀਆਂ ਸਕਰੀਨਾਂ ਲਈ(tenant_id, updated_at) admin views ਲਈ “ਹਾਲ ਹੀ ਵਿੱਚ ਬਦਲੇ” ਪੇਜਾਂ ਲਈ(tenant_id, external_id) webhooks ਜਾਂ imports ਤੋਂ ਲੁੱਕਅਪ ਲਈOver-indexing ਉਸ ਵੇਲੇ ਹੁੰਦਾ ਹੈ ਜਦੋਂ ਤੁਸੀਂ ਹਰ ਥੋੜ੍ਹ੍ਹਾ-ਬੋਹਤ੍ਰਾ ਵੱਖਰੀ ਸਕਰੀਨ ਲਈ ਨਵਾਂ ਇੰਡੈਕਸ ਜੋੜ ਦਿੰਦੇ ਹੋ। ਕਿਸੇ ਹੋਰ ਇੰਡੈਕਸ ਬਣਾਉਣ ਤੋਂ ਪਹਿਲਾਂ ਦੇਖੋ ਕਿ ਕੀ ਮੌਜੂਦਾ composite index ਪਹਿਲੇ columns ਨੂੰ ਕਵਰ ਕਰ ਰਿਹਾ ਹੈ ਜੋ ਤੁਹਾਨੂੰ ਚਾਹੀਦੇ ਹਨ। ਉਦਾਹਰਨ ਲਈ, ਜੇ ਤੁਹਾਡੇ ਕੋਲ (tenant_id, created_at) ਹੈ, ਤਾਂ ਅਕਸਰ ਤੁਹਾਨੂੰ (tenant_id, created_at, id) ਦੀ ਲੋੜ ਨਹੀਂ ਹੁੰਦੀ ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਵਾਕਈ id ਉੱਤੇ ਫਿਲਟਰ ਨਾ ਕਰ ਰਹੇ ਹੋ।
Partial indexes ਸਾਈਜ਼ ਅਤੇ ਲਿਖਤ ਖ਼ਰਚ ਨੂੰ ਘਟਾ ਸਕਦੇ ਹਨ ਜਦੋਂ ਜ਼ਿਆਦातर ਰਿਕਾਰਡ ਲਾਗੂ ਨਹੀਂ ਹੁੰਦੇ। ਇਹ soft deletes ਅਤੇ “active only” ਡੇਟਾ ਨਾਲ ਚੰਗੇ ਕੰਮ ਕਰਦੇ ਹਨ, ਉਦਾਹਰਨ ਵਜੋਂ: ਸਿਰਫ਼ ਜਿੱਥੇ deleted_at IS NULL ਜਾਂ ਸਿਰਫ਼ ਜਿੱਥੇ status = 'active' ਨੂੰ ਇੰਡੈਕਸ ਕਰੋ।
ਹਰ ਇਕ ਵਧੀਕ ਇੰਡੈਕਸ ਲਿਖਤ ਨੂੰ ਭਾਰ ਲਾਉਂਦਾ ਹੈ। Inserts ਨੂੰ ਹਰ ਇਕ ਇੰਡੈਕਸ ਅਪਡੇਟ ਕਰਨਾ ਪੈਂਦਾ ਹੈ, ਅਤੇ updates ਇੱਕ column ਬਦਲਣ 'ਤੇ ਵੀ ਕਈ ਇੰਡੈਕਸਾਂ ਨੂੰ ਛੂਹ ਸਕਦੇ ਹਨ। ਜੇ ਤੁਹਾਡਾ ਐਪ ਬਹੁਤ ਸਾਰੇ events ingest ਕਰਦਾ ਹੈ (ਉਦਾਹਰਨ ਲਈ ਤੇਜ਼ੀ ਨਾਲ ਬਣੇ ਐਪ ਜੋ Koder.ai ਨਾਲ ਬਣ ਰਹੇ ਹੋ), ਤਾਂ ਇੰਡੈਕਸਾਂ ਨੂੰ ਉਹਨਾਂ ਕੁਝ ਕਵੈਰੀ ਆਕਾਰਾਂ 'ਤੇ ਕੇਂਦਰਤ ਰੱਖੋ ਜੋ ਹਰ ਰੋਜ਼ ਵਰਤੇ ਜਾਂਦੇ ਹਨ।
JSONB ਲਚਕੀਲੇ extra fields ਲਈ ਸੁਹਾਵਣਾ ਹੈ ਜਿਵੇਂ feature flags, user attributes ਜਾਂ per-tenant settings। ਮੁਸੀਬਤ ਇਹ ਹੈ ਕਿ ਵੱਖ-ਵੱਖ JSONB operators ਵੱਖ-ਵੱਖ ਤਰੀਕੇ ਨਾਲ ਵਰਤਿਆ ਜਾਂਦੇ ਹਨ, ਇਸ ਲਈ ਸਭ ਤੋਂ ਚੰਗਾ ਇੰਡੈਕਸ ਇਸ ਗੱਲ 'ਤੇ ਨਿਰਭਰ ਕਰਦਾ ਹੈ ਕਿ ਤੁਸੀਂ ਕਿਵੇਂ ਕਵੈਰੀ ਕਰਦੇ ਹੋ।
ਦੋ ਆਕਾਰ ਸਭ ਤੋਂ ਜ਼ਿਆਦਾ ਮਹੱਤਵ ਰੱਖਦੇ ਹਨ:
@\u003e ਨਾਲ ਹੁੰਦਾ ਹੈ।-\u003e / -\u003e\u003e ਨਾਲ ਕੀਤੀ ਜਾਂਦੀ ਹੈ (ਅਕਸਰ = ਨਾਲ ਤੁਲਨਾ ਕੀਤੀ ਜਾਂਦੀ ਹੈ)।ਜੇ ਤੁਸੀਂ ਅਕਸਰ @\u003e ਨਾਲ ਫਿਲਟਰ ਕਰਦੇ ਹੋ, ਤਾਂ JSONB ਕਾਲਮ 'ਤੇ GIN ਇੰਡੈਕਸ ਆਮ ਤੌਰ 'ਤੇ ਲਾਭਦਾਇਕ ਹੁੰਦਾ ਹੈ।
-- Query shape: containment
SELECT id
FROM accounts
WHERE tenant_id = $1
AND metadata @\u003e '{\"region\":\"eu\",\"plan\":\"pro\"}';
-- Index
CREATE INDEX accounts_metadata_gin
ON accounts
USING GIN (metadata);
ਜੇ ਤੁਹਾਡੀ JSON ਰਚਨਾ predictable ਹੈ ਅਤੇ ਤੁਸੀਂ ਮੁੱਖਤੌਰ 'ਤੇ top-level keys 'ਤੇ @\u003e ਵਰਤਦੇ ਹੋ ਤਾਂ jsonb_path_ops ਛੋਟਾ ਅਤੇ ਤੇਜ਼ ਹੋ ਸਕਦਾ ਹੈ, ਪਰ ਇਹ ਘੱਟ operator ਕਿਸਮਾਂ ਨੂੰ ਸਪੋਰਟ ਕਰਦਾ ਹੈ।
ਜੇ ਤੁਹਾਡੀ UI ਇੱਕ ਖੇਤਰ (ਜਿਵੇਂ plan) 'ਤੇ ਮੁੜ-ਮੁੜ ਫਿਲਟਰ ਕਰਦੀ ਹੈ, ਤਾਂ ਉਸ field ਨੂੰ ਬਾਹਰ ਕੱਢ ਕੇ ਉਸ 'ਤੇ ਇੰਡੈਕਸ ਬਣਾਉਣਾ ਆਮ ਤੌਰ 'ਤੇ ਇੱਕ ਵਿਆਪਕ GIN ਨਾਲੋਂ ਤੇਜ਼ ਅਤੇ ਸਸਤਾ ਹੋ ਸਕਦਾ ਹੈ।
SELECT id
FROM accounts
WHERE tenant_id = $1
AND metadata-\u003e\u003e'plan' = 'pro';
CREATE INDEX accounts_plan_expr
ON accounts ((metadata-\u003e\u003e'plan'));
ਇੱਕ ਪ੍ਰਾਇਕਟੀਕਲ ਨਿਯਮ: JSONB ਨੂੰ ਲਚਕੀਲੇ, ਘੱਟ ਵਰਤੇ ਜਾਣ ਵਾਲੇ attributes ਲਈ ਰੱਖੋ, ਪਰ ਜੋ stable, high-usage ਫੀਲਡ ਹਨ (plan, status, created_at) ਉਹਨਾਂ ਨੂੰ ਅਸਲੀ ਕਾਲਮਾਂ ਵਿੱਚ promote ਕਰੋ। ਜੇ ਤੁਸੀਂ ਕਿਰਿਆਸ਼ੀਲ ਤਰੀਕੇ ਨਾਲ ਤੇਜ਼ੀ ਨਾਲ ਆਉਣ ਵਾਲੇ ਐਪ 'ਤੇ ਕੰਮ ਕਰ ਰਹੇ ਹੋ, ਤਾਂ ਇੱਕ ਵਾਰੀ ਦੇਖੋ ਕਿ ਕਿਹੜੇ ਫਿਲਟਰ ਹਰ ਪੇਜ ਤੇ ਆ ਰਹੇ ਹਨ; ਫਿਰ ਸਕੀਮਾ ਨੂੰ ਆਸਾਨੀ ਨਾਲ ਠੀਕ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ।
ਉਦਾਹਰਨ: ਜੇ ਤੁਸੀਂ {\"tags\":[\"beta\",\"finance\"],\"region\":\"us\"} JSONB ਵਿੱਚ ਸਟੋਰ ਕਰਦੇ ਹੋ, ਤਾਂ attribute bundles (@\u003e) ਨਾਲ ਫਿਲਟਰ ਕਰਨ ਲਈ GIN ਵਰਤੋ, ਅਤੇ ਉਹ ਕੁਝ keys ਜਿਨ੍ਹਾਂ ਦਾ ਸਭ ਤੋਂ ਵਧ ਪਰੇਯੋਗ ਹੁੰਦਾ ਹੈ (plan, region) ਲਈ expression indexes ਜੋੜੋ।
ਐਰੇ ਆਸਾਨੀ ਨਾਲ ਸਟੋਰ ਕਰਨ ਅਤੇ ਪੜ੍ਹਨ ਲਈ ਅਕਰਸ਼ਕ ਹੁੰਦੇ ਹਨ। users.roles text[] ਜਾਂ projects.labels text[] ਕਾਲਮ ਚੰਗੇ ਕੰਮ ਕਰ ਸਕਦੇ ਹਨ ਜਦੋਂ ਤੁਸੀਂ ਅਕਸਰ ਇੱਕ ਹੀ ਸਵਾਲ ਪੁੱਛਦੇ ਹੋ: ਕੀ ਇਸ row ਵਿੱਚ ਕੋਈ value ਹੈ? ਇਹ ਓਹੀ ਹਾਲਤ ਹੈ ਜਿੱਥੇ GIN ਫਾਇਦਾ ਦਿੰਦਾ ਹੈ।
GIN ਐਰੇ membership queries ਲਈ ਜਾਵੇ-ਪ੍ਰਮੁੱਖ ਚੋਣ ਹੈ। ਇਹ ਐਰੇ ਨੂੰ ਇਨਡਿਵਿਜੂਅਲ ਆਈਟਮਾਂ ਵਿੱਚ ਤੋੜ ਕੇ rows ਦਾ ਤੇਜ਼ lookup ਬਣਾਉਂਦਾ ਹੈ ਜੋ ਉਹਨਾਂ ਆਈਟਮਾਂ ਨੂੰ ਰੱਖਦੇ ਹਨ।
ਐਰੇ ਕਵੈਰੀ ਆਕਾਰ ਜੋ ਆਮ ਤੌਰ 'ਤੇ ਲਾਭਦਾਇਕ ਹਨ:
@\u003e (array contains)\u0026\u0026 (array shares any items)= ANY(...), ਪਰ @\u003e ਅਕਸਰ ਜ਼ਿਆਦਾ predictable ਹੈਉਪਯੋਗੀ ਉਦਾਹਰਨ—users ਨੂੰ role 'admin' ਨਾਲ ਫਿਲਟਰ کرنا:
-- Find users who have the \"admin\" role
SELECT id, email
FROM users
WHERE roles @\u003e ARRAY['admin'];
CREATE INDEX users_roles_gin ON users USING GIN (roles);
ਅਤੇ projects ਨੂੰ label ਸੈਟ ਨਾਲ ਫਿਲਟਰ ਕਰਨਾ (ਦੋਹਾਂ labels ਹੋਣੀ ਚਾਹੀਦੀ ਹੈ):
SELECT id, name
FROM projects
WHERE labels @\u003e ARRAY['billing', 'urgent'];
CREATE INDEX projects_labels_gin ON projects USING GIN (labels);
ਲੋਕਾਂ ਨੂੰ ਹੈਰਾਨੀ ਹੁੰਦੀ ਹੈ ਕਿ ਕੁਝ ਪੈਟਰਨ ਇੰਡੈਕਸ ਦੀ ਉਮੀਦ ਅਨੁਸਾਰ ਕੰਮ ਨਹੀਂ ਕਰਦੇ। ਜੇ ਤੁਸੀਂ ਐਰੇ ਨੂੰ string array_to_string(labels, ',') ਵਿਚ ਬਦਲ ਕੇ LIKE ਚਲਾਉਂਦੇ ਹੋ, ਤਾਂ GIN ਇੰਡੈਕਸ ਮਦਦ ਨਹੀਂ ਕਰੇਗਾ। ਅਤੇ ਜੇ ਤੁਹਾਨੂੰ “starts with” ਜਾਂ fuzzy matches ਚਾਹੀਦੇ ਹਨ ਤਾਂ ਤੁਸੀਂ text search ਦਾਇਰੇ ਵਿੱਚ ਆ ਜਾਉਗੇ, ਨਾ ਕਿ array membership ਵਿੱਚ।
ਐਰੇ ਮੁਸ਼ਕਲ ਬਣ ਸਕਦੇ ਹਨ ਜਦੋਂ ਉਹ ਇਕ mini-database ਬਣ ਜਾਂਦੇ ਹਨ: ਤੇਜ਼ ਅਪਡੇਟ, ਹਰ item ਲਈ metadata (ਕਿਸ ਨੇ label ਜੋੜਿਆ, ਕਦੋਂ, ਕਿਉਂ), ਜਾਂ label-ਅਧਾਰਿਤ analytics ਦੀ ਲੋੜ। ਇਸ ਵੇਲੇ, project_labels(project_id, label) ਵਰਗਾ join table ਆਮ ਤੌਰ 'ਤੇ validate, query ਅਤੇ evolve ਕਰਨ ਲਈ ਆਸਾਨ ਹੁੰਦਾ ਹੈ।
Search boxes ਲਈ ਦੋ ਆਮ ਪੈਟਰਨ ਆਉਂਦੇ ਹਨ: ਫੁੱਲ-ਟੈਕਸਟ search (ਕਿਸੇ ਵਿਸ਼ੇ 'ਤੇ ਰਿਕਾਰਡ ਲੱਭੋ) ਅਤੇ fuzzy matching (ਟਾਈਪੋ, ਅਧੂਰੀਆਂ ਨਾਵਾਂ, ਅਤੇ ILIKE ਪੈਟਰਨਸ ਨੂੰ ਨੂੰਹ-ਸਹੀ ਕਰਨਾ)। ਸਹੀ ਇੰਡੈਕਸ "instant" ਅਤੇ "timeouts at 10k users" ਵਿਚ ਫਰਕ ਪੈਦਾ ਕਰ ਸਕਦਾ ਹੈ।
ਜਦੋਂ ਯੂਜ਼ਰ ਅਸਲ ਸ਼ਬਦ ਟਾਈਪ ਕਰਦੇ ਹਨ ਅਤੇ ਤੁਸੀਂ relevance ਅਨੁਸਾਰ ਨਤੀਜੇ ਚਾਹੁੰਦੇ ਹੋ, ਤਾਂ ਫੁੱਲ-ਟੈਕਸਟ search ਵਰਤੋ—ਦਰਅਸਲ tickets ਨੂੰ subject ਅਤੇ description ਦੁਆਰਾ search ਕਰਨ ਲਈ। ਆਮ ਤਰੀਕਾ ਇਹ ਹੈ ਕਿ tsvector (ਅਕਸਰ generated column) ਨੂੰ ਸਟੋਰ ਕਰੋ ਅਤੇ ਇਸਨੂੰ GIN ਨਾਲ ਇੰਡੈਕਸ ਕਰੋ। ਤੁਸੀਂ @@ ਅਤੇ tsquery ਨਾਲ search ਕਰੋਂਗੇ।
-- Tickets: full-text search on subject + body
ALTER TABLE tickets
ADD COLUMN search_vec tsvector
GENERATED ALWAYS AS (
to_tsvector('simple', coalesce(subject,'') || ' ' || coalesce(body,''))
) STORED;
CREATE INDEX tickets_search_vec_gin
ON tickets USING GIN (search_vec);
-- Query
SELECT id, subject
FROM tickets
WHERE search_vec @@ plainto_tsquery('simple', 'invoice failed');
-- Customers: fuzzy name search using trigrams
CREATE INDEX customers_name_trgm
ON customers USING GIN (name gin_trgm_ops);
SELECT id, name
FROM customers
WHERE name ILIKE '%jon smth%';
ਕਿੰਨਾ vector ਵਿੱਚ ਰੱਖਣਾ ਹੈ: ਸਿਰਫ ਉਹੀ fields ਜੋ ਤੁਸੀਂ ਵਾਕਈ search ਕਰਦੇ ਹੋ। ਜੇ ਤੁਸੀਂ ਹਰ ਚੀਜ਼ (notes, internal logs) include ਕਰ ਲਓ ਤਾਂ ਇੰਡੈਕਸ ਸਾਈਜ਼ ਅਤੇ ਲਿਖਣ ਦੀ ਲਾਗਤ ਵੱਧ ਜਾਵੇਗੀ।
ਜਦੋਂ ਯੂਜ਼ਰ ਨਾਮ, email ਜਾਂ ਛੋਟੇ ਵਾਕਾਂਸ਼ ਲਿਖਦੇ ਹਨ ਅਤੇ ਤੁਸੀਂ partial matches ਜਾਂ typo-tolerance ਚਾਹੁੰਦੇ ਹੋ, ਤਾਂ trigram similarity ਵਰਤੋ। Trigrams ILIKE '%term%' ਅਤੇ similarity operators ਵਿੱਚ ਮਦਦ ਕਰਦੇ ਹਨ। GIN ਆਮ ਤੌਰ 'ਤੇ “ਕੀ ਇਹ match ਕਰਦਾ ਹੈ?” ਲੁੱਕਅਪ ਲਈ ਤੇਜ਼ ਹੁੰਦਾ ਹੈ; GiST ਉਹਨਾਂ ਕੇਸਾਂ ਵਿੱਚ ਚੰਗਾ ਹੋ ਸਕਦਾ ਹੈ ਜਦੋਂ ਤੁਸੀਂ similarity ਅਨੁਸਾਰ ordering ਵੀ ਚਾਹੁੰਦੇ ਹੋ।
ਨਿਯਮਾਂ ਦੇ ਟੁਕੜੇ:
tsvector ਵਰਤੋ।ਧਿਆਨ ਵਿੱਚ ਰੱਖਣ ਵਾਲੀਆਂ ਗੱਲਾਂ:
ILIKE '%abc') scans ਕਰਵਾਉਂਦੇ ਹਨ।ਜੇ ਤੁਸੀਂ search ਸਕਰੀਨਾਂ ਨੂੰ ਜਲਦੀ ship ਕਰ ਰਹੇ ਹੋ, ਤਾਂ ਇੰਡੈਕਸ ਨੂੰ feature ਦਾ ਹਿੱਸਾ ਸਮਝੋ: search UX ਅਤੇ ਇੰਡੈਕਸ ਚੋਣ ਇਕੱਠੇ ਡਿਜ਼ਾਇਨ ਕੀਤੇ ਜਾਣੇ ਚਾਹੀਦੇ ਹਨ।
ਸ਼ੁਰੂ ਕਰੋ ਉਹੀ ਸਹੀ ਕਵੈਰੀ ਲੈ ਕੇ ਜੋ ਤੁਹਾਡਾ ਐਪ ਚਲਾਂਦਾ ਹੈ, ਅੰਦਾਜ਼ ਨਹੀਂ। ਇੱਕ “slow screen” ਆਮ ਤੌਰ 'ਤੇ ਇੱਕ ਵਿਸ਼ੇਸ਼ SQL statement ਹੁੰਦੀ ਹੈ ਜਿਸਦਾ ਇੱਕ ਨਿਰਧਾਰਤ WHERE ਅਤੇ ORDER BY ਹੁੰਦਾ ਹੈ। ਇਸਨੂੰ logs, ORM debug output ਜਾਂ ਜੋ ਵੀ query capture ਤੁਸੀਂ ਵਰਤਦੇ ਹੋ ਓਥੋਂ ਨਕਲ ਕਰੋ।
ਇੱਕ workflow ਜੋ ਅਸਲ ਐਪਸ ਵਿੱਚ ਕੰਮ ਕਰਦੀ ਹੈ:
EXPLAIN (ANALYZE, BUFFERS) ਚਲਾਓ।=, \u003e=, LIKE, @\u003e, @@), ਕੇਵਲ ਕਾਲਮ ਨਾਂ ਨਹੀਂ।EXPLAIN (ANALYZE, BUFFERS) ਚਲਾਓ।ਇੱਥੇ ਇੱਕ ਸਪੱਸ਼ਟ ਉਦਾਹਰਨ ਹੈ। Customers ਪੇਜ tenant ਅਤੇ status ਨਾਲ ਫਿਲਟਰ ਕਰਦਾ ਹੈ, newest ਨਾਲ sort ਕਰਦਾ ਹੈ ਅਤੇ paginate ਕਰਦਾ ਹੈ:
SELECT id, created_at, email
FROM customers
WHERE tenant_id = $1 AND status = $2
ORDER BY created_at DESC
LIMIT 50;
ਜੇ EXPLAIN sequential scan ਅਤੇ sort ਦਿਖਾਉਂਦਾ ਹੈ, ਤਾਂ ਇੱਕ B-tree ਇੰਡੈਕਸ ਜੋ filter ਅਤੇ sort ਨੂੰ match ਕਰਦਾ ਹੈ ਅਕਸਰ ਇਸਨੂੰ ਠੀਕ ਕਰ ਦਿੰਦਾ ਹੈ:
CREATE INDEX ON customers (tenant_id, status, created_at DESC);
ਜੇ slow ਹਿੱਸਾ JSONB filtering ਹੈ ਜਿਵੇਂ metadata @\u003e '{\"plan\":\"pro\"}', ਤਾਂ ਇਹ GIN ਨੂੰ ਦਿਖਾਉਂਦਾ ਹੈ। ਜੇ ਇਹ full-text search ਹੈ ਜਿਵੇਂ to_tsvector(...) @@ plainto_tsquery(...), ਤਾਂ ਵੀ GIN-ਅਧਾਰਤ search index ਦੀ ਲੋੜ ਹੋਵੇਗੀ। ਜੇ ਇਹ “closest match” ਜਾਂ overlap-style operator ਹੈ, ਤਾਂ GiST ਅਕਸਰ ਫਿੱਟ ਹੁੰਦਾ ਹੈ।
ਇੰਡੈਕਸ ਜੋੜਨ ਤੋਂ ਬਾਅਦ ਟਰੇਡ-ਆਫ਼ ਨੂੰ ਮਾਪੋ। ਇੰਡੈਕਸ ਦਾ size, insert ਅਤੇ update ਸਮਾਂ ਜਾਂਚੋ, ਅਤੇ ਦੇਖੋ ਕਿ ਕੀ ਇਹ top ਕੁਝ slow ਕਵੈਰੀਆਂ ਵਿੱਚ ਫਾਇਦਾ ਪਹੁੰਚਾ ਰਿਹਾ ਹੈ ਜਾਂ ਕੇਵਲ ਇੱਕ edge case ਲਈ। ਤੇਜ਼-ਬਦਲਦੇ projects (ਜਿਨ੍ਹਾਂ ਵਿੱਚ Koder.ai ਨਾਲ ਬਣ ਰਹੇ ones ਵੀ ਸ਼ਾਮਿਲ ਹਨ) ਵਿੱਚ ਇਹ re-check ਤੁਹਾਨੂੰ unused ਇੰਡੈਕਸਾਂ ਦੇ accumulation ਤੋਂ ਬਚਾਉਂਦਾ ਹੈ।
ਜ਼ਿਆਦਾਤਰ ਇੰਡੈਕਸ ਸਮੱਸਿਆਵਾਂ B-tree vs GIN vs GiST ਚੋਣ ਬਾਰੇ ਨਹੀਂ ਹਨ। ਇਹ ਇਸ ਬਾਰੇ ਹੁੰਦੀਆਂ ਹਨ ਕਿ ਤੁਸੀਂ ਇੱਕ ਇੰਡੈਕਸ ਬਣਾਉਂਦੇ ਹੋ ਜੋ ਠੀਕ ਲੱਗਦਾ ਹੈ ਪਰ ਐਪ ਜਿਸ ਤਰੀਕੇ ਨਾਲ ਕਵੈਰੀ ਕਰਦੀ ਹੈ ਉਸ ਨਾਲ ਮਿਲਦਾ ਨਹੀਂ।
ਜ਼ਿਆਦਾਤਰ ਨੁਕਸਾਨ ਪਹੁੰਚਾਉਣ ਵਾਲੀਆਂ ਗਲਤੀਆਂ:
tenant_id ਅਤੇ created_at ਨਾਲ ਸ਼ੁਰੂ ਹੁੰਦੀ ਹੈ, ਪਰ ਇੰਡੈਕਸ created_at ਨਾਲ ਸ਼ੁਰੂ ਹੁੰਦਾ ਹੈ, ਤਾਂ planner ਇਸਨੂੰ ਛੱਡ ਸਕਦਾ ਹੈ।status, is_active ਜਾਂ boolean ਤੇ ਇਕਲਾ ਇੰਡੈਕਸ ਅਕਸਰ ਘੱਟ ਮਦਦ ਕਰਦਾ ਹੈ ਕਿਉਂਕਿ ਇਹ ਬਹੁਤ ਸਾਰੀਆਂ rows ਨੂੰ ਮੇਚ ਕਰਦਾ ਹੈ। ਇਸਨੂੰ ਇੱਕ selective column (ਜਿਵੇਂ tenant_id ਜਾਂ created_at) ਨਾਲ ਜੋੜੋ ਜਾਂ ਇਸਨੂੰ ਛੱਡ ਦਿਓ।ANALYZE ਹਾਲ ਹੀ ਵਿੱਚ ਨਹੀਂ ਚੱਲਿਆ, ਤਾਂ planner ਗਲਤ plans ਚੁਣ ਸਕਦਾ ਹੈ ਭਾਵੇਂ ਸਹੀ ਇੰਡੈਕਸ ਮੌਜੂਦ ਹੋਵੇ।ਇੱਕ ਸਪੱਸ਼ਟ ਉਦਾਹਰਨ: ਤੁਹਾਡੀ Invoices ਸਕਰੀਨ tenant_id ਅਤੇ status ਨਾਲ filter ਕਰਦੀ ਹੈ, ਫਿਰ created_at DESC ਨਾਲ sort ਕਰਦੀ ਹੈ। صرف status 'ਤੇ ਇਕ ਇੰਡੈਕਸ ਘੱਟ ਮਦਦ ਕਰੇਗਾ। ਇੱਕ ਬਿਹਤਰ ਫਿੱਟ composite index ਹੈ ਜੋ tenant_id, ਫਿਰ status, ਫਿਰ created_at (filter ਪਹਿਲਾਂ, sort ਆਖਰ) ਰੱਖੇ। ਇਹ ਇੱਕ ਬਦਲਾਅ ਅਕਸਰ ਤਿੰਨ ਅਲੱਗ ਇੰਡੈਕਸਾਂ ਪਾਉਣ ਨਾਲੋਂ ਵਧੀਆ ਨਤੀਜਾ ਦਿੰਦਾ ਹੈ।
ਹਰ ਇੰਡੈਕਸ ਨੂੰ ਇੱਕ ਲਾਗਤ ਵਾਂਗ ਮੰਨੋ। ਇਹ ਆਪਣੀ ਜਗ੍ਹਾ ਵਿੱਚ ਅਸਲ ਕਵੈਰੀਆਂ ਲਈ ਮਹਨਤ ਕਮਾਉਣੀ ਚਾਹੀਦੀ ਹੈ, ਸਿਧਾ ਸਿਧਾ ਕਾਨੂੰਨੀ ਸਿਧਾਂਤ ਨਹੀਂ।
ਇੰਡੈਕਸ ਬਦਲਾਅ ਆਸਾਨੀ ਨਾਲ ship ਕੀਤੇ ਜਾ ਸਕਦੇ ਹਨ ਅਤੇ ਜੇ ਉਹ ਲਿਖਣ ਦੀ ਲਾਗਤ ਵਧਾਉਂਦੇ ਹਨ ਜਾਂ ਇੱਕ ਸਖ਼ਤ ਟੇਬਲ ਨੂੰ ਲੌਕ ਕਰਦੇ ਹਨ ਤਾਂ undo ਕਰਨਾ ਬੇਹੱਦ ਝੰਜਟਲਾ ਹੋ ਸਕਦਾ ਹੈ। merge ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਇਸਨੂੰ ਇਕ ਛੋਟੀ ਰੀਲੀਜ਼ ਵਾਂਗ ਸੋਚੋ।
ਸ਼ੁਰੂ ਕਰੋ ਇਸਨੂੰ ਦਰਸਾਉਂਦੇ ਹੋਏ ਕਿ ਤੁਸੀਂ ਕੀ optimize ਕਰ ਰਹੇ ਹੋ। ਆਪਣੀਆਂ logs ਜਾਂ monitoring ਤੋਂ ਦੋ ਛੋਟੀ ਰੈਂਕਿੰਗ ਨਿਕਾਲੋ: ਸਭ ਤੋਂ ਵੱਧ ਚਲਦੀਆਂ queries, ਅਤੇ ਸਭ ਤੋਂ ਵੱਧ latency ਵਾਲੀਆਂ queries। ਹਰ ਇਕ ਲਈ ਸਹੀ ਆਕਾਰ ਲਿਖੋ: filter columns, sort order, joins, ਅਤੇ operators (equals, range, IN, ILIKE, JSONB operators, array contains)। ਇਹ ਅਨੁਮਾਨ ਲਾਉਣ ਤੋਂ ਬਚਾਉਂਦਾ ਹੈ ਅਤੇ ਤੁਹਾਨੂੰ ਸਹੀ ਇੰਡੈਕਸ ਟਾਈਪ ਚੁਣਨ ਵਿੱਚ ਮਦਦ ਕਰਦਾ ਹੈ।
Pre-ship ਚੇਕਲਿਸਟ:
ਇੰਡੈਕਸ ਜੁੜਨ ਤੋਂ ਬਾਅਦ verify ਕਰੋ ਕਿ ਇਹ ਹਕੀਕਤ ਵਿੱਚ ਮਦਦ ਕਰ ਰਿਹਾ ਹੈ। ਉਹੀ EXPLAIN (ANALYZE, BUFFERS) ਕਵੈਰੀ ਚਲਾਓ ਅਤੇ before vs after ਦੀ ਤੁਲਨਾ ਕਰੋ। ਫਿਰ production ਵਿਹਾਰ ਨੂੰ ਇੱਕ ਦਿਨ ਲਈ ਨਿਗਰਾਨੀ ਕਰੋ:
ਜੇ ਤੁਸੀਂ Koder.ai ਨਾਲ ਬਣਾ ਰਹੇ ਹੋ, ਤਾਂ generated SQL ਨੂੰ ਇੱਕ-ਦੋ slow screens ਲਈ change ਦੇ ਕੋਲ ਰੱਖਣਾ ਵਧੀਆ ਹੈ, ਤਾਂ ਜੋ ਇੰਡੈਕਸ ਉਹੀ ਕਵੈਰੀ ਨੂੰ match ਕਰੇ ਜੋ ਐਪ ਅਸਲ ਵਿੱਚ ਚਲਾਂਦਾ ਹੈ।
ਇੱਕ ਆਮ admin ਸਕਰੀਨ ਸੋਚੋ: Users list ਜਿਸ ਵਿੱਚ tenant scoping, ਕੁਝ ਫਿਲਟਰ, last active ਨਾਲ sort ਅਤੇ ਇੱਕ search box ਹੋਵੇ। ਇੱਥੇ ਇੰਡੈਕਸ ਥਿਊਰੀ ਤੋਂ practical ਤੌਰ 'ਤੇ ਫਰਕ ਲਿਆਉਂਦੇ ਹਨ।
ਤਿੰਨ ਆਮ ਕਵੈਰੀ ਆਕਾਰ ਜੋ ਤੁਸੀਂ ਅਕਸਰ ਵੇਖੋਗے:
-- 1) List page with tenant + status filter + sort
SELECT id, email, last_active_at
FROM users
WHERE tenant_id = $1 AND status = $2
ORDER BY last_active_at DESC
LIMIT 50;
-- 2) Search box (full-text)
SELECT id, email
FROM users
WHERE tenant_id = $1
AND to_tsvector('simple', coalesce(name,'') || ' ' || coalesce(email,'')) @@ plainto_tsquery($2)
ORDER BY last_active_at DESC
LIMIT 50;
-- 3) Filter on JSON metadata (plan, flags)
SELECT id
FROM users
WHERE tenant_id = $1
AND metadata @\u003e '{\"plan\":\"pro\"}'::jsonb;
ਇਸ ਸਕਰੀਨ ਲਈ ਇੱਕ ਛੋਟਾ ਪਰ ਇਰਾਦੇ ਵਾਲਾ ਇੰਡੈਕਸ ਸੈੱਟ:
(tenant_id, status, last_active_at DESC)。tsvector column ਨਾਲ GIN ਇੰਡੈਕਸ।@\u003e ਬਹੁਤ ਵਰਤਦੇ ਹੋ ਤਾਂ GIN (metadata), ਜਾਂ ਜੇ ਮੁੱਖਤੌਰ 'ਤੇ ਇੱਕ key ਤੇ ਫਿਲਟਰ ਹੈ ਤਾਂ expression B-tree ((metadata-\u003e\u003e'plan'))।ਮਿਕਸਡ ਲੋੜਾਂ ਆਮ ਹਨ। ਜੇ ਇਕ ਪੇਜ filters + search + JSON ਕਰਦਾ ਹੈ, ਤਾਂ ਸਭ ਕੁਝ ਇਕ mega ਇੰਡੈਕਸ ਵਿੱਚ ਦਬਾਉਣ ਤੋਂ ਬਚੋ। B-tree ਨੂੰ sorting/pagination ਲਈ ਰੱਖੋ, ਫਿਰ ਇੱਕ specialized index (ਅਕਸਰ GIN) expensive ਹਿੱਸੇ ਲਈ ਸ਼ਾਮਿਲ ਕਰੋ।
ਅਗਲੇ ਕਦਮ: ਇੱਕ slow screen ਚੁਣੋ, ਉਸਦੇ ਸਿਖਰ 2-3 query ਆਕਾਰ ਲਿਖੋ, ਅਤੇ ਹਰ ਇੱਕ ਇੰਡੈਕਸ ਨੂੰ ਉਦਿਆਂ ਤੋਂ ਦੇਖੋ (filter, sort, search, JSON)। ਜੇ ਇੱਕ ਇੰਡੈਕਸ ਦਾ ਸਪੱਸ਼ਟ ਮੇਲ ਕਿਸੇ ਅਸਲ ਕਵੈਰੀ ਨਾਲ ਨਹੀਂ ਹੈ, ਤਾਂ ਉਸਨੂੰ ਯੋਜਨਾ ਤੋਂ ਕੱਢ ਦਿਓ। ਜੇ ਤੁਸੀਂ ਤੇਜ਼ੀ ਨਾਲ Koder.ai 'ਤੇ iteration ਕਰ ਰਹੇ ਹੋ, ਤਾਂ ਨਿਯਮਤ ਸਮੀਖਿਆ ਨਾਲ index sprawl ਤੋਂ ਬਚ ਸਕਦੇ ਹੋ ਜਦੋਂ ਤੱਕ ਤੁਹਾਡਾ schema ਬਦਲਦਾ ਰਹਿੰਦਾ ਹੈ।
ਇੱਕ ਇੰਡੈਕਸ PostgreSQL ਨੂੰ ਬਹੁਤ ਸਾਰਾ ਟੇਬਲ ਪੜ੍ਹਨ ਤੋਂ ਬਚਾਉਂਦਾ ਹੈ ਅਤੇ ਮਿਲਦੇ ਹੋਏ ਰਿਕਾਰਡ ਸਿੱਧੇ ਲੱਭਣ ਵਿੱਚ ਮਦਦ ਕਰਦਾ ਹੈ। ਸਧਾਰਣ SaaS ਸਕਰੀਨਾਂ—ਜਿਵੇਂ ਲਿਸਟ, ਡੈਸ਼ਬੋਰਡ ਅਤੇ ਖੋਜ—ਲਈ ਸਹੀ ਇੰਡੈਕਸ ਤੋਂ ਸਕੈਨ ਤੇ ਵੱਡਾ ਫਾਇਦਾ ਮਿਲਦਾ ਹੈ ਅਤੇ ਪਰਫਾਰਮੈਂਸ ਵੱਧਦੀ ਹੈ।
ਅਕਸਰ ਸ਼ੁਰੂ B-tree ਨਾਲ ਕਰੋ: ਇਹ = ਫਿਲਟਰ, ਰੇਂਜ, ਜੁਆਇਨ ਅਤੇ ORDER BY ਲਈ ਵਧੀਆ ਹੈ। ਜੇ ਤੁਸੀਂ ਮੁੱਖਤੌਰ 'ਤੇ containment (JSONB, arrays) ਜਾਂ text search ਕਰ ਰਹੇ ਹੋ ਤਾਂ GIN ਸੋਚੋ; GiST ਉਹਨਾਂ ਕੇਸਾਂ ਲਈ ਹੈ ਜਿੱਥੇ overlap ਜਾਂ “ਨਜ਼ਦੀਕ/ਦूरी” ਸਵਾਲ ਹਨ।
ਜਿਨ੍ਹਾਂ ਕਾਲਮਾਂ 'ਤੇ ਤੁਸੀਂ = ਨਾਲ ਫਿਲਟਰ ਕਰਦੇ ਹੋ ਉਹ ਪਹਿਲਾਂ ਰੱਖੋ, ਫਿਰ ਜਿਸ ਕਾਲਮ ਨਾਲ ਤੁਸੀਂ ORDER BY ਕਰਦੇ ਹੋ ਉਹ ਆਖਿਰ ਨੂੰ ਰੱਖੋ। ਇਸ ਸਿਰਤੋਂ ਪਲੈਨਰ ਇੰਡੈਕਸ ਨੂੰ ਅਚੀ ਤਰ੍ਹਾਂ ਵਰਤ ਸਕਦਾ ਹੈ।
ਜੇ ਹਰ ਕਵੈਰੀ tenant_id ਨਾਲ ਸਕੋਪ ਹੁੰਦੀ ਹੈ ਤਾਂ tenant_id ਨੂੰ ਇੰਡੈਕਸ ਦਾ ਪਹਿਲਾ ਕਾਲਮ ਰੱਖੋ। ਇਸ ਨਾਲ ਇੰਡੈਕਸ ਦੇ ਅੰਦਰ ਹਰ ਟੈਨੈਂਟ ਦੇ ਰਿਕਾਰਡ ਇਕੱਠੇ ਰਹਿੰਦੇ ਹਨ ਅਤੇ ਰੀਡ ਕੰਮ ਤੇਜ਼ ਹੋ ਜਾਂਦਾ ਹੈ।
INCLUDE ਤੁਹਾਨੂੰ ਹੋਰ ਕਾਲਮ ਇੰਡੈਕਸ ਵਿਚ ਸ਼ਾਮਿਲ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ ਤਾਂ ਜੋ index-only reads ਸੰਭਵ ਹੋ ਸਕਣ ਬਿਨਾਂ ਇੰਡੈਕਸ ਕੀ ਨੂੰ ਵੱਡਾ ਕੀਤੇ। ਇਹ ਉਨ੍ਹਾਂ ਲਿਸਟ ਪੇਜਾਂ ਲਈ χρήσιμο ਹੈ ਜਿੱਥੇ ਤੁਸੀਂ ਫਿਲਟਰੇਸ਼ਨ ਅਤੇ sort ਲਈ ਕੁਝ ਕਾਲਮ ਵਰਤਦੇ ਹੋ ਪਰ ਦਿਖਾਉਣ ਲਈ ਹੋਰ fields ਵੀ ਲੋੜੀਂਦੇ ਹਨ।
ਜਦੋਂ ਤੁਸੀਂ ਸਿਰਫ਼ ਕੁਝ ਸਬਸੈਟ ਰਿਕਾਰਡਾਂ ਨੂੰ ਇੰਡੈਕਸ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ—ਉਦਾਹਰਣ ਵਜੋਂ deleted_at IS NULL ਜਾਂ status = 'active'—ਤਾਂ partial index ਛੋਟਾ ਅਤੇ ਸਸਤਾ ਰਹਿੰਦਾ ਹੈ ਅਤੇ ਲਿਖਣ ਦਾ ਖਰਚ ਘਟਾਉਂਦਾ ਹੈ।
ਜੇ ਤੁਸੀਂ ਜ਼ਿਆਦਾ ਤਰ containment ਵਰਤਦੇ ਹੋ (ਉਦਾਹਰਣ: metadata @\u003e '{"plan":"pro"}') ਤਾਂ JSONB 'ਤੇ GIN ਛੇਤੀ ਨਤੀਜੇ ਦੇ ਸਕਦਾ ਹੈ। ਪਰ ਜੇ ਤੁਸੀਂ ਇੱਕ ਜਾਂ ਦੋ ਖਾਸ JSON keys ਤੇ ਮੁੱਖਤੌਰ 'ਤੇ ਫਿਲਟਰ ਕਰਦੇ ਹੋ ਤਾਂ expression B-tree ((metadata-\u003e\u003e'plan')) ਆਮ ਤੌਰ 'ਤੇ ਛੋਟਾ ਅਤੇ ਤੇਜ਼ ਹੁੰਦਾ ਹੈ।
ਜਦੋਂ ਤੁਹਾਡਾ ਮੁੱਖ ਸਵਾਲ “ਕੀ ਇਹ ਐਰੇ X ਰੱਖਦਾ ਹੈ?” ਹੋਵੇ (operators ਜਿਵੇਂ @\u003e ਜਾਂ \u0026\u0026), GIN ਚੰਗਾ ਖੇਡਦਾ ਹੈ। ਪਰ ਜੇ ਹਰ ਆਈਟਮ ਲਈ ਮੈਟਾਡੇਟਾ, ਅਕਸਰ ਅਪਡੇਟ ਜਾਂ label-ਸਤਰਾਂ ਦੀਆਂ analytics ਲੋੜੀਂਦੀਆਂ ਹਨ, ਤਾਂ ਇੱਕ ਨਰਮਲਾਇਜ਼ ਕੀਤੀ join-table ਆਮ ਤੌਰ ਤੇ ਬਿਹਤਰ ਹੁੰਦੀ ਹੈ।
ਫੁੱਲ-ਟੈਕਸਟ ਲਈ tsvector (ਅਕਸਰ generated column) ਰੱਖੋ ਅਤੇ GIN ਨਾਲ ਇੰਡੈਕਸ ਕਰੋ, ਫਿਰ @@ ਨਾਲ ਖੋਜ ਕਰੋ। fuzzy matching ਲਈ trigrams (ਆਮ ਤੌਰ ਤੇ GIN) ਵਰਤੋ, ਖਾਸ ਕਰਕੇ ILIKE '%term%' ਜਾਂ ਟਾਈਪੋ-ਟੋਲਰੈਂਸ ਲਈ।
ਉਸੇ ਸਹੀ SQL ਨਾਲ ਸ਼ੁਰੂ ਕਰੋ ਜੋ ਐਪ ਚਲਾਂਦਾ ਹੈ। EXPLAIN (ANALYZE, BUFFERS) ਚਲਾਇਓ, ਦੇਖੋ ਕਿਹੜੇ operators ਸਭ ਤੋਂ ਜ਼ਿਆਦਾ ਭਾਰ ਲੈ ਰਹੇ ਹਨ, ਫਿਰ ਉਸੇ operators ਅਤੇ sort-order ਨੂੰ ਮੁਤਾਬਕ ਸਭ ਤੋਂ ਨਿਰਾਲਾ ਇੰਡੈਕਸ ਬਣਾਓ। ਫਿਰ ਮੁੜ EXPLAIN ਚਲਾਉ ਕੇ ਫਾਇਦਾ ਪੁਸ਼ਟ ਕਰੋ।