Files
pgsql-jellyfin/sql/performance_indexes.sql
T
wjones 77e30685bb Complete multi-instance support: Phases 3–6 & deployment
- Implements Phases 3–6: session isolation, cache coordination, primary election, and file system monitor coordination for Jellyfin with PostgreSQL.
- Adds new database entities (Instance, DistributedLock, FileSystemChange) and EF model configurations.
- Includes SQL migration scripts and EF migration for all required tables, columns, and helper functions.
- Updates Device entity and JellyfinDbContext for multi-instance tracking.
- Integrates new DI services for instance registry, distributed locks, cache coordinator, and primary election.
- Adds publishing profiles (Win/Linux/FrameworkDependent) and automation script for deployment.
- Extensive documentation for architecture, setup, and publishing.
- All changes are backward compatible and build successfully.
2026-03-05 16:10:26 -05:00

226 lines
8.6 KiB
SQL

-- PostgreSQL Performance Indexes for Jellyfin
-- Run this script to improve query performance
-- Safe to run multiple times (uses IF NOT EXISTS)
\pset pager off
\timing on
\echo 'Creating performance indexes for Jellyfin PostgreSQL database...'
-- ============================================================================
-- BaseItems Table Indexes
-- ============================================================================
\echo 'Creating indexes on BaseItems table...'
-- Index for filtering by type and virtual items (improves library filtering)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitems_type_isvirtual
ON library."BaseItems" (Type, BaseItems.IsVirtualItem)
WHERE IsVirtualItem = false;
-- Index for parent-child relationships (improves hierarchy queries)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitems_parent_type
ON library."BaseItems" (BaseItems.ParentId, Type)
WHERE ParentId IS NOT NULL;
-- Index for date-based queries (recently added items, sorting)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitems_datecreated
ON library."BaseItems" (BaseItems.DateCreated DESC)
WHERE DateCreated IS NOT NULL;
-- Index for date modified (sync operations)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitems_datemodified
ON library."BaseItems" (BaseItems.DateModified DESC)
WHERE DateModified IS NOT NULL;
-- Index for sorting by name (alphabetical browsing)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitems_sortname
ON library."BaseItems" (BaseItems.SortName)
WHERE SortName IS NOT NULL;
-- Index for TopParent lookups (library organization)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitems_topparent_type
ON library."BaseItems" (BaseItems.TopParentId, Type)
WHERE TopParentId IS NOT NULL;
-- Index for premiere date (upcoming/recent content)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitems_premieredate
ON library."BaseItems" (BaseItems.PremiereDate DESC)
WHERE PremiereDate IS NOT NULL;
-- Index for production year (decade/year filtering)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitems_productionyear
ON library."BaseItems" (BaseItems.ProductionYear DESC)
WHERE ProductionYear IS NOT NULL;
-- Index for community rating (sorting by rating)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitems_rating
ON library."BaseItems" (BaseItems.CommunityRating DESC NULLS LAST);
-- Index for series episodes
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitems_series_episode
ON library."BaseItems" (BaseItems.SeriesPresentationUniqueKey, BaseItems.IndexNumber, BaseItems.ParentIndexNumber)
WHERE Type = 'MediaBrowser.Controller.Entities.TV.Episode';
-- ============================================================================
-- BaseItemProviders Table Indexes
-- ============================================================================
\echo 'Creating indexes on BaseItemProviders table...'
-- Index for provider value lookups (IMDb, TMDB, etc.)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitem_providers_value
ON library."BaseItemProviders" (ProviderValue, ProviderId);
-- Index for finding all items by provider (reverse lookup)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitem_providers_id
ON library."BaseItemProviders" (ProviderId, ItemId);
-- ============================================================================
-- UserData Table Indexes
-- ============================================================================
\echo 'Creating indexes on UserData table...'
-- Index for finding user's watched items
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_userdata_user_played
ON library."UserData" (UserId, Played);
-- Index for finding user's favorite items
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_userdata_user_favorite
ON library."UserData" (UserId, IsFavorite)
WHERE IsFavorite = true;
-- Index for last played date
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_userdata_lastplayed
ON library."UserData" (LastPlayedDate DESC)
WHERE LastPlayedDate IS NOT NULL;
-- ============================================================================
-- PeopleBaseItemMap Table Indexes
-- ============================================================================
\echo 'Creating indexes on PeopleBaseItemMap table...'
-- Index for finding all items for a person
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_people_items_person
ON library."PeopleBaseItemMap" (PeopleId, ItemId);
-- Index for finding all people for an item (already covered by FK, but explicit)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_people_items_item
ON library."PeopleBaseItemMap" (ItemId, PeopleId);
-- ============================================================================
-- ItemValuesMap Table Indexes
-- ============================================================================
\echo 'Creating indexes on ItemValuesMap table...'
-- Index for finding items by value (genres, tags, etc.)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_itemvalues_map_value
ON library."ItemValuesMap" (ItemValueId, ItemId);
-- ============================================================================
-- ItemValues Table Indexes (Critical Performance Optimization)
-- ============================================================================
\echo 'Creating indexes on ItemValues table...'
-- Index for CleanValue lookups (genre/tag searches by name)
-- Fixes: 1.3 billion row sequential scans
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_itemvalues_cleanvalue
ON library."ItemValues" ("CleanValue")
WHERE "CleanValue" IS NOT NULL;
-- Index for Value lookups (direct value searches)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_itemvalues_value
ON library."ItemValues" ("Value")
WHERE "Value" IS NOT NULL;
-- Index for ItemValueId + CleanValue (ItemValuesMap joins)
-- Improves genre/tag filtering performance
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_itemvalues_id_cleanvalue
ON library."ItemValues" ("ItemValueId", "CleanValue");
-- ============================================================================
-- ActivityLog Table Indexes
-- ============================================================================
\echo 'Creating indexes on ActivityLog table...'
-- Index for date filtering (recent activity)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_activitylog_date
ON activitylog."ActivityLog" (DateCreated DESC);
-- Index for user activity
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_activitylog_user
ON activitylog."ActivityLog" (UserId, DateCreated DESC)
WHERE UserId IS NOT NULL;
-- ============================================================================
-- MediaStreamInfos Table Indexes
-- ============================================================================
\echo 'Creating indexes on MediaStreamInfos table...'
-- Index for finding streams by item and type
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_mediastreams_item_type
ON library."MediaStreamInfos" (ItemId, Type);
-- Index for codec searches
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_mediastreams_codec
ON library."MediaStreamInfos" (Codec)
WHERE Codec IS NOT NULL;
-- ============================================================================
-- Analyze Tables
-- ============================================================================
\echo 'Analyzing tables to update statistics...'
ANALYZE library."BaseItems";
ANALYZE library."BaseItemProviders";
ANALYZE library."UserData";
ANALYZE library."PeopleBaseItemMap";
ANALYZE library."ItemValuesMap";
ANALYZE library."ItemValues";
ANALYZE library."MediaStreamInfos";
-- Analyze ActivityLog if it exists
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_schema = 'activitylog'
AND table_name = 'ActivityLogs'
) THEN
ANALYZE activitylog."ActivityLogs";
RAISE NOTICE 'ActivityLog analyzed';
END IF;
END $$;
-- ============================================================================
-- Report Index Usage
-- ============================================================================
\echo 'Current index usage statistics:'
SELECT
schemaname,
relname as tablename,
indexrelname as indexname,
pg_size_pretty(pg_relation_size(indexrelid)) as index_size,
idx_scan as times_used,
idx_tup_read as rows_read,
idx_tup_fetch as rows_fetched
FROM pg_stat_user_indexes
WHERE schemaname IN ('library', 'activitylog', 'users', 'authentication', 'displaypreferences')
ORDER BY schemaname, relname, indexrelname;
\echo ''
\echo 'Index creation complete!'
\echo ''
\echo 'Note: CONCURRENTLY builds indexes without blocking writes, but takes longer.'
\echo 'Monitor progress with: SELECT * FROM pg_stat_progress_create_index;'
\echo ''
\echo 'After indexing, restart Jellyfin to clear connection pools.'