Docs
Поиск⌘ K
  • Главная страница
  • О The Graph
  • Поддерживаемые сети
  • Protocol Contracts
  • Субграфы
    • Субпотоки
      • Token API
        • AI Suite
          • Индексирование
            • Ресурсы
              Субграфы > Лучшие практики

              4 минуты

              Лучшие практики для субграфов №3 – Улучшение индексирования и производительности запросов с использованием неизменяемых объектов и байтов в качестве идентификаторов

              Краткое содержание

              Использование неизменяемых объектов и байтов в качестве идентификаторов в файле schema.graphql значительно улучшает скорость индексирования и производительность запросов.

              Неизменяемые объекты

              Чтобы сделать объект неизменяемым, просто добавьте (immutable: true) к объекту.

              1type Transfer @entity(immutable: true) {2  id: Bytes!3  from: Bytes!4  to: Bytes!5  value: BigInt!6}

              Сделав объект Transfer неизменяемым, graph-node сможет обрабатывать его более эффективно, что улучшит скорость индексирования и отклик на запросы.

              Структуры неизменяемых объектов не будут изменяться в будущем. Идеальным кандидатом для превращения в неизменяемый объект может быть объект, который напрямую фиксирует данные событий в блокчейне, например, событие Transfer, записываемое как объект Transfer.

              Как это устроено

              Изменяемые объекты имеют «диапазон блоков», указывающий их актуальность. Обновление таких объектов требует от graph node корректировки диапазона блоков для предыдущих версий, что увеличивает нагрузку на базу данных. Запросы также должны фильтровать данные, чтобы находить только актуальные объекты. Неизменяемые объекты работают быстрее, поскольку все они актуальны, и, так как они не изменяются, не требуется никаких проверок или обновлений при записи, а также фильтрации во время выполнения запросов.

              Когда не следует использовать неизменяемые объекты

              Если у Вас есть поле, такое как status, которое необходимо изменять с течением времени, то не следует делать объект неизменяемым. В противном случае, используйте неизменяемые объекты, когда это возможно.

              Использование Bytes в качестве идентификаторов

              Каждый объект требует уникального идентификатора. В предыдущем примере мы видим, что идентификатор уже имеет тип Bytes.

              1type Transfer @entity(immutable: true) {2  id: Bytes!3  from: Bytes!4  to: Bytes!5  value: BigInt!6}

              Хотя для идентификаторов возможны и другие типы, такие как String и Int8, рекомендуется использовать тип Bytes для всех идентификаторов. Это связано с тем, что строковые данные занимают в два раза больше места, чем строковые данные в формате Byte, а сравнение строк в кодировке UTF-8 требует учета локали, что намного более затратно по сравнению с побайтовым сравнением, используемым для строк типа Byte.

              Причины, по которым не стоит использовать Bytes как идентификаторы

              1. Если идентификаторы объектов должны быть читаемыми для человека, например, автоинкрементированные числовые идентификаторы или читаемые строки, то не следует использовать тип Bytes для идентификаторов.
              2. If integrating a Subgraph’s data with another data model that does not use Bytes as IDs, Bytes as IDs should not be used.
              3. Если улучшения производительности индексирования и запросов не являются приоритетом.

              Конкатенация (объединение) с использованием Bytes как идентификаторов

              It is a common practice in many Subgraphs to use string concatenation to combine two properties of an event into a single ID, such as using event.transaction.hash.toHex() + "-" + event.logIndex.toString(). However, as this returns a string, this significantly impedes Subgraph indexing and querying performance.

              Вместо этого следует использовать метод concatI32() для конкатенации свойств события. Эта стратегия приводит к созданию идентификатора типа Bytes, который гораздо более производителен.

              1export function handleTransfer(event: TransferEvent): void {2  let entity = new Transfer(event.transaction.hash.concatI32(event.logIndex.toI32()))3  entity.from = event.params.from4  entity.to = event.params.to5  entity.value = event.params.value67  entity.blockNumber = event.block.number8  entity.blockTimestamp = event.block.timestamp9  entity.transactionHash = event.transaction.hash1011  entity.save()12}

              Сортировка с использованием идентификаторов Bytes

              Сортировка с использованием идентификаторов Bytes не является оптимальной, как это видно из примера запроса и ответа.

              Запрос:

              1{2  transfers(first: 3, orderBy: id) {3    id4    from5    to6    value7  }8}

              Ответ на запрос:

              1{2  "data": {3    "transfers": [4      {5        "id": "0x00010000",6        "from": "0xabcd...",7        "to": "0x1234...",8        "value": "256"9      },10      {11        "id": "0x00020000",12        "from": "0xefgh...",13        "to": "0x5678...",14        "value": "512"15      },16      {17        "id": "0x01000000",18        "from": "0xijkl...",19        "to": "0x9abc...",20        "value": "1"21      }22    ]23  }24}

              Идентификаторы возвращаются в виде шестнадцатеричной строки.

              Чтобы улучшить сортировку, мы должны создать другое поле в объекте, которое будет иметь тип BigInt.

              1type Transfer @entity {2  id: Bytes!3  from: Bytes! # address4  to: Bytes! # address5  value: BigInt! # unit2566  tokenId: BigInt! # uint2567}

              Это позволит оптимизировать сортировку в последовательном порядке.

              Запрос:

              1{2  transfers(first: 3, orderBy: tokenId) {3    id4    tokenId5  }6}

              Ответ на запрос:

              1{2  "data": {3    "transfers": [4      {5        "id": "0x…",6        "tokenId": "1"7      },8      {9        "id": "0x…",10        "tokenId": "2"11      },12      {13        "id": "0x…",14        "tokenId": "3"15      }16    ]17  }18}

              Заключение

              Using both Immutable Entities and Bytes as IDs has been shown to markedly improve Subgraph efficiency. Specifically, tests have highlighted up to a 28% increase in query performance and up to a 48% acceleration in indexing speeds.

              Читайте больше о применении неизменяемых объектов и Bytes как идентификаторов в этом блоге от Дэвида Луттеркорта, инженера-программиста в Edge & Node: Два простых способа улучшить производительность субграфов.

              Лучшие практики для субграфов 1-6

              1. Improve Query Speed with Subgraph Pruning

              2. Improve Indexing and Query Responsiveness by Using @derivedFrom

              3. Improve Indexing and Query Performance by Using Immutable Entities and Bytes as IDs

              4. Improve Indexing Speed by Avoiding eth_calls

              5. Simplify and Optimize with Timeseries and Aggregations

              6. Use Grafting for Quick Hotfix Deployment

              ⁠Редактировать на GitHub⁠

              Arrays with @derivedFromAvoiding eth_calls
              На этой странице
              • Краткое содержание
              • Неизменяемые объекты
              • Как это устроено
              • Когда не следует использовать неизменяемые объекты
              • Использование Bytes в качестве идентификаторов
              • Причины, по которым не стоит использовать Bytes как идентификаторы
              • Конкатенация (объединение) с использованием Bytes как идентификаторов
              • Сортировка с использованием идентификаторов Bytes
              • Заключение
              • Лучшие практики для субграфов 1-6
              The GraphСтатусТестовая сетьБрундовые ресурсыФорумБезопасностьПолитика конфиденциальностиУсловия обслуживания