7eb2b445cb
- Major query performance boost: replaced correlated subqueries with DistinctBy() in BaseItemRepository, added episode deduplication index, and increased default command timeout to 120s. - Fixed LibraryMonitor shutdown race: added disposal checks and exception handling in FileRefresher to prevent ObjectDisposedException. - Added PowerShell and SQL utilities for fixing Linux paths in config files and database; new scripts for WebDir and path migration. - Auto-fix for WebDir in startup.json on load; new helper scripts and docs. - Added automated weekly DB performance monitoring scripts and reporting. - Expanded documentation and README for all fixes, scripts, and migration steps. - Updated SQL scripts for index creation and diagnostics; improved output handling. - Updated db-config defaults and added new diagnostic/report files.
226 lines
8.6 KiB
SQL
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 = 'ActivityLog'
|
|
) THEN
|
|
ANALYZE activitylog."ActivityLog";
|
|
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.'
|