Computer >> 컴퓨터 >  >> 프로그램 작성 >> Redis

Redis로 실시간 거래 플랫폼 구축

포트폴리오는 자산 및 자산 관리 산업의 기반을 형성합니다. Harry Makowitz가 현대적인 포트폴리오 이론을 개척한 이래로 자산 및 자산 관리 전문가는 주어진 위험 수준에서 포트폴리오의 수익을 극대화하는 데 집착해 왔습니다. 오늘날, 업계의 전문가들은 수백만 명의 개인 투자자들과 합류하여 투자의 판도를 영원히 바꾸어 놓았습니다. 이러한 신규 진입자들은 소매 중개, 거래소, 교환소의 거래 인프라를 뒷받침하는 기술에 엄청난 파급효과를 일으키고 있습니다.

예를 들어 2021년 1월의 GameStop 주식 열풍을 생각해 보십시오. 개인 투자자들은 GameStop 주식을 기록적인 수준으로 거래하기 시작했습니다. 이 투자자들은 또한 AMC Entertainment와 같은 다른 밈 주식에 쌓여 VIX로 측정된 몇 거래일 만에 전체 시장 변동성이 76% 이상 상승했습니다. 이러한 변동성은 수천 개의 증권에 대한 가격 압력으로 이어졌습니다. 수백만 명의 투자자가 동시에 자신의 포트폴리오에 미친 듯이 액세스하려고 시도했지만 수요를 따라가지 못하는 앱에 직면했습니다. 투자자들은 앱이 가장 필요할 때 제대로 작동하지 않는 회사에 친절하지 않습니다.

Redis로 실시간 거래 플랫폼 구축

이 광란의 시기에 대부분의 투자자는 포트폴리오에 대해 항상 액세스해야 하는 두 가지 데이터 포인트를 찾고 있습니다.

  1. 당시 포트폴리오의 총 가치는 얼마입니까?
  2. 포트폴리오에 있는 특정 증권의 손익은 무엇입니까?

이러한 질문에 대한 답변을 통해 투자자는 특정 증권을 구매, 판매 또는 보유할 수 있습니다. 오늘날과 같이 빠르게 변화하는 시장에서 지연은 기회와 이익의 손실을 의미할 수 있습니다. 이러한 질문에 답하려면 가격에 실시간으로 액세스해야 하지만 두 가지 큰 문제가 있습니다.

  • 수천 증권의 가격을 동시에 업데이트
  • 수백만 고객의 요청에 한 번에 응답

유가 증권의 가격은 거래량, 특정 유가 증권의 변동성 및 시장의 변동성에 따라 빠르게 변할 수 있습니다. 반면에 중개업체는 수백만 명의 고객을 보유할 수 있으며 각 고객은 포트폴리오에 수십 개의 증권을 보유하고 있습니다. 고객이 로그인하는 즉시 포트폴리오를 최신 가격으로 업데이트해야 하며 중개업체가 거래소에서 가격을 받을 때 최신 가격으로 유지해야 합니다.

기본적으로 우리는 실시간 주식 차트를 만들고 있습니다. 많은 중개 앱은 대규모로 이를 시도하지 않습니다. 대신 이러한 앱은 수백만 명의 고객에게 가격을 제공하는 대신 최신 가격을 가져옵니다. 예를 들어 포트폴리오 페이지에 새로 고침 버튼이 있을 수 있습니다.

이러한 차세대 과제는 사소하지 않으며 초당 수백만 개의 작업을 처리하도록 설계되지 않은 디스크 기반 데이터베이스로 쉽게 해결할 수 없습니다. 금융 산업의 요구 사항에는 쉽게 확장할 수 있고 초당 수억 건의 작업을 처리할 수 있는 데이터베이스가 필요합니다. 인메모리 데이터베이스 플랫폼인 Redis Enterprise는 이러한 수많은 문제를 해결할 잠재력을 가지고 있습니다.

Redis로 실시간 거래 플랫폼 구축

금융계의 다양한 실시간 사용 사례를 다루는 블로그 시리즈 중 첫 번째 블로그입니다. 우리는 각 사용 사례의 세부 사항과 비즈니스 과제와 이러한 과제를 해결하는 데 Redis Enterprise가 할 수 있는 역할을 다룰 것입니다. 블로그의 일부로 샘플 디자인, 데이터 모델 및 코드 샘플을 제공합니다. 우리는 또한 각 접근 방식의 장단점을 논의할 것입니다.

