LiteDB – Embedded NoSQL for .NET

.NET Framework 환경에서

  • Embedded
  • NoSQL
  • Single file

로 하면 이것 말고 쓸 게 없네요.

http://www.litedb.org/

SQLite와 비교하면 파일의 용량이 거의 2배가 되긴 하지만요…

순수 C#으로 만들어져 모바일 환경에서도 쓸 수 있습니다.
모습은 음… MongoDB 를 보는 듯한…

RDBMS에서 랜덤 샘플링을 위해 ORDER BY RAND() 이런 것은 금지!!!

RDBMS에서 랜덤하게 데이터를 추출하기 위해
ORDER BY RAND()를 한다면…
바로 망하는 길입니다.
물론 데이터의 수가 적을 때에는 문제가 되지 않을 수도 있습니다.
하지만 레코드가 100만개라면?


1000만개의 데이터에 각각 RAND() 값을 정하게 되어
RAND() 1000만번을 호출하게 되고 이를 임시 공간에 들고 있어야 되며,
1000만개의 값을 정렬해야 되니… 쿼리 하나 때문에 장시간 DB가 멈추겠죠?


그럼 어떻게 랜덤하게 데이터를 추출해야 하는가?


두가지 정도가 될 거 같습니다. (MySQL 기준)


우선은 랜덤한 데이터를 1개씩 n회 추출하는 방법은


1. 전체 데이터 개수를 가져온다. (cnt)
2. 프로그램에서 [0, cnt)의 정수를 랜덤으로 정한다. (skip_cnt)
3. SELECT some_fields FROM some_tabe LIMIT skip_cnt, 1
4. 2, 3번을 n회 반복한다. (필요에 따라 skip_count가 중복되지 않게 처리)

조금 번거롭게 느껴질 수도 있습니다만 제일 확실한 방법이 아닐까요?
물론 some_table은 PK가 있어야 성능이 나오겠죠?
인덱스가 없으면 skip_cnt를 구하기나 LIMIT를 사용할 때에 좀 애매해 보입니다.


두번째 방법 범위를 이용해서 한번에 n개를 뽑는다


이 방법을 사용하려면 범위를 정하기 위한 인덱싱이 되어 있는 정수 타입의 필드가 있어야 합니다.


1. SELECT MIN(soem_indexed_field), MAX(soem_indexed_field) FROM some_table (min_val, max_val)
2. 프로그램에서 min_val, max_val 사이에서 n x m개의 랜덤 리스트를 만든다. (random_list)
3. SELECT some_fields FROM some_table WHERE soem_indexed_field IN random_list LIMIT n

m이 등장하는데 이유는 some_indexed_field의 값들에서 비어있는 값이 있을 경우를 대비해서 입니다.
데이터가 1, 2, 3, 4, 5, 6, … 과 같이 빈 값이 없다면 m == 1일 겁니다.
하지만 데이터가 중간 중간 값이 비어 있어서
1, 5, 7, 123, … 이런 식이라면? ㅠ.ㅠ m이 적절한 값이 되어야 합니다.
문제는 m 값을 정하는 것이 쉽지 않다는 것.
게다가 some_indexed_field 값이 단순 증가 ID 필드가 아닐 경우에는 상당히 곤란해 집니다.


적절히 작지도 크지도 않은 m 값을 가지고 두번째 방법을 통해 데이터를 추출하고
그렇게 나온 데이터가 n개가 되도록 루프…. 이게 가장 현실적이 아닐까요?

MongoDB 샤딩된 컬렉션 풀기?

아래와 같은 방법으로 하면…

database = 'rnr_sessions'
collection = database + '.sessions'
 
sh.stopBalancer()
primary = db.databases.findOne({_id: database}).primary
 
// move all chunks to primary
db.chunks.find({ns: collection, shard: {$ne: primary}}).forEach(function(chunk){
    print('moving chunk from', chunk.shard, 'to', primary, '::', tojson(chunk.min), '-->', tojson(chunk.max));
    sh.moveChunk(collection, chunk.min, primary);
});
 
// unshard
db.collections.remove({ "_id" : collection })
db.chunks.remove({ ns : collection })
 
// flush all mongos, is this neccessary?!
use admin
db.runCommand({ flushRouterConfig: 1 })

참고자료) [Command to unshard a collection]

MongoDB 샤딩을 사용하는 환경에서의 제약 사항들

매번 찾아보지만 도무지 외워지질 않는 이 녀석들!!!

group 사용 불가능 -> mapReduce 또는 aggregate 사용하는 것으로 변경해야 함

db.eval() 사용 불가능

$where 함수에서 DB 오브젝트 참조 불가능

$isolated 를 update 관련에서 사용 불가능

$snapshot 쿼리 불가능

geoSearch 명령 불가능

covered query 불가능

기타 등등 많군요.

