클라이언트-서버 동기화 패턴 / 알고리즘?


224

클라이언트-서버 동기화 패턴이 있어야한다는 느낌이 듭니다. 그러나 나는 완전히 하나를 구글에 실패했습니다.

상황은 매우 간단합니다. 서버는 여러 클라이언트가 동일한 데이터에 연결하고 조작하는 중앙 노드입니다. 충돌이 발생할 경우 서버에있는 모든 데이터가 우선 순위를 갖습니다 (사용자의 충돌 해결을 피하기 위해). 잠재적으로 많은 양의 데이터로 인해 부분 동기화가 선호됩니다.

그러한 상황에 대한 패턴 / 모범 사례가 있습니까, 아니면 모르는 경우-귀하의 접근법은 무엇입니까?

아래는 이제 내가 해결하려고 생각하는 방법입니다. 데이터와 평행하게 모든 트랜잭션 타임 스탬프가있는 수정 저널이 개최됩니다. 클라이언트가 연결되면 마지막 확인 이후 모든 변경 사항이 통합 된 형태로 수신됩니다 (서버는 목록을 살펴보고 삭제가 뒤 따르는 추가 사항을 제거하고 각 아톰에 대한 업데이트를 병합하는 등). 짜잔, 우리는 최신 상태입니다.

대안은 각 레코드의 수정 날짜를 유지하고 데이터 삭제를 수행하는 대신 삭제 된 것으로 표시하는 것입니다.

이견있는 사람?


27
이런 종류의 일에 대한 패턴에 대한 이야기는 거의 없다는 데 동의했습니다. 비록이 시나리오는 꽤 일반적이긴하지만
Jack Ukleja

답변:


88

분산 변경 관리의 작동 방식을 살펴 봐야합니다. 델타 작업을 관리하는 SVN, CVS 및 기타 리포지토리를 살펴보십시오.

몇 가지 사용 사례가 있습니다.

  • 변경 사항을 동기화하십시오. 변경 로그 (또는 델타 히스토리) 접근 방식이 적합합니다. 클라이언트는 델타를 서버로 보냅니다. 서버는 델타를 통합하고 클라이언트에 배포합니다. 이것이 일반적인 경우입니다. 데이터베이스는이를 "트랜잭션 복제"라고합니다.

  • 클라이언트의 동기화가 끊어졌습니다. 백업 / 복원 또는 버그로 인해 발생합니다. 이 경우 클라이언트는 델타를 거치지 않고 서버에서 현재 상태를 가져와야합니다. 이것은 마스터에서 세부 사항까지의 사본이며 델타 및 성능이 저하됩니다. 일회성입니다. 클라이언트가 고장났습니다. 이를 최적화하지 말고 신뢰할 수있는 사본을 구현하십시오.

  • 클라이언트가 의심 스럽다. 이 경우 클라이언트와 서버를 비교하여 클라이언트가 최신이고 델타가 필요한지 판별해야합니다.

매번 변경 될 때마다 데이터베이스 및 SVN 디자인 패턴을 따라야합니다. 이렇게하면 클라이언트가 동기화를 시도하기 전에 사소한 요청 ( "어떤 수정본이 있어야합니까?")을 만들 수 있습니다. 그럼에도 불구하고 쿼리 ( "2149 이후의 모든 델타")는 클라이언트와 서버가 처리하기 매우 간단합니다.


델타가 정확히 무엇인지 설명해 주시겠습니까? 내 생각에 그것은 해시 / 타임 스탬프 조합 일 것입니다 ... 나는 당신으로부터 듣고 싶습니다.
Anis

델타는 두 개정 사이의 변경을 나타냅니다. 예를 들어, 사용자 이름이 변경된 경우 델타는 {revision : 123, name : "John Doe"}
dipole_moment

31

팀의 일원으로 데이터 동기화와 관련된 많은 프로젝트를 수행 했으므로이 질문에 대답 할 수 있어야합니다.

데이터 동기화는 매우 광범위한 개념이므로 논의 할 방법이 너무 많습니다. 여기에는 장점과 단점이있는 다양한 접근 방식이 포함됩니다. 동기 / 비동기, 클라이언트 / 서버 / 피어 투 피어라는 두 가지 관점에 따라 분류가 가능합니다. 동기화 구현은 이러한 요소, 데이터 모델 복잡성, 전송 및 저장되는 데이터 양 및 기타 요구 사항에 따라 크게 달라집니다. 따라서 각 특정 경우에 앱 요구 사항을 충족하는 가장 간단한 구현을 선택해야합니다.

