我正在研究一个用golang编写的简单应用程序,使用tiedot作为NoSQL数据库引擎。 我需要将一些用户存储在数据库中。
type User struct {
Login string
PasswordHash string
Salt string
}
当然,两个用户不能拥有相同的登录名,并且 - 由于此引擎不提供任何事务机制 - 我想知道如何确保在编写时数据库中没有重复登录。
我首先想到的是,我可以在插入之前通过登录搜索用户,但是数据库将是 使用得很清楚,它不可靠。
也许我可以等待一个随机时间,如果该集合中有另一个用户具有相同的登录名,则将其删除,但这听起来也不可靠。
这是否可行,或者我应该切换到支持事务的数据库引擎吗?
答案 0 :(得分: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
}
答案 1 :(得分:1)
我首先想到的是,我可以在插入之前通过登录搜索用户,但由于数据库将被使用,因此它不可靠。
对,它会造成竞争条件。解决此问题的唯一方法是:
表锁不是可扩展的解决方案,因为它会在您的应用程序中造成昂贵的瓶颈。这就是为什么像MySQL的MyISAM这样的非事务性存储引擎正在逐步淘汰的原因。这就是为什么MongoDB必须使用集群来扩展的原因。
如果您拥有较小的数据集大小和较少的并发性,它可以工作,因此它可能足以在一个使用不当的网站上创建登录。新登录可能不会经常创建,因此需要进行大规模扩展。
但是,用户登录,密码更改或帐户属性的其他更改确实会更频繁地发生。
解决方法是使此操作成为原子,以避免竞争条件。例如,尝试插入并使数据库引擎验证唯一性,如果违反该约束,则拒绝插入。
不幸的是,我没有在tiedot中看到任何文档,表明它支持索引的唯一约束或唯一性强制执行。
Tiedot 98%由一位开发人员撰写,期限约为2年(2013年5月 - 2015年4月)。此后活动非常少(见https://www.openhub.net/p/tiedot)。我认为tiedot是一个实验项目,不太可能扩展功能集。