참고 자료) [MongoDB Limits ans Thresholds]

MongoDB C++ Client Library for Windows (VS2012)

역시 Open Source로 배포되는 소스들은 Windows에서 빌드하기가 너무 힘드네요… 문서대로 해도 절대 안되네요…

[Build with Visual Studio 2010]

이걸 보고 했는데 안됩니다.

[Download and Compile C++ Driver]

이거 보고 했는데 안됩니다.

그래서 위 페이지의 “Download and Compile C++ Driver Before Version 2.5.3” 링크를 클릭해서 다른 소스를 받았습니다.
우분투에 설치한 서버가 2.4.8 이기 때문에 문제가 없어보이거든요…

하지만 문서대로 해도 안되네요… ㅠ.ㅠ

그래서 결국은 해당 2.4 마지막 안정버전 클라이언트 소스를 가지고 직접 VC 프로젝트를 만들어 보았습니다.

[다운로드 페이지] 에서 [2.4-latest] 를 받았습니다.

그리고 새로운 Win32 프로젝트를 만들고 boost 경로(BOOST_INCLUDE 환경 변수로 사용 중)를 설정하고
이런 저런 수정을 해서 mongodbclient.lib 파일을 생성하는데 성공했습니다.
그리고 초간단 접속 테스트만 진행한 상태입니다.
추후 테스트 하다가 오류나면… 그냥 안쓸래요… 아니면 누가 빌드해주면 쓰던가 ㅋ

cfile30.uf.2777BA3E52CCC76C0E079B.7z

용량 문제로 .lib 파일은 포함하지 못했습니다. VS2012에서 열어서 빌드하시면 될 거에요…

WITH (TABLOCKX)로 프로시저 내에서 테이블 LOCK 하기

struct TableA
{
	int FieldA;

	CRITICAL_SECTION cs;
};

void func(TableA& ta)
{
	EnterCriticalSection(&ta.cs);
	if (ta.fieldA == 0)
		ta.fieldA = 1;
	else
		ta.fieldB = 0;
	LeaveCriticalSection(&ta.cs);
}

위와 같은 C++ 코드와 동일한 MS SQL Server의 프로시저를 만든다면???
CPU를 최대한 사용해서
UPDATE 이후에 @@ROWCOUNT를 가지고 결과를 보고
원하는대로 되지 않으면 다시 루프… 이런 spin lock과 유사한 방법도 있겠지만…
역시 Lock을 하는 것이 깔끔 하겠죠?

CREATE PROCEDURE [func]
AS
BEGIN
	SET NOCOUNT ON;
	DECLARE @curval INT;
	BEGIN TRANSACTION;
	SELECT @curval = [FieldA] FROM [TableA] WITH (TABLOCKX);
	IF @curval = 0 UPDATE [TableA] SET [FieldA]=1;
	ELSE UPDATE [TableA] SET [FieldA]=0;
	COMMIT TRANSACTION;
END

위에 코드는 단순 샘플로 테이블에 데이터가 1개 존재한다는 것을 가정하고 있습니다.
SELECT나 UPDATE문의 WITH 절에 TABLOCKX 을 가지고 테이블을 LOCK 하게 되면 다른 트랜잭션에서 수정할 수 없으므로
이어지는 IF 문에서 안전하게 현재 값을 가지고 처리를 할 수 있습니다.

또다른 LOCK 형식이나 다른 Table Hints는 아래 사이트에서 참조

[Table Hints (Transact-SQL) MSDN 라이브러리 문서]

데이터베이스 보안 주체는 데이터베이스의 스키마를 소유하며 삭제할 수 없습니다. 오류: 15138

MS SQL Server에서
보안 > 로그인 > 특정 계정 의 속성창에서
사용자 매핑에 가면 해당 계정이 사용할 수 있는 DB가 주욱 나오는데
그것들 중에 일부를 체크 박스를 해제하였더니
데이터베이스 보안 주체는 데이터베이스의 스키마를 소유하며 삭제할 수 없습니다. 오류: 15138
이렇게 출력됩니다.

그래서 검색해보니 아래와 같은 방법으로 일단 조치를 할 수 있더군요.

삭제하고자 하는 계정명: BSTest
대상 DB: BSTestDB

USE [BSTestDB];
SELECT [name] FROM [sys].[schemas] WHERE [principal_id]=USER_ID('BSTest');

이렇게 하면 스키마 이름이 주욱 나오는데 그걸 아래에 맞게 잘 넣으면 됩니다.

ALTER AUTHORIZATION ON SCHEMA::SomeName TO dbo;

예를 들어 처음 SELECT로 나온 스키마 이름이 ‘db_owner”였다면

ALTER AUTHORIZATION ON SCHEMA::db_owner TO dbo;

이렇게 되겠죠?

참고 포스트: Pinal Dave