2016-10-15 6 views
3

私はgolangで書かれた単純なアプリケーションで、NoSQLデータベースエンジンとしてtiedotを使用しています。 データベースにユーザーを保存する必要があります。このエンジンは、任意のトランザクションメカニズムを提供していないよう - - 私が書いデータベースには重複ログインはありませんことを保証するために、どのように思ったんだけどNoSQLレコード(Golang + tiedot)でプロパティの一意性を保証する方法

type User struct { 
    Login  string 
    PasswordHash string 
    Salt   string 
} 

もちろん2人のユーザが同じログインを持ち、することはできません。

私は最初に、ログインする前にログインするだけでよいと思っていましたが、データベースは がそのまま使用されているため信頼できません。

多分私はランダムな時間を待つことができ、コレクションに同じログインを持つ別のユーザーがいる場合は削除しても、信頼できるとは言えません。

これも可能ですか、トランザクションをサポートするデータベースエンジンに切り替える必要がありますか?

答えて

1

私が最初に私がちょうど挿入する前にログインして、ユーザーを検索できると思ったが、データベースがconcurently使用されるように、それが信頼できるものではありません。

右、競争条件を作成します。これを解決する唯一の方法は次のとおりです。

  1. は、ログイン
  2. 用テーブル
  3. 検索をロックログインが
  4. 見つからない場合は挿入し

表ロックがあるテーブルのロックを解除アプリケーションに高価なボトルネックが発生するため、スケーラブルなソリューションではありません。 MySQLのMyISAMのような非トランザクション型ストレージエンジンが廃止される理由です。 MongoDBがスケールアップするためにクラスタを使用しなければならない理由です。

データセットのサイズが小さく、並行処理が少ないため、軽度に使用されているWebサイトでのログイン作成に適しています。新しいログインはおそらくあまり頻繁には作成されないため、大規模に拡張する必要があります。

ただし、ログインしたユーザーやパスワードの変更など、アカウント属性の変更は頻繁に発生します。

解決策は競合状態を避けるためにこの操作をアトミックにすることです。たとえば、挿入を試行し、データベースエンジンに一意性を確認させ、その制約に違反する場合は挿入を拒否させます。

残念ながら、私は、それが固有の制約またはインデックスの一意の強制をサポートしていることを示す文書をtiedotに表示していません。

Tiedotは、約2年間(2013年5月〜2015年4月)の単一開発者によって98%書かれています。それ以来、ほとんど活動していません(https://www.openhub.net/p/tiedot参照)。私はtiedotを実験的なプロジェクトとみなし、機能セットを拡張することはまずありません。

+0

ええ、そうです。私は既に別の解決策を試すために時間を無駄にしたくないので、すでにSQLiteのバックエンドに切り替えました。私はgithub上の1000人以上の星について、あまりにも自信を持って感じました。私の悪い...答えに時間をとっていただきありがとうございます。 – n00dl3

2

以下は私の解決策です。 Tiedot固有ではありませんが、CQRSを使用しており、さまざまなDBに適用できます。

キャッシュや一括書き込み(DBがサポートしている場合)など、リクエストごとにDBを要求しないようにするなど、他の利点もあります。

package main 

import (
    "sync" 
    "log" 
    "errors" 
) 

type User struct { 
    Login  string 
    PasswordHash string 
    Salt   string 
} 

type MutexedUser struct { 
    sync.RWMutex 
    Map map[string]User 
} 

var u = &MutexedUser{} 

func main() { 
    var user User 

    u.Sync() 
    // Get new user here 
    //... 
    if err := u.Insert(user); err != nil { 
     // Ask to provide new login 
     //... 
     log.Println(err) 
    } 
} 

func (u *MutexedUser) Insert(user User) (err error) { 
    u.Lock() 
    if _, ok := u.Map[user.Login]; !ok { 
     u.Map[user.Login] = user 
     // Add user to DB 
     //... 
     u.Unlock() 
     return err 
    } 
    u.Unlock() 
    return errors.New("duplicated login") 
} 

func (u *MutexedUser) Read(login string) User { 
    u.RLock() 
    value := u.Map[login] 
    u.RUnlock() 

    return value 
} 

func (u *MutexedUser) Sync() (err error) { 
    var users []User 

    u.Lock() 
    defer u.Unlock() 
    // Read users from DB 
    //... 
    u.Map = make(map[string]User) 
    for _, user := range users { 
     u.Map[user.Login] = user 
    } 
    return err 
} 
+0

文字通り、データベース全体のテーブルをマップとしてメモリに読み込みますか?テーブルが大きすぎてアプリケーションメモリに収まらない場合はどうなりますか? –

+0

また、これがWebアプリケーションの場合、数百から数千のリクエストを同時に実行するこのコードを持つことができます。しかし、マップへのアクセスをミューテックスすると、あなたのウェブサイトが要求を連続して実行させただけです。 –

+0

必ずしもすべてのデータベースを保持する必要はありませんが、ログインのみ可能です。また、私はu.RLock()を使用してリクエストを読み込むための待ち時間を最小限に抑えています。そして、はい、私は同時に何千もの要求を持っています。 –

関連する問題