이 블로그 게시물에서 다음 내용을 다룹니다.

  • Redis Enterprise에서 확장 가능한 고성능 증권 포트폴리오 데이터 모델의 샘플 구현
  • 중개사가 거래소에서 최신 가격을 수신하면 실시간으로 포트폴리오의 증권 가격을 업데이트합니다.

클라이언트 앱이 포트폴리오를 검색하고 최신 가격을 받으면 다음을 수행할 수 있습니다.

  • 포트폴리오의 총 포트폴리오 가치를 계산합니다.
  • 각 포트폴리오 보유에 대한 손익을 계산합니다.

보안 포트폴리오 데이터 모델

포트폴리오의 보유를 모델링하는 것으로 시작하겠습니다. 아래 그림에서 CVS Health Corp.(NYSE: CVS)는 당사의 예시 보유 자산 중 하나입니다. CVS에는 두 개의 별도 로트가 있었습니다. 첫 번째는 2021년 1월 4일에, 두 번째는 2021년 3월 1일에 인수되었습니다. 매수 기간 동안 같은 수의 주식을 구입했습니다.   각 로트에 대한 거래. 두 거래 모두 10주에 대한 거래였지만 서로 다른 주당 가격입니다. — 첫 번째 로트의 경우 $68.3378, 두 번째 로트의 경우 $68.82입니다. 포트폴리오에서 보유하고 있는 CVS의 총 수량은 20이고 평균 비용은 다음과 같이 계산됩니다. (($68.3378 * 10) + ($68.82 * 10))/20 =주당 $68.5789.

Redis로 실시간 거래 플랫폼 구축

요구사항 구현

Redis로 실시간 거래 플랫폼 구축

Redis의 데이터 표현은 단순합니다. 예를 들어 다른 세트 내에 세트를 포함할 수 없습니다. 따라서 ER 다이어그램으로 설명하는 데이터 모델은 반드시 직접 구현될 수는 없습니다. 엔터티 모델을 직접 구현하는 것은 원하는 성능 특성을 갖지 않을 수 있으므로 구현에 이르기까지 조금 다르게 생각해야 합니다. 이 섹션에서는 Redis를 사용하여 고성능 및 확장성 구현을 설계할 때 필요한 몇 가지 기본 설계 원칙을 다룹니다.

여기에서 데이터 모델은 다음 항목을 언급합니다.

Redis로 실시간 거래 플랫폼 구축

ER 다이어그램은 진행 상황을 보는 데 도움이 되는 시각적 표현을 제공합니다.

위의 다이어그램에서 누락된 것은 들어오는 가격 세트가 유가 증권의 가격 내역에 기록되지만 즉각적인 가치와 이익의 계산입니다. 가격이 변경됨에 따라. 따라서 ER 다이어그램은 포트폴리오 평가가 수행되는 컨텍스트인 비교적 정적인 데이터를 나타냅니다.

전체 아키텍처

이 시스템에 대해 고려해야 할 핵심 사항은 다음과 같습니다.

  • 적시성(예:대기 시간)이 중요합니다. 이것은 전반적인 운전 속성입니다.
  • 포트폴리오 가치 계산은 계정별 데이터(예:로트 정보)와 계정 간에 공유되는 데이터(예:유가증권 가격)를 결합합니다. 따라서 계정별 데이터는 공유 데이터가 사용되는 컨텍스트입니다.
  • 포트폴리오 가치는 투자자가 온라인 상태인 계정(총 계정 수의 일부)에 대해서만 계산하면 됩니다.
  • 데이터는 시스템을 통해 가격이 산출되는 거래소에서 온라인 투자자에게 포트폴리오 가치를 제시하는 클라이언트 시스템(브라우저, 휴대폰 애플리케이션)으로 흐릅니다.
  • 역동성이 높은 특정 영역에는 입력 데이터 흐름 속도와 온라인 투자자 수가 포함됩니다.

