2016-10-15 3 views
3

Я работаю над простым приложением, написанным в golang, используя tiedot как механизм базы данных NoSQL. Мне нужно сохранить некоторых пользователей в базе данных.Как обеспечить уникальность свойства в записи NoSQL (Golang + tiedot)

type User struct { 
    Login  string 
    PasswordHash string 
    Salt   string 
} 

Конечно два пользователи не могут иметь один и тот же логин, и - как этот двигатель не обеспечивает механизм транзакций - мне интересно, как убедиться, что нет дублированного Войти в базе данных при записи.

Сначала я подумал, что могу просто найти пользователя по имени пользователя перед вставкой, но поскольку база данных будет , то использовать это, безусловно, ненадежно.

Возможно, я могу подождать случайное время, и если в коллекции есть другой пользователь с тем же именем, удалите его, но это тоже не так уж и звучит.

Возможно ли это, или я должен переключиться на механизм базы данных, поддерживающий транзакции?

ответ

1

Я сначала подумал, что я мог бы просто найти пользователя по логину перед установкой, но, как база данных будет использоваться concurently, это не является надежным.

Правильно, это создает состояние гонки. Единственный способ решить это:

  1. Зафиксировать таблицу
  2. Поиск логин
  3. Вставка если Логин не найден
  4. Разблокировать таблицу

Столешницы замки а не масштабируемое решение, поскольку оно создает дорогостоящее узкое место в вашем приложении. Именно поэтому не транзакционные системы хранения, такие как MySQL MyISAM, постепенно прекращаются. Именно поэтому MongoDB должен использовать кластеры для расширения масштабов.

Он может работать, если у вас есть небольшой размер набора данных и небольшое количество параллелизма, поэтому, возможно, он подходит для создания логина на малоиспользуемом веб-сайте. Новые логины, вероятно, создаются не так часто, что им нужно масштабировать так много.

Но пользователи, вступающие в систему или изменяющие пароль, или другие изменения атрибутов учетной записи, чаще повторяются.

Решение этого заключается в том, чтобы сделать эту операцию атомной, чтобы избежать условий гонки. Например, попробуйте вставить и убедитесь, что механизм базы данных проверяет уникальность и отклоняет вставку, если она нарушает это ограничение.

К сожалению, я не вижу никакой документации в tiedot, которая показывает, что она поддерживает уникальное ограничение или уникальность для индексов.

Tiedot составляет 98%, написанный одним разработчиком, в течение примерно 2 лет (май 2013 года - апрель 2015 года). С тех пор очень мало активности (см. https://www.openhub.net/p/tiedot). Я бы рассматривал tiedot как экспериментальный проект, вряд ли расширяющийся в наборе функций.

+0

Да, ты прав. Я уже перешел на back-end SQLite, так как не хотел тратить больше времени на попытку другого решения. Я чувствовал себя слишком уверенно в отношении 1000+ звезд на github, мой плохой ... Спасибо, что нашли время, чтобы ответить. – n00dl3

2

Ниже мое решение. Это не Tiedot, но он использует CQRS и может применяться к различным БД.

У вас также могут быть другие преимущества, такие как кеширование и объемная запись (в случае поддержки БД), чтобы предотвратить запрос БД по каждому запросу.

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

Также, если это веб-приложение, вы можете использовать этот код одновременно сотнями или тысячами запросов. Но если вы используете мьютекс для доступа к карте, вы просто заставляете свой веб-сайт запускать запросы поочередно. –

+0

Не обязательно хранить всю базу данных, может быть только для входа в систему. Также я использую u.RLock(), чтобы свести к минимуму ожидаемое время для чтения запросов. И да, у меня есть тысячи запросов одновременно. –

Смежные вопросы