To ensure that the data is correct, it is important to recognize that when multiple clients are working on the same data simultaneously, careless operations can easily lead to data errors. This article will show you how to use Redis transactions to prevent data errors and, in some cases, to improve performance. Redis transactions are not the same as those of traditional relational databases. In a relational database, the user sends BEGIN to the database server, then performs consistent write and read operations. Finally, the user can choose to send COMMIT to confirm previous changes, or ROLLBACK to discard those changes. There are also simple methods in Redis to handle a sequence of read and write operations that are consistent with each other. Redis transactions begin with the special command MULTT, followed by multiple commands passed in by the user, and end with EXEC. But because this simple transaction does not perform any actual operations until the EXEC command is invoked, the user will not be able to make decisions based on the data read. This problem seemed to be insignificant, but actually cannot read data in a consistent format will lead to a certain types of problems become difficult to solve, in addition to this, because in multiple transactions usually need to use when dealing with the same object at the same time second order submission (two – phase commit), so if the transaction can’t read the data in the form of a consistent, The second-order commit will fail, causing some transactions that could have executed successfully to fail. For example, “buying an item in the market” is one of the problems that can become difficult to solve because the data cannot be read in a consistent form, and this problem will be described in the real world.

Deferment of transactions helps performance because Redis delays execution of enqueued commands during transactions until the client sends EXEC commands. Therefore, many Redis clients, including the Python client used in this article, wait until all the commands contained in the transaction are present before sending the MULTT command, the set of commands to be executed in the transaction, and the EXEC command all at once to Redis. Then wait until you receive a response to all commands. This practice of “sending multiple commands at once and waiting for all responses to appear” is often referred to as pielining. It improves Redis performance when executing multiple commands by reducing the number of network communications between clients and Redis servers.

For a while, a friend’s gaming company (Fick Games) found that its role-playing web games on SoulToYou, a fictional social network, were becoming increasingly popular. So Fick Games, a company that cares about the needs of its players, decided to add a marketplace where players could sell and buy goods. The rest of the article will describe how to design and implement the marketplace, and how to extend the marketplace as needed.

1 Define user information and user packages

User information and user package examples. Frank had $43 and was going to sell one of his things

The figure above shows the structure used to represent user information and inventory in the game: the user information is stored in a hash whose key-value pairs record the user’s name, the amount of money the user has, and so on. User packages are represented by a collection that records the unique number of each item within the package.

Backpack inside to achieve other sort.

In order to store all the information of the goods to be sold in the market, we will splice the ID of the goods with the ID of the seller, and store the result of splicing as a member in the market ZSET, while the price of the goods is used as the score of the member. By including all the data together, we greatly simplify the data structure needed to implement a market for buying and selling goods, and because everything in the market is sorted by price, paging and lookup for goods can be easily implemented. The following figure shows an example of a market with only a few items.

A basic goods buying and selling market, where user 4 is selling goods IteamA for 35

Now that we know the data structure needed to implement a marketplace for buying and selling goods, it’s time to consider how to implement a marketplace for listing goods.

Put the goods on the market for sale

In order to place an item on the market, programs need to use WATCH commands in addition to MULTI and EXEC commands, and sometimes even UNWATCH or DISCARD commands. After the user uses the WATCH command to monitor the key until the user executes the EXEC command, if any other client replaces, updates or deletes any monitored key before the user executes the EXEC command, then when the user tries to execute the EXEC command, The transaction will fail and an error will be returned (the user can then choose to retry the transaction or abandon it). Using commands such as WATCH, MULTI/XEC, and UNWATCH/DISCARD, programs can avoid data errors by ensuring that the data they are using does not change when performing important operations.

What is this? The UNWATCH command can reset the connection after the WATCH command but before the MULTI command. Similarly, the DISCARD command can reset the connection after the MULTI command but before the EXEC command. This means that after using WATCH to monitor one or more keys, then using MULTI to start a new transaction and enqueue multiple commands to the transaction queue, the user can still cancel the WATCH command and empty all enqueued commands by sending the DISCARD command. None of the examples presented in this article use discards, mainly because we already know if we want to execute MULTI/EXEC or UNWATCH, so there is no need to use discards in these examples.

