클라이언트-서버 데이터베이스 동기화


82

항상 온라인 상태가 아닌 클라이언트 응용 프로그램과 중앙 서버의 데이터를 동기화하기위한 몇 가지 일반적인 전략을 찾고 있습니다.

내 특별한 경우에는 sqlite 데이터베이스가있는 안드로이드 폰 애플리케이션과 MySQL 데이터베이스가있는 PHP 웹 애플리케이션이 있습니다.

사용자는 전화 애플리케이션과 웹 애플리케이션에서 정보를 추가하고 편집 할 수 있습니다. 전화가 서버와 즉시 통신 할 수없는 경우에도 한 곳에서 변경 한 내용이 모든 곳에 반영되도록해야합니다.

전화에서 서버로 또는 그 반대로 데이터를 전송하는 방법에 대해서는 관심이 없습니다. 예를 들어 MySQL에서 사용할 수있는 복제 기능을 사용할 수 없기 때문에 내 특정 기술에 대해서만 언급합니다.

클라이언트-서버 데이터 동기화 문제가 오랫동안 존재 해 왔음을 알고 있으며 문제를 처리하기위한 패턴에 대한 정보 (기사, 서적, 조언 등)를 원합니다. 강점, 약점 및 장단점을 비교하기 위해 동기화를 처리하는 일반적인 전략에 대해 알고 싶습니다.

답변:


93

가장 먼저 결정해야 할 것은 변경 사항이 충돌하는 경우 어느 쪽이 "권한"으로 간주되는지에 대한 일반적인 정책입니다.

즉, 레코드 # 125가 1 월 5 일 오후 10시에 서버에서 변경되고 동일한 레코드가 1 월 5 일 오후 11시에 전화기 중 하나 (클라이언트 A라고 부르겠습니다)에서 변경되었다고 가정합니다. 마지막 동기화는 1 월 3 일이었습니다. 그런 다음 사용자는 1 월 8 일에 다시 연결합니다.

변경해야 할 사항을 식별하는 것은 클라이언트와 서버 모두 마지막 동기화 날짜를 알고 있으므로 마지막 동기화 이후에 생성되거나 업데이트 된 모든 항목 (아래 참조)을 조정해야 한다는 점에서 "쉽습니다" .

따라서 변경된 유일한 레코드가 # 125라고 가정합니다. 둘 중 하나가 자동으로 "승리"하고 다른 버전을 덮어 쓰도록 결정하거나 사용자가 올바른 버전 (서버 또는 클라이언트)을 결정하고 다른 버전을 덮어 쓸 수있는 조정 단계를 지원해야합니다.

이 결정은 매우 중요하며 클라이언트의 "역할"에 가중치를 부여해야합니다. 특히 클라이언트와 서버 사이에 잠재적 충돌이있을 때뿐만 아니라 다른 클라이언트가 동일한 레코드를 변경할 수있는 경우에 더욱 그렇습니다.