이러한 점을 감안할 때 몇 가지 일반적인 접근 방식은 다음과 같습니다.

  • 정적 데이터와 동적 데이터 모두에 대한 짧은 지연 시간 액세스를 위해 Redis의 인메모리 아키텍처에 의존
  • Redis의 데이터 구조를 사용하여 데이터 모델링을 최적화하여 천천히 변화하는 컨텍스트 데이터에 빠르게 액세스할 수 있습니다.
  • Redis의 통신 구조(Streams, Consumer Groups, Pub/Sub)를 사용하여 동적 데이터 요구 사항을 처리합니다.
  • 전체 시스템 성능에 영향을 주지 않고 필요한 만큼만 데이터 저장 공간을 줄입니다.
  • 클라이언트 자체에 대한 클라이언트별 계산 구현. 이는 온라인 투자자 수에 따라 자연스럽게 자동으로 확장되어 규모 부담을 크게 덜어줍니다.

다음은 주요 계산 구성 요소와 데이터 흐름입니다.

Redis로 실시간 거래 플랫폼 구축

Redis Enterprise는 온프레미스 배포, Kubernetes, 하이브리드 클라우드 배포, 관리형 서비스 또는 기본 자사 클라우드 서비스와 같은 여러 시스템에 걸쳐 하나 이상의 노드로 구성되며 수십만 명의 투자자가 고객과 온라인으로 연결됩니다. ) 선택.

Redis Enterprise 구성 요소

보안 가격 업데이트는 Redis Streams에 흡수됩니다. 유가 증권에 대한 업데이트는 이 스트림에서 함께 혼합되며 데이터를 유용하게 만들기 위해 세분화해야 합니다. 소비자 그룹은 해당 세분화를 수행하고 보안별로 데이터를 두 가지 구조로 처리하는 데 사용됩니다.

  • RedisTimeSeries 데이터베이스, 가격 변경 이력 추적(또한 방금 연결한 클라이언트에 대한 최신 가격 기록)
  • Pub/Sub 브로커 채널을 통해 해당 채널을 구독한 고객(즉, 포트폴리오에 해당 증권이 포함된 투자자)에게 가격 변경 알림을 푸시합니다.

다음 다이어그램은 아키텍처의 이 부분을 자세히 설명합니다.

Redis로 실시간 거래 플랫폼 구축

우리 모델에서 가장 중요한 요소는 로트 및 관련 보안을 나타내는 계정별 데이터입니다. 성능에 중점을 두고 Redis에서 데이터 모델링에 대해 생각하는 방법의 예로 두 가지 구현을 비교할 것입니다. 다른 구현도 가능합니다. 여기서 우리의 목표는 Redis에서 데이터를 구현할 때 전반적인 디자인 원칙과 사고 프로세스를 도입하는 것입니다.

다음 정보를 구체적인 예로 사용하겠습니다.

Redis로 실시간 거래 플랫폼 구축

우리는 부동 소수점을 피하고 모든 것을 정수로 유지하기 위해 가능한 가장 낮은 통화 단위로 가격을 책정하고 있습니다. 우리는 고객이 달러와 센트로의 변환을 처리하도록 할 수 있습니다. 이 예에서는 소수점 이하 두 자리의 정밀도로 가격을 사용하고 있습니다.

데이터 모델 A

첫 번째 구현은 계정 ID로 식별되는 SET를 사용하여 계정에 있는 모든 로트의 ID를 기록한 다음 LOT ID로 식별되는 LOT당 하나의 Redis HASH를 사용하며 시세, 수량 및 구매 가격을 필드로 사용합니다. 즉, HASH를 사용하여 각 속성과 함께 LOT 엔터티 구조를 모델링합니다. LOT 엔티티의 필드 Redis HASH에서.

이 데이터 모델을 사용하면 각 계정에 대한 키와 Redis SET로 저장된 해당 계정에 대한 모든 LOT ID를 포함하는 값이 있습니다.

lotids:<ACCOUNT_ID> SET <LOTID>

또한 각 로티드에 대해 시세, 수량 및 구매 가격이 필드인 HASH가 있습니다.

lot:<LOTID> HASH <ticker TICKER> <quantity INTEGER> <price INTEGER>

구체적으로 다음과 같이 키를 생성합니다.

