В Google Cloud Spanner у нас возникает проблема с памятью для некоторых запросов, которые анализируют много данных
GenerateArrayEvaluator ran out of memory during buffering one value (original error message 'GenerateArrayEvaluator ran out of memory. Requested: 9 byte(s). Already reserved: 294649856 byte(s). Limit: 294649856 byte(s). Details: Cannot reserve 9 bytes to create a new segment The operation HashJoinIterator is reserving the most memory (114717769 bytes).'). Requested: 0 byte(s). Already reserved: 294649856 byte(s). Limit: 294649856 byte(s). Max Memory Operation: The operation HashJoinIterator is reserving the most memory (114717769 bytes).
Я выяснил, что по некоторым причинам запрос выполняет очень неоптимизированные операции. Мне удалось выделить виновную часть запроса. Так что это минимальный запрос для воспроизведения этой ситуации:
SELECT
COUNT(DISTINCT a) a,
COUNT(DISTINCT b) b
FROM foo
WHERE primary_split_key = "..."
Этот запрос имеет 2 предложения COUNT(DISTINCT ...)
, вот в чем проблема. Он создаст операцию map compute
, которая умножит количество возвращаемых строк на число COUNT(DISTINCT ...)
в предложении select.
Другими словами, если SELECT * FROM foo WHERE primary_split_key = "..."
возвращает 10 строк, то при вычислении карты будет создано 20 строк (10row * 2countDistinct
).
Если у нас будет 500 тыс. Строк и 3 count distinct
, то получится 1,5 млн. Строк.
См. Объяснение запроса для 443 тыс. Строк и 2 COUNT(DISTINCT ...)
: ![query explanation](https://i.stack.imgur.com/HXZvt.png)
Таким образом, можно сказать, что этот запрос действительно плохо масштабируется.
Мы работаем над его настройкой, чтобы он работал лучше. Однако мы хотели бы услышать от команды Cloud Spanner: это ожидаемое поведение гаечного ключа / счетчика, или это то, что вы хотите улучшить в ближайшем будущем?
Также всегда рады услышать альтернативы от опыт других пользователей.
РЕДАКТИРОВАТЬ: я обнаружил, что некоторые другие случаи запросов с ошибками с тем же сообщением об ошибке. Однако эти запросы явно используют функцию GENERATE_ARRAY следующим образом:
SELECT *
FROM a
JOIN b ON a.id = b.id_a
LEFT JOIN UNNEST(GENERATE_ARRAY(0, a.some_number)) record
По этой причине я подозреваю, что generate_array не оптимизирован или имеет утечку памяти.