[# 125가 두 번째 클라이언트 (클라이언트 B)에 의해 수정 될 수 있다고 가정하면 아직 동기화되지 않은 클라이언트 B가 동일한 레코드의 또 다른 버전을 제공하여 이전 충돌 해결에 대한 의구심을 갖게 될 가능성이 있습니다.]

위의 " 생성 또는 업데이트 된 "요점 과 관련하여 ... 레코드가 클라이언트 중 하나에서 생성 된 경우 레코드를 올바르게 식별 할 수있는 방법은 무엇입니까 (문제 도메인에서 의미가 있다고 가정)? 앱이 비즈니스 연락처 목록을 관리한다고 가정 해 보겠습니다. 클라이언트 A가 새로 생성 된 John Smith를 추가해야한다고 말하고 서버에 클라이언트 D가 어제 생성 한 John Smith가있는 경우 ... 두 레코드가 서로 다른 사람이 아님을 확신 할 수 없기 때문에 생성합니까? 사용자에게이 충돌도 조정하도록 요청 하시겠습니까?

클라이언트가 데이터 하위 집합의 "소유권"을 가지고 있습니까? 즉, 클라이언트 B가 영역 # 5의 데이터에 대한 "권한"으로 설정된 경우 클라이언트 A가 영역 # 5의 레코드를 수정 / 생성 할 수 있습니까? (이렇게하면 갈등 해결이 더 쉬워 지지만 상황에 따라 실행 불가능할 수 있습니다).

요약하면 주요 문제는 다음과 같습니다.

  • 분리 된 클라이언트가 새 레코드를 만들기 전에 서버에 액세스하지 않았을 수 있음을 고려하여 "ID"를 정의하는 방법입니다.
  • 이전 상황은 솔루션이 아무리 정교하더라도 데이터 중복을 초래할 수 있으므로 이러한 문제를 정기적으로 해결하는 방법과 고객에게 "레코드 # 675"로 간주 한 내용이 실제로 병합 / 대체되었음을 클라이언트에게 알리는 방법을 예측해야합니다. 기록 # 543
  • 충돌을 법정 화폐 로 해결할지 (예 : "마지막 동기화 이후에 전자가 업데이트 된 경우 서버 버전이 항상 클라이언트보다 우선합니다") 또는 수동 개입 으로 해결할지 결정합니다.
  • fiat의 경우 , 특히 클라이언트가 우선권을 갖는다 고 결정하는 경우, 더 많은 변경 사항이있을 수있는 아직 동기화되지 않은 다른 클라이언트를 처리하는 방법도주의해야합니다.
  • 이전 항목은 데이터의 세분성을 고려하지 않았습니다 (설명을 더 간단하게 만들기 위해). 내 예에서와 같이 "기록"수준에서 추론하는 대신 필드 수준에서 변경 내용을 기록하는 것이 더 적절할 수 있습니다. 또는 한 번에 일련의 레코드 (예 : 개인 레코드 + 주소 레코드 + 연락처 레코드)를 작업하여 집계를 일종의 "메타 레코드"로 취급합니다.

서지:

(마지막 세 개는 ACM 디지털 라이브러리에서 가져온 것으로 회원인지 또는 다른 채널을 통해 얻을 수 있는지 알 수 없습니다.)

로부터 Dr.Dobbs의 사이트 :

  • SQL Server CE 및 SQL RDA로 앱 만들기 by Bill Wagner 2004 년 5 월 19 일 (데스크톱 및 모바일 PC 용 애플리케이션 설계 모범 사례-Windows / .NET)

arxiv.org에서 :

  • 충돌없는 복제 된 JSON 데이터 유형 -이 문서는 JSON CRDT 구현을 설명합니다 (충돌없는 복제 된 데이터 유형-CRDT는 동시 수정을 지원하고 이러한 동시 업데이트의 수렴을 보장하는 데이터 구조 제품군입니다).

답변 주셔서 감사합니다. 나는 당신이 설명하는 문제에 대해 일반적으로 사용되는 / 가능한 해결책 (장단점, 비교)에 대해 읽는 데 매우 관심이 있습니다.
Scott Saunders

이미 Wikipedia와 링크 된 항목을 확인했다고 가정합니다. 그렇죠?
p.marino 2010 년

3
+1 해당 문제에 대한 매우 중요한 정보가 포함 된 멋진 게시물입니다. 한 가지 누락 된 점 : 삭제 된 레코드 동기화.
Stefan Steinegger

7
저는 "삭제됨"을 "업데이트 됨"의 특별한 경우로 간주하는 경향이 있습니다. 특히 이런 종류의 상황에서는 "물리적 삭제"대신 "논리적 삭제"를 선호하는 경향이 있기 때문입니다. 그래서 저에게 마스터 나 슬레이브 측에서 "삭제"는 다른 무엇보다 "특수 부울 is-deleted 플래그가 뒤집혔다"는 의미입니다.
p.marino 2010 년

감사. 다른 기사 (dobbs 박사)에 대한 링크를 하나 더 추가했으며 다른 것을 찾을 수있는 경우 참고 문헌을 업데이트 할 것입니다.
p.marino 2010-08-12

9

모든 테이블에 타임 스탬프 열 이 있고 삽입하거나 업데이트 할 때마다 영향을받는 각 행의 타임 스탬프 값을 업데이트하는 것이 좋습니다 . 그런 다음 타임 스탬프가 대상 데이터베이스에있는 것보다 최신인지 확인하는 모든 테이블을 반복합니다. 최신 버전이면 삽입하거나 업데이트해야하는지 확인하십시오.

관찰 1 : 행이 소스 db에서 삭제되고 서버 db에서 동일한 작업을 수행해야하므로 물리적 삭제에 유의하십시오. 이 문제를 해결하여 물리적 삭제를 피하거나 타임 스탬프가있는 테이블의 모든 삭제를 로깅 할 수 있습니다. 다음과 같은 내용 : DeletedRows = (id, table_name, pk_column, pk_column_value, timestamp)따라서 DeletedRows 테이블의 모든 새 행을 읽고 table_name, pk_column 및 pk_column_value를 사용하여 서버에서 삭제를 실행해야합니다.

관찰 2 : 다른 테이블과 관련된 테이블에 데이터를 삽입하면 실패 할 수 있으므로 FK에 유의하십시오. 데이터 동기화 전에 모든 FK를 비활성화해야합니다.


3
시계가 동기화해야
tofutim

6

누군가 비슷한 디자인 문제를 다루고 있고 여러 Android 기기에서 변경 사항을 동기화해야하는 경우 Android 용 Google 클라우드 메시징을 확인하는 것이 좋습니다. (GCM) .

한 클라이언트에서 수행 한 변경 사항을 다른 클라이언트로 전파해야하는 하나의 솔루션을 개발 중입니다. 그리고 방금 개념 증명 구현 (서버 및 클라이언트)을 구현했으며 매력처럼 작동합니다.

기본적으로 각 클라이언트는 델타 변경 사항을 서버로 보냅니다. 예 : 리소스 ID ABCD1234가 값 100에서 99로 변경되었습니다.

서버는 데이터베이스에 대해 이러한 델타 변경을 확인하고 변경을 승인하고 (클라이언트가 동기화 중임) 데이터베이스를 업데이트하거나 변경을 거부합니다 (클라이언트가 동기화되지 않음).

서버가 변경을 승인하면 서버는 GCM을 통해 다른 클라이언트 (델타 변경을 보낸 사람 제외)에 알리고 동일한 델타 변경을 전달하는 멀티 캐스트 메시지를 보냅니다. 클라이언트는이 메시지를 처리하고 데이터베이스를 업데이트합니다.

멋진 점은 이러한 변경 사항이 거의 즉시 전파된다는 것입니다 !!! 해당 장치가 온라인 인 경우. 그리고 이러한 클라이언트에 대해 폴링 메커니즘을 구현할 필요가 없습니다.

기기가 너무 오래 오프라인 상태이고 GCM 대기열에서 전달을 위해 대기중인 메시지가 100 개 이상인 경우 GCM은 해당 메시지를 삭제하고 기기가 다시 온라인 상태가되면 특별한 메시지를 보냅니다. 이 경우 클라이언트는 서버와 전체 동기화를 수행해야합니다.

CGM 클라이언트 구현을 시작하려면 이 튜토리얼 도 확인하십시오 .


5

이것은 Xamarin 프레임 워크를 사용하는 개발자에게 대답합니다 ( /programming/40156342/sync-online-offline-data 참조). )