127.0.0.1:6379> SADD 로트:ACC-1001 LOT-9001 LOT-9002(정수) 2127.0.0.1:6379> HMSET 로트:LOT-9001 시세표 AAPL 수량 200 가격 .17.650.70 HMSET lot:LOT-9002 시세 CAT 수량 1200 가격 18063OK

RedisTimeSeries 모듈을 사용하면 대용량 삽입 및 짧은 대기 시간 읽기와 함께 관련 시간 및 값 쌍을 저장하고 검색할 수 있습니다. 해당 시계열 키를 사용할 때 클라이언트가 액세스할 관심 종목의 최신 가격을 얻을 것입니다.

price_history:<TICKER> TIMESERIES <price INTEGER>

127.0.0.1:6379> TS.GET 가격 기록:APPL1) (정수) 16194568530612) 12572127.0.0.1:6379> TS.GET 가격 기록:CAT1) (정수) 2016 

업데이트를 위해 가격 책정 채널을 구독하십시오:

<TICKER> SUBSCRIPTION_CHANNEL

모든 데이터를 가져오기 위해 클라이언트는 다음 작업을 수행합니다.

  1. lotids에서 SMEMBERS 1회 키 - 시간 복잡도 O(N)(N은 로트 수)
  2. 로트에서 N 번 HGETALL 키 - 시간 복잡도 N 곱하기 O(1)
  3. price_history에서 TS.GET을 T 번 키 – 시간 복잡도 T 곱하기 O(1), T는 티커 수
  4. 에서 1회 구독 채널 - 시간 복잡도 O(T)(한 번의 SUBSCRIBE 호출로 모든 채널을 구독할 수 있음)

전체 시간 복잡도는 O(N + T)입니다.

구체적으로 작업 1과 2는 다음과 같습니다.

127.0.0.1:6379> SMEMBERS lotids:ACC10011) "LOT-9001"2) "LOT-9002"127.0.0.1:6379> HGETALL lot:LOT-90011) "ticker"2) "AAPL"3) " 수량"4) "200"5) "가격"6) "12556"127.0.0.1:6379> HGETALL lot:LOT-90021) "시세"2) "CAT"3) "수량"4) "1200"5) "가격"6) "18063"

파이프라이닝(클라이언트 측 일괄 처리 형식) 및/또는 LUA 스크립트의 반복 사용(SCRIPT LOAD 및 EVALSHA 사용)을 사용하여 네트워크 지연 시간을 최소화할 수 있습니다. 참고 사항:트랜잭션은 파이프라인을 사용하여 구현할 수 있으며 네트워크 지연 시간을 줄일 수 있지만 이는 클라이언트에 따라 다르며 목표는 서버의 원자성이므로 네트워크 지연 시간 문제를 실제로 해결하지는 못합니다. 파이프라인은 입력과 출력이 서로 독립적이어야 하는 명령으로 구성됩니다. LUA 스크립트를 사용하려면 모든 키가 사전에 제공되어야 합니다.  모든 키가 동일한 슬롯에 해시됩니다(자세한 내용은 이 주제에 대한 Redis Enterprise 문서 참조).

이러한 제약 조건이 주어지면 파이프라인에 대한 작업 할당이 다음과 같은 것을 알 수 있습니다.

  • 파이프라인 1:작업 #1의 단일 명령
  • 파이프라인 2:작업 #2의 모든 N 명령
  • 파이프라인 3:작업 #3 및 #4의 모든 N 명령

각 작업이 서로 다른 키를 사용하고 해당 키에 동일한 슬롯으로 해시될 수 있는 공통 부분이 없기 때문에 LUA 스크립트를 사용하는 것이 불가능합니다.

이 모델을 사용할 때 O(N+T)의 시간 복잡도와 3개의 네트워크 홉이 있습니다.

데이터 모델 B

대안 모델은 LOT 엔티티 구조를 평면화하고 계정 ID로 식별되는 키를 사용하여 각 엔티티 속성을 나타내는 것입니다. 각 HASH의 필드는 LOT ID와 수량, 티커 또는 가격에 해당하는 값이 됩니다. 따라서 다음과 같은 키가 있습니다.

tickers_by_lot: <ACCOUNT_ID> HASH <LOTID TICKER>

quantities_by_lot:<ACCOUNT_ID> HASH <LOTID INTEGER>