기존의 상용 솔루션을 검토하여 동기화 대상 객체의 세분성이 다른 몇 가지 주요 동기화 클래스를 설명 할 수 있습니다.

  • 전체 문서 또는 데이터베이스의 동기화는 Dropbox, Google Drive 또는 Yandex.Disk와 같은 클라우드 기반 응용 프로그램에서 사용됩니다. 사용자가 파일을 편집하고 저장하면 새 파일 버전이 클라우드에 완전히 업로드되어 이전 사본을 덮어 씁니다. 충돌이 발생하면 두 파일 버전이 모두 저장되므로 사용자가 관련성이 높은 버전을 선택할 수 있습니다.
  • 키-값 쌍의 동기화는 변수가 원자적인 것으로 간주되는 (즉, 논리 구성 요소로 나뉘 지 않는) 단순한 데이터 구조를 가진 앱에서 사용될 수 있습니다. 이 옵션은 값과 문서를 완전히 덮어 쓸 수 있으므로 전체 문서를 동기화하는 것과 유사합니다. 그러나 사용자 관점에서 문서는 여러 부분으로 구성된 복잡한 개체이지만 키-값 쌍은 짧은 문자열 또는 숫자입니다. 따라서이 경우 마지막으로 변경 한 경우 더 관련성 높은 값을 고려하여보다 간단한 갈등 해결 전략을 사용할 수 있습니다.
  • 트리 또는 그래프로 구조화 된 데이터 동기화는 매번 업데이트 할 때마다 데이터베이스를 전체적으로 전송할 수있는 데이터 양이 큰보다 복잡한 응용 프로그램에서 사용됩니다. 이 경우 개별 개체, 필드 또는 관계 수준에서 충돌을 해결해야합니다. 우리는 주로이 옵션에 중점을 둡니다.

