Этот единственный запрос SQL может помочь вам найти индексы B-Tree, которые могут стать индексами Hash, экономя ваши деньги на счетах за хранение!
Недавно мы сравнили индексы Hash и B-Tree и усвоили урок по улучшению бенчмаркинга. В этой статье я хочу помочь вам найти подходящих кандидатов для хэш-индексов в ваших проектах.
Я поделился своей историей о том, почему эта возможность может привести к продвижению по службе или повышению зарплаты, так что попробуйте.
Без дальнейших церемоний, давайте посмотрим на запрос. Я оставил несколько комментариев, чтобы лучше описать, что он делает в частности.
DO $$ DECLARE count bigint; unique_count bigint; row record; BEGIN CREATE TEMPORARY TABLE hash_index_candidates ( table_name text, index_name text, -- b-tree index to migrate column_name text, unique_ratio decimal, btree_index_size text, potential_hash_index_size text, -- assumption based value count bigint ); -- stores temporary results for the current session FOR row IN ( SELECT t.table_name AS table_name, t.index_name AS index_name, t.column_name AS column_name, pg_size_pretty(t.size) AS index_size FROM ( SELECT tab.relname AS table_name, cls.relname AS index_name, pa.attname AS column_name, pg_relation_size(cls.relname :: text) AS size FROM pg_stat_user_indexes idx JOIN pg_index pi ON pi.indexrelid = idx.indexrelid JOIN pg_class cls ON cls.oid = idx.indexrelid JOIN pg_class tab ON tab.oid = idx.relid JOIN pg_am am ON am.oid = cls.relam JOIN pg_attribute pa ON cls.oid = pa.attrelid JOIN pg_type pt ON pa.atttypid = pt.oid WHERE amname = 'btree' -- b-tree type AND indnatts = 1 -- index covers single column AND indislive = true -- check index "health" AND indisvalid = true -- check index "health" AND indpred IS NULL -- index has no conditions AND typname IN ('varchar', 'text', 'char') AND indisprimary = false -- index is not primary AND typlen = -1 -- type has unlimited bytes length AND indisunique = false -- index is not unique ) t WHERE t.size > 104857600 -- index size is more than 100MB ) LOOP EXECUTE format('SELECT COUNT(%I) FROM %I', row.column_name, row.table_name ) INTO count; EXECUTE format('SELECT COUNT(DISTINCT %I) FROM %I', row.column_name, row.table_name) INTO unique_count; INSERT INTO hash_index_candidates VALUES ( row.table_name, row.index_name, row.column_name, round(unique_count :: decimal / (count :: decimal + 1) * 100, 2), row.index_size, pg_size_pretty(count / 300 * 8192), -- assumes 300 tuples per hash bucket count ); END LOOP; END $$ LANGUAGE plpgsql; SELECT * FROM hash_index_candidates WHERE unique_ratio > 95; -- fetch results
В одном из моих проектов я нашел несколько индексов, которые освободили бы около 1,2 ГБ, если бы я перенес их на хэш-индексы.
Но подождите секунду, прежде чем сделать это. Во-первых, мы должны проверить, удовлетворяет ли кандидат эмпирическому правилу, которое мы здесь придумали:
- Значения имеют высокую кардинальность. В идеале их хэш-коды также должны иметь высокую кардинальность;
- Значения запрашиваются только с помощью равенства операторов;
- Длина значений должна быть не менее 25 символов.
Наиболее важной частью является использование индекса. Если вы уверены, что ваше поле в индексе запрашивается только операторами равенства, переходите к миграции.
У нас может быть лучшее представление о сохраненной статистике из PostgreSQL для ее автоматизации, но для этого может потребоваться определенная конфигурация вашей базы данных, которой у вас еще может не быть. Поэтому я предоставил кое-что, что может помочь большинству из нас.
В качестве примечания: мы можем пропустить правило 3, если результирующий размер индекса меньше, чем при использовании B-Tree. Запрос SQL охватывает правило 1.
Вот и все!
Спасибо за потраченное время. Пожалуйста, дайте мне знать, помогло ли вам это.