xamarin 프레임 워크로이를 달성하는 매우 간단한 방법은 Azure의 오프라인 데이터 동기화를 사용하여 요청시 서버에서 데이터를 푸시하고 가져올 수 있도록하는 것입니다. 읽기 작업은 로컬에서 수행되고 쓰기 작업은 요청시 푸시됩니다. 네트워크 연결이 끊어지면 연결이 복원 될 때까지 쓰기 작업이 대기 한 다음 실행됩니다.

구현은 다소 간단합니다.

1) Azure Portal에서 모바일 앱 만들기 ( https://tryappservice.azure.com/ 에서 무료로 사용해 볼 수 있음) )

2) 클라이언트를 모바일 앱에 연결합니다. https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-xamarin-forms-get-started/

3) 로컬 저장소를 설정하는 코드 :

const string path = "localrepository.db";

//Create our azure mobile app client
this.MobileService = new MobileServiceClient("the api address as setup on Mobile app services in azure");

//setup our local sqlite store and initialize a table
var repository = new MobileServiceSQLiteStore(path);

// initialize a Foo table
store.DefineTable<Foo>();

// init repository synchronisation
await this.MobileService.SyncContext.InitializeAsync(repository);
var fooTable = this.MobileService.GetSyncTable<Foo>();

4) 그런 다음 데이터를 푸시하고 가져 와서 최신 변경 사항이 있는지 확인합니다.

