NoSQL is usually perceived as an alternative to relational databases, however, many of them, especially those that are simpler, can not only replace, but also perfectly complement them. In fact, to use some kind of NoSQL solution instead of the usual database, you need either a new project or the ability to rewrite the old one almost completely. Rare cases in daily development. At the same time, you can easily pick off a lot of low-hanging fruits.
It's about
Redis , because it’s good, it supports all sorts of useful structures and I just like it. I will talk about several options to ease my life with it, used by me in real projects.
Key - valueRadish base, can be used to replace memcached. Cache, sessions, etc. Often, persistence is not out of place: long-running cache, sessions. But it is too obvious and therefore boring, we go further.
')
CountersThe task - there are some entities, for example, posts for which you need to display the number of views. The solution is simple - when viewing the post perform
INCR post:<id>
and we get the number of hits as an answer, in the absence of a key it will be created, the value is increased to 1 and returned, so we do not even need any initialization. And everything works very quickly, because the radish hangs in the memory. We also do not need to worry about saving something somewhere, save radish.
You can use GET to get the value of the counter without adding and MGET to get several at once. The latter is convenient when displaying a list of posts.
TopsLet's slightly complicate the previous example. Suppose, in addition to the number of hits, we need to display the top, list of the most popular posts. In this case, the usual keys are not good enough, we use ordered sets, fortunately, there is also an increment. The previous command changes to:
ZINCRBY post 1 <id>
This team, despite its seeming simplicity, does several things at once. First, it creates an ordered set of post, if it does not exist, and second, it adds an element with a score of 0 to it, if it hasn’t been yet, and third, it increases its account by 1. That is, it does everything necessary to build ordered set of posts with the number of views as accounts.
To get the id of the 10 most popular posts, just run:
ZREVRANGE post 0 9 WITHSCORES
You can drop WITHSCORES if the number of views we do not need.
Let's complicate the task a little more, now we want the old posts to go down over time if they are no longer viewed. It’s easy - we’ll just periodically charge X% of each account (pseudocode on pearl):
my $x = X / 100; my %posts = ZRANGE post 0 -1 WITHSCORES; while (my ($id, $score) = each %posts) { ZINCRBY post -$score*$x $id; }
We put it in crowns once a day, ready. Junk will exponentially decay, making room for a new one.
The list of visitors on the siteIt can be quite troublesome when implemented in traditional ways. With radish - easy. In some way, we define its user id, it can be really an id from the corresponding table, session id or ip + useragent. When hit, save the last call time:
ZADD guys_online <unix_timestamp> <user_id>
Because it's still a set, though ordered, the previous entry with the same id in guys_online will be replaced and only one user_id will remain - the last hit timestamp. To get the number of guys online (in the last 15 minutes):
ZCOUNT guys_online <unix_timestamp-15*60> +inf
To get their list, simply use ZRANGEBYSCORE instead of ZCOUNT. Of course, a lot of guys_online will be gradually hammered, so we will put in crowns
ZREMRANGEBYSCORE guys_online -inf <unix_timestamp-15*60>
Event invalid cacheThe usual way to implement invalidation on an event is to run over all dependent cache keys when an event occurs and erase them. The downside here is excessive dependency - the event handler needs to know about the pile of cache pieces. When caching a new piece, it is necessary to add its invalidation to the event handler, or even to several handlers. Awful, inconvenient, tangled code connectivity.
There is another way. When saving something to cache, add invalidator (s):
SET <cache_key> <data> SADD <event_name1> <cache_key> # cache_key event_name1 SADD <event_name2> <cache_key> # … event_name2
When the event event_name occurs, we erase all dependent cache keys and the invalidator pointing to them:
my @cache_keys = SMEMBERS <event_name>; DEL @cache_keys <event_name>
Disabled events now get rid of unnecessary dependence are determined in the same place where the cache is written. And you can write a general handler for events. By the way, something like this is done only on an industrial scale in
cacheops .
What's next?Then you can read the
article with a similar idea, but with other examples from the creator of the radish. You can adapt the radish to your tasks, but you can pay attention to how easily and naturally many tasks are solved with the help of radish, and pause with a slight shudder at times when you had to push it all into the framework of relational databases.