따라서이 기사에 대한 지식을 습득하여 핵심 데이터 기반 iOS 앱의 주제 => 데이터 동기화 ( http://blog.denivip.ru/index.php/2014/04 / data-syncing-in-core-data-based-ios-apps /? lang = en )


3
^^^^^^ 이것은 최고의 답변입니다, 여러분!
hgoebl

나는 Denis가 주제에 많은 것을 가져 왔고 기사 링크가 훌륭하다는 데 동의합니다. DanielPaull이 언급 한 구약에 대해서도 이야기합니다. S.Lott의 답변은 좋지만 이것은 훨씬 더 깊이 있습니다.
Krystian

28

실제로 필요한 것은 OT ( Operational Transform )입니다. 이것은 많은 경우에 갈등을 수용 할 수도 있습니다.

이것은 여전히 ​​활발한 연구 분야이지만 다양한 OT 알고리즘이 구현되어 있습니다. 저는 수년간 이러한 연구에 참여해 왔으므로이 경로가 귀하에게 관심이 있는지 알려 주시면 관련 자료를 제공해 드리겠습니다.


7
Daniel, 관련 리소스에 대한 조언을 부탁드립니다.
Parand

4
나는 단지 wikipedia 기사를 다시 읽습니다. 그것은 먼 길이며 그 페이지의 맨 아래에 많은 관련 참조가 있습니다. 나는 당신에게 Chengzheng Sun의 작품을 지적했을 것입니다-그의 작품은 Wikipedia에서 인용되었습니다. en.wikipedia.org/wiki/Operational_transformation . 희망이 도움이됩니다!
Daniel Paull

13

문제는 명백하지 않지만, 내가 당신이라면 낙관적 잠금을 조사 할 것입니다. 서버가 각 레코드에 대해 리턴하는 시퀀스 번호로 구현 될 수 있습니다. 클라이언트가 레코드를 다시 저장하려고하면 서버에서받은 시퀀스 번호가 포함됩니다. 업데이트가 수신 될 때 시퀀스 번호가 데이터베이스에있는 것과 일치하면 업데이트가 허용되고 시퀀스 번호가 증가합니다. 시퀀스 번호가 일치하지 않으면 업데이트가 허용되지 않습니다.


2
시퀀스 번호는 당신의 친구입니다. 지속성 메시지 큐에 대해 생각해보십시오.
Daniel Paull

7

약 8 년 전에 앱용으로 이와 같은 시스템을 구축했으며 앱 사용이 증가함에 따라 발전한 몇 가지 방법을 공유 할 수 있습니다.

모든 장치의 모든 변경 (삽입, 업데이트 또는 삭제)을 "기록"테이블에 기록하여 시작했습니다. 예를 들어 누군가가 "contact"테이블에서 전화 번호를 변경하면 시스템은 contact.phone 필드를 편집하고 action = update, field = phone, record = [contact ID], value = [새 전화 번호]. 그런 다음 장치가 동기화 될 때마다 마지막 동기화 이후 기록 항목을 다운로드하여 로컬 데이터베이스에 적용합니다. 위에서 설명한 "트랜잭션 복제"패턴처럼 들립니다.

한 가지 문제는 다른 장치에서 항목을 만들 수있을 때 ID를 고유하게 유지하는 것입니다. 나는 이것을 시작할 때 UUID에 대해 몰랐기 때문에 자동 증가 ID를 사용하고 중앙 서버에서 실행되는 복잡한 코드를 작성하여 장치에서 업로드 된 새로운 ID를 확인하고 충돌이있는 경우 고유 ID로 변경합니다. 소스 장치가 로컬 데이터베이스에서 ID를 변경하도록 지시하십시오. 새 레코드의 ID를 변경하는 것만 큼 나쁘지는 않지만 연락처 테이블에 새 항목을 만든 다음 이벤트 테이블에 새 관련 항목을 만들면 외래 키도 있습니다. 확인하고 업데이트하십시오.

결국 UUID가 이것을 피할 수 있다는 것을 알았지 만 그때까지 내 데이터베이스가 상당히 커지고 전체 UUID 구현으로 인해 성능 문제가 발생할 것을 두려워했습니다. 따라서 전체 UUID를 사용하는 대신 무작위로 생성 된 8 자의 영숫자 키를 ID로 사용하기 시작했으며 충돌을 처리하기 위해 기존 코드를 그대로 두었습니다. 내 현재 8 문자 키와 UUID의 36 문자 사이에 불필요한 부풀림없이 충돌을 제거 할 수있는 스위트 스팟이 있어야하지만 이미 충돌 해결 코드가 있으므로 실험 해 보는 것이 우선 순위가 아닙니다. .

다음 문제는 히스토리 테이블이 나머지 데이터베이스 전체보다 약 10 배 크다는 것입니다. 이로 인해 스토리지가 비싸고 히스토리 테이블의 유지 보수가 어려울 수 있습니다. 전체 테이블을 유지하면 사용자가 이전 변경 사항을 롤백 할 수 있지만 과도하게 느껴지기 시작했습니다. 따라서 장치를 마지막으로 다운로드 한 기록 항목이 기록 테이블에 더 이상 존재하지 않으면 서버가 최근 기록 항목을 제공하지 않고 대신 모든 데이터를 포함하는 파일을 제공하는 루틴을 동기화 프로세스에 추가했습니다. 그 계정. 그런 다음 90 일보다 오래된 기록 항목을 삭제하기 위해 cronjob을 추가했습니다. 즉, 사용자는 90 일 미만의 변경 사항을 계속 롤백 할 수 있으며 90 일마다 한 번 이상 동기화하면 업데이트가 이전과 같이 증분됩니다. 하지만 90 일 이상 기다리면

이 변경으로 인해 히스토리 테이블의 크기가 거의 90 % 줄었으므로 이제 히스토리 테이블을 유지 보수하면 데이터베이스가 10 배가 아닌 2 배만 커집니다. 이 시스템의 또 다른 장점은 필요한 경우 기록 테이블 없이도 동기화를 계속 수행 할 수 있다는 것입니다. 또는 다른 가격대의 계정에 대해 다른 롤백 기간을 제공 할 수 있습니다. 다운로드 할 때까지 90 일이 지난 후에도 전체 파일이 증분 형식보다 효율적입니다.

오늘 다시 시작한 경우 ID 충돌 검사를 건너 뛰고 경우에 따라 일종의 오류 검사와 함께 충돌을 제거하기에 충분한 키 길이를 목표로합니다. 그러나 최근 업데이트에 대한 기록 테이블과 증분 다운로드 조합 또는 필요할 때 전체 다운로드가 제대로 작동했습니다.


1

델타 (변경) 동기화의 경우 pubsub 패턴을 사용하여 변경 사항을 구독 한 모든 클라이언트에 다시 게시하면 푸셔 와 같은 서비스에서 이를 수행 할 수 있습니다.

데이터베이스 미러의 경우 일부 웹 프레임 워크는 로컬 미니 데이터베이스를 사용하여 서버 측 데이터베이스를 브라우저 데이터베이스의 로컬에 동기화합니다. 부분 동기화가 지원됩니다. 계량기를 점검하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.