prices_by_lot:<ACCOUNT_ID> HASH <LOTID INTEGER>

이러한 해시는 데이터 모델 A의 LOTID 및 LOT 키를 대체하지만 price_history 및 <TICKER> 키는 그대로 유지됩니다.

키 생성:

HSET tickers_by_lot:ACC-1001 LOT-9001 AAPL LOT-9002 CATHSET 수량_by_lot:ACC-1001 LOT-9001 200 LOT-9002 1200HSET 가격_by_lot:ACC-1001 LOT-9051 12 

값 검색:

127.0.0.1:6379> HGETALL tickers_by_lot:ACC-10011) "LOT-9001"2) "AAPL"3) "LOT-9002"4) "CAT"127.0.0.1:6379> HGETALL quantity_by_lot:ACC-10 ) "LOT-9001"2) "200"3) "LOT-9002"4) "1200"127.0.0.1:6379> HGETALL price_by_lot:ACC-10011) "LOT-9001"2) "12556"3) "LOT -9002"4) "18063"

이제 클라이언트에 필요한 작업은 다음과 같습니다.

  1. lot_quantity에서 1배 HGETALL 키 - 시간 복잡도 N x O(1)
  2. lot_ticker에서 1회 HGETALL 키 - 시간 복잡도 N x O(1)
  3. lot_price에 HGETALL의 1배 키 - 시간 복잡도 N x O(1)
  4. price_history의 T 배 TS.GET 키 - 시간 복잡도 T x O(1), T는 티커 수
  5. 에서 1회 구독 채널 - 시간 복잡도 1 x O(T)

이것은 이전과 동일하게 O(N+T)의 전체 시간 복잡도를 갖습니다.

파이프라인 관점에서 이것은 다음과 같습니다.

  • 파이프라인 1 - 작업 #1, #2, #3의 모든 명령
  • 파이프라인 2 - 작업 #4 및 #5의 모든 T 명령

그래서 우리는 네트워크 홉 수를 1로 줄였습니다. 절대적인 측면에서는 많지 않지만 상대적인 측면에서는 33%입니다.

또한 키를 알고 있기 때문에 LUA를 쉽게 사용할 수 있으며 특정 계정의 모든 키를 동일한 슬롯에 매핑할 수 있습니다. 작업의 단순성을 감안할 때 우리는 LUA에 대해 더 이상 파고들지 않을 것이지만, 이 디자인이 최소한 그것을 가능하게 한다는 점에 유의하십시오!

간단한 벤치마크에서 데이터 모델 B는 4.13ms 더 빠르게 실행되었습니다(수천 번 이상 벤치마크). 클라이언트가 계정에 대해 초기화될 때마다 한 번만 실행된다는 점을 감안할 때 전체 성능에는 영향을 미치지 않을 것입니다.

요약

이 블로그에서는 Redis 데이터 유형을 사용하여 Entity 모델의 두 가지 가능한 구현을 보여주었습니다. 또한 Redis 데이터 유형을 선택할 때마다 수행해야 하는 시간 복잡도 분석과 함께 대규모 및 고성능이 필요한 경우 중요한 단계인 네트워크 성능 향상을 고려하는 방법을 도입했습니다. 후속 블로그에서는 데이터 모델이 확장됨에 따라 이러한 아이디어를 더 확장할 것입니다.

우리는 대규모 증권 포트폴리오를 관리하는 데 있어 몇 가지 비즈니스 과제를 소개했으며 다음을 보여주었습니다.

  • 실시간 확장 가능한 증권 포트폴리오를 구현하기 위한 Redis 데이터 모델입니다.
  • 포트폴리오의 총 가치와 각 보유의 손익을 계산하는 데 사용할 수 있는 고성능 실시간 가격 업데이트 시스템입니다.

이 두 가지 중요한 기능을 갖춘 중개 앱 클라이언트는 수백만 개의 계정을 처리할 수 있도록 수행하고 확장하는 실시간 포트폴리오 업데이트를 제공할 수 있습니다. 이 디자인은 포트폴리오의 총 가치와 각 보유 자산에 대한 손익을 실시간으로 표시할 수 있습니다. 이 데이터 모델과 아키텍처는 증권을 넘어 암호화폐, 광고 거래소 등의 사용 사례에도 적용될 수 있습니다.