await this.MobileService.SyncContext.PushAsync();
await this.saleItemsTable.PullAsync("allFoos", fooTable.CreateQuery());

https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-xamarin-forms-get-started-offline-data/


0

Symmetricds 도 살펴 보시기 바랍니다 . Android 시스템에서 사용할 수있는 SQLite 복제 라이브러리입니다. 이를 사용하여 클라이언트와 서버 데이터베이스를 동기화 할 수 있습니다. 또한 각 클라이언트에 대해 별도의 데이터베이스를 서버에 두는 것이 좋습니다. 모든 사용자의 데이터를 하나의 mysql 데이터베이스에 보관하는 것이 항상 최선의 생각은 아닙니다. 특히 사용자 데이터가 빠르게 증가 할 경우.


0

CUDR 동기화 문제 라고 부를 수 있습니다 (CRUD가 마음에 들지 않습니다-작성 / 업데이트 / 삭제는 쓰기이며 함께 쌍을 이루어야하기 때문입니다)

이 문제는 쓰기-오프라인 우선 또는 쓰기-온라인 우선 관점에서 볼 수도 있습니다. 쓰기-오프라인 접근 방식에는 고유 식별자 충돌 문제가 있으며 동일한 트랜잭션에 대한 여러 네트워크 호출로 인해 위험 (또는 비용)이 증가합니다.

저는 개인적으로 온라인 쓰기 우선 접근 방식을 관리하기가 더 쉽다는 것을 발견했습니다 (따라서 다른 모든 것이 동기화되는 곳에서 단일 진실 소스가 될 것입니다). 온라인 쓰기 접근 방식은 사용자가 먼저 오프라인으로 쓰기를 허용하지 않도록 요구합니다. 온라인 쓰기에서 확인 응답을 받아 오프라인으로 작성합니다.

그는 먼저 오프라인에서 읽을 수 있으며 네트워크를 사용할 수있게되면 온라인에서 데이터를 가져와 로컬 데이터베이스를 업데이트 한 다음 UI를 업데이트 할 수 있습니다 ....

고유 식별자 충돌을 피하는 한 가지 방법은 고유 한 사용자 ID + 테이블 이름 또는 테이블 ID + 행 ID (sqlite에서 생성)의 조합을 사용하는 것입니다. 그런 다음 동기화 된 부울 플래그 열을 함께 사용합니다. 다른 모든 ID가 생성 될 고유 ID를 얻으려면 먼저 온라인으로 등록해야합니다. 여기에서 시계가 동기화되지 않은 경우에도 문제가 발생합니다.


또한 쓰기 오프라인 접근 방식은 앱 제거에 문제가 있으며 온라인으로 업로드되지 않은 모든 데이터는 삭제됩니다
DragonFire
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.