In put a product in the market for sale, program will need to be added to the record market sales of the goods is an ordered set of goods sold, and in the process of adding operation, monitoring package to ensure that the seller is selling goods indeed exist in a package of the seller, the following code that a specific implementation of the operation.

The 1ist_item () function behaves just as we described earlier: it performs some initialization steps and then monitors the seller’s package to verify that the item the seller wants to sell is still in the seller’s package. If so, the function adds the sold item to the market. And remove the item from the seller’s package. As shown in the while loop in the function, if the package is updated or modified during monitoring with the WATCH command, the program receives an error and retries.

Here is how the 1ist_item () function executes when Frank (user ID 17) tries to sell ItemM for $97.

List_item (conn,”ItemM”,17,97

Because the program will ensure that users can only sell their own goods, so in the general case, the user can successfully add you want to sell the goods to the above commodity trading markets, but as said before, if the user package after WATCH execution until the EXEC perform before this period of time has changed, The add operation will fail and retry.

Now that you know how to put something in the market, let’s look at how to buy something from the market.

3 Buying goods

The purchase_item () function in the code above shows how to purchase an item from the market: The program uses wATCH to monitor the market and the buyer’s personal information, then fetches the amount of money the buyer has, the price of the item, and checks whether the buyer has enough money to purchase the item. If the buyer does not have enough money, the program cancels the transaction; Conversely, if the buyer has enough money, the program first transfers the buyer’s payment to the seller, then moves the sold item to the buyer’s package and removes the item from the market. When WatchError occurs due to changes in the buyer’s personal information or in the market, the program will retry, with a maximum retry time of 10 seconds.

, commodity purchase operation in the implementation of the program in addition to need to spend a lot of time to prepare relevant data, also need to commodity trading market and the buyers personal information monitor: monitor commodity trading markets is to ensure that buyers want to buy goods is still available (or be prompted) walking in the commodity has been others to buy, The purpose of monitoring buyers’ personal information is to verify that they have enough money to buy the goods they want. When the program confirms that the item still exists and the buyer has enough money, it moves the purchased item to the buyer’s package and transfers the buyer’s payment to the seller. After observing the items on display in the market, Bill (user ID 27) decides to purchase the ItemM Frank is selling in the market. The following 2 figures show how the data structure changes during the purchase operation.

If market zSET or Bill’s personal information changes between WATCH and EXEC executions, as indicated in the code at the beginning of the previous purchase, purchase_item () will be retried or waived after the retry operation times out.

Why does Redis not implement the typical locking function? When data is accessed FOR write purposes (SELECT FOR UPDATE in SQL), the relational database locks the accessed rows until the transaction is committed (CoMIT) or rolled back (ROLLBACK). If another client tries to write to the locked row, the client will be blocked until the first transaction completes. Locking works very well in practice and is implemented in almost all relational databases, but the downside is that the slower the client that holds the lock runs, the longer the client waiting to unlock is blocked. Because locking may cause a long wait, Redis does not lock data when executing the WATCH command in order to minimize the wait time of the client. Instead, Redis only notifies the client executing the WATCH command if the data has already been modified by another client, a practice known as optistie locking. However, actual locking operations performed by relational databases are called pessmistic locking. Optimistic locks are also very effective in practice, because clients never have to spend time waiting for the first client to acquire the lock — they just need to retry if their transaction fails.

This article showed you how to use a combination of WATCH, MULTI, and EXEC commands to manipulate multiple types of data to achieve a market for buying and selling goods in a game. In addition to the existing buying and selling functions, we could add features such as auctions and limited-time sales, support for more different types of ordering, and more advanced search and filtering capabilities based on the techniques of subsequent articles.

When multiple clients are working on the same data at the same time, using transactions correctly can effectively prevent data errors. The next article from Bugs Bunny will show us how to perform operations faster without worrying about data being modified by other clients.