This is a creation in Article, where the information may have evolved or changed.
I spoke last week in Gopher China about our game server using a set of self-developed memory databases that map the database structure from MySQL and support transactions. Perhaps because of the more hurried, and the space of the PPT is limited, some friends of this part still have doubts, come back after more friends asked me this memory database transaction how to achieve, so I take advantage of today Stargate write this article to fine talk about this useless skills.
In the direct use of the database, we have experienced the transaction (do not experience please self-experiment), when a business needs a multi-step data non-query operation, the database transaction mechanism can help us ensure the integrity of the data, do not appear like the bank transfer operation, the money has been deducted but the other party confiscated such situation.
And once we are out of the database to manage the data, the integrity of the data need to maintain their own, the most Earth-based approach is on demand, each business is written on their own error handling and data rollback logic, but this is the most uninsured, people are the most prone to make mistakes, so need to find ways to do a set of transaction mechanisms.
I was just in the design of "Fairy Tao" in the memory of the database in the transaction this card for a long time, this is the biggest difference between the trained and wild way, lack of theoretical knowledge and did not do, so I do not know how to do. But after figuring it out, it's easy to find the principle.
We classify non-query operations, is nothing more than insert, delete, modify, these three categories, we again for each kind of research rollback scheme, the data inserted in the rollback needs to be deleted, the deleted data needs to be inserted when the rollback, the modified data needs to be modified back to the old data in the rollback, in these three cases.
To know the operation type and the data of the operation, you need a list to record the operation in the transaction, so there is a transaction log such a thing.
Each step in the transaction log has a corresponding commit and rollback action, so there is an interface:
type TransLog interface { Commit(*Database) Rollback(*Database)}
Suppose we have a table in our database called Player_item, and the database structure mapping in memory is probably like this:
type Database struct { transLogs []TransLog playerItem map[int]*PlayerItem}type PlayerItem struct { Id int ItemId int Num int}
We recorded the operation type and the old and new data when the table was added and changed, so we had the transaction log.
func (db *Database) InsertPlayerItem(playerItem *PlayerItem) { db.playerItem[playerItem.Id] = playerItem db.transLogs = append(db.transLogs, &PlayerItemTransLog{ Type: INSERT, New: playerItem, })}func (db *Database) DeletePlayerItem(playerItem *PlayerItem) { old := db.playerItem[playerItem.Id] delete(db.playerItem, playerItem.Id) db.transLogs = append(db.transLogs, &PlayerItemTransLog{ Type: DELETE, Old: old, })}func (db *Database) UpdatePlayerItem(playerItem *PlayerItem) { old := db.playerItem[playerItem.Id] db.playerItem[playerItem.Id] = playerItem db.transLogs = append(db.transLogs, &PlayerItemTransLog{ Type: UPDATE, Old: old, New: playerItem, })}
Because the database is just using the transaction log interface, regardless of the implementation of the specific transaction log, we need to implement the transaction log for the Player_item table:
Type Transtype intconst (INSERT transtype = Iota DELETE UPDATE) type playeritemtranslog struct {type Transtyp E old *playeritem New *playeritem}func (TransLog *playeritemtranslog) Commit (db *database) {switch translog.ty PE {case insert:fmt. Printf ("INSERT into Player_item (ID, item_id, num) VALUES (%d,%d,%d) \ n", TransLog.New.Id, Translo G.new.itemid, TransLog.New.Num,) case delete:fmt. Printf ("DELETE player_item where id =%d\n", transLog.Old.Id,) case UPDATE:FMT.P rintf ("UPDATE player_item SET id =%d, item_id =%d, num =%d\n", TransLog.New.Id, TransLog.New.Ite MId, TransLog.New.Num,)}}func (TransLog *playeritemtranslog) Rollback (db *database) {switch Translog.type {Case Insert:delete (Db.playeritem, transLog.New.Id) case Delete:db.playeritem[translog.old.id] = tr Anslog.old Case UPDATE:db.playerIteM[translog.old.id] = Translog.old}}
We wrap up the rollback and commit logic, so the memory database has a transaction mechanism:
func (db *Database) Transaction(trans func()) { defer func() { if err := recover(); err != nil { for i := len(db.transLogs) - 1; i >= 0; i-- { db.transLogs[i].Rollback(db) } panic(err) } else { for _, tl := range db.transLogs { tl.Commit(db) } } db.transLogs = db.transLogs[0:0] }() trans()}
Because the transaction log is sequential, the data for the latter operation may be generated by the previous step, so the rollback requires a reverse order and a rollback from the last step.
Because the format is very fixed, the code is easily generated by the code generator.
The complete code is in: Https://github.com/idada/go-labs/blob/master/labs30/labs30.go
Now you will find that the so-called "Memory Database" and "Memory database transaction" are completely the title party well, there is nothing magical about it, congratulations on a useless skill:)