A multithreaded fork of Redis that is faster
docs.keydb.dev
docs.keydb.dev
Replacing Redis with "something faster" is a bit like removing the doors on a car because "lighter means faster!". It might look good on a racetrack, but it's about as pragmatic as climbing through a window every morning before setting off for work.
I also find it interesting that the BSD license enables this 3rd party company to fork Redis and build closed source commercial software on top of it. One of the trade offs to consider when licensing a project.
As user of commercial software I am fine with it, not so sure if FOSS advocates at large will be so happy when only non-copyleft licenses survive and we are back in the shareware/pd libraries days.
Why do we assume closed-source software vendors contribute nothing back?
Speaking as an employee at a company that produces a closed-source software product that uses open-source libraries, I've contributed plenty back to various libraries, including publishing some of my own.
Libraries with permissive licenses get more users, and more users mean more opportunities for receiving contributions. Something like GPLv2 is really only truly effective at soliciting contributions that it wouldn't have received otherwise if there's no viable alternative.
Or to give another example, Rust is dual-licensed under the Apache License, Version 2.0 and MIT. This permissive license made it really easy for lots of people (including myself) to contribute to it. If it were released instead using the GPL, it would likely be a shadow of the language it is today, if even still alive at all.
when you're contributing to GPL software, you don't need your employer's permission for it to be upstreamed. If they release the code, and don't violate the GPL, then there's nothing the employer can do to stop it from going upstream.
Yes, you do; if you are contributing to it, you need to have exactly that permission. If you are working on a derivative, you need that permission before you can contribute it to anyone else, including upstream.
> If they release the code
Plenty of people work on internal code for their employers, so this is not a given.
Yes you do. Except the permission in this case is "permission to use the library" in the first place, which is a much higher bar than permission to upstream changes to a permissively-licensed library because using a GPL library has much farther-reaching implications than merely contributing changes back.
Like companies staying away from Linux or GCC? It's debatable if these projects would have been as successful using MIT/BSD.
Companies having problems with GPL is a problem of companies and not a problem of the license. If the library you want is GPL, then why blame the project and not your company's legal department?
Thanks!
For the curious: https://engineering.fb.com/data-infrastructure/scribe/
Edit: HN thread: https://news.ycombinator.com/item?id=21181982
Basically, everything that needs logging and post-processing by both real-time systems (e.g. Puma) and batch processing (e.g. all of the data that's ingested and sent to the data warehouse) goes through Scribe.
(disclaimer: I work in Scribe)
Scuba: https://research.fb.com/publications/scuba-diving-into-data-...
Puma: https://research.fb.com/publications/realtime-data-processin...
But it's not like that's through one pipe. We don't talk about how many zillions of tons of steel per minute are moved on freeways.
We picked the job queue framework long before we started moving tens of thousands of jobs a second through it with a later feature, and probably exceeded its design constraints - in that instance we effectively were using Redis + jobs as a pauseable and throttleable write buffer for MySQL.
With major tooling changes we likely could have come up with something a lot more elegant, I'm not longer with that company, but our plan was always to get rid of the need for that system to hit MySQL at all, which would eliminate a lot of our need to control throughput with our Redis buffer. Of course, doing that would have taken a lot more time and effort, and in our case doing "the simplest possible thing that would work reliably and serve our customers" meant that we probably could have used a faster Redis instead of multiplexing requests and workers across multiple Redis instances on the same hardware.
Sometimes an "improved" version of a tool you're already using can be really useful, if you find yourself in a bind and the alternative is major architectural changes.
Also, you didn't mention that, but I've seen this happening often, you should never use redis as a primary data store if your data is important.
I guess I don't know how redis deals with the network, but I'd assume that it handles concurrent requests. If not, then I guess that's the case where you'd see less than 1/n cpu utilization and it could still be CPU bound.
Because redis handles all requests serially you need to be very careful if the redis cluster is shared between different apps. You want to share the cluster among apps with similar usage patterns.
What are the advantages of "multi-threading" other than for performance?
Multithreading IO also reduces the latency hit from disk-persistence and provides more concurrency and throughput, which is a great tradeoff when you don't really need sub-millisecond performance but want the same API across a larger dataset limited by disk instead of RAM space.
> Taking advantage of all your cores.
Uh, is there any reason you'd want to "take advantage of all your cores" other than performance?
The question/proposal/dialog we started from, don't forget, was:
1. Redis is already as performant as I want, it's not even close to being a bottleneck, so I have no need for multi-threading, and this is a _very very common_ case, as redis is performant enough for a lot.
2. There are other advantages of multi-threading than performance. (Ie, other reasons you'd want it despite 1).
You seem to just be going around in circles. If 1, why would you care about "taking advantage of all your cores"?
That means nothing at all, if performance is not your concern.
Basically redis can't necessarily guarantee data safety - which is perfectly fine for it's normal use case as a caching layer and data you don't necessarily care if you lose (for example rate limiting relies on tracking the number of requests/s within say an hour - if you lose the data you don't really care). User sessions too, worst case people have to log in again, meh whatever.
It would be great to remove limitations like RAM-only capacity in exchange for a slight performance hit, and while also gaining better core utilization. We used ScyllaDB (a very fast cassandra clone) in the past for the cpu/disk scalability but always felt Redis offered better APIs. Now it's a real option.
However, “should” you in a world where you only have cloudwatch because you don’t want to roll your own monitoring or bring in a third party vendor? Probably not. It’s not a good thing, but it’s a reality. 1/n approximation is your go to here.
However yeah, 99% of all deployments could get by with SQLite/MySQL/Postgres performance amounts.
Show HN: KeyDB – A Multithreaded Fork of Redis, https://news.ycombinator.com/item?id=19257987
KeyDB: A Multithreaded Redis Fork, https://news.ycombinator.com/item?id=19368955
jdsully spurred the implementation of two of the best changes that you might see in redis in the near future and you consider even using keydb pointless.
Yea, ok.
Could he have gone with a non-blocking approach like libuv?
The multithreaded option is also based on non-blocking IO. You can think of it as one Event Loop per CPU core, rather than only one Event Loop per computer.
A sibling post says that the keydb implementation also lets the multithreaded executor perform parsing. So I guess that is one difference.
Unlike most databases the core data structure is the
fastest part of the system. Most of the query time
comes from parsing the REPL protocol and copying data
to/from the network.
I wonder if anyone in the Redis ecosphere has explored a binary client server protocol, something that could be parsed/compiled on the client and then executed without parsing on the server, if the above is really true seems like that might offer even more perf gain than multithreading on the server.One related choice that Redis makes (or made at the time) is to rely extremely heavily on the malloc implementation, rather than doing work to manage it's memory internally. Even a very trivial, naive free list provided a modest speed-up, for example.
There are a lot of these choices in the code base, largely owing to maintainability concerns (though antirez can surely speak for himself). Given how easy it is for an otherwise uninitiated C programmer such as myself to hack on it, I struggle to disagree with the prioritization. :)
"Unlike most databases the core data structure is the fastest part of the system. Most of the query time comes from parsing the REPL protocol and copying data to/from the network."
I can see cases where a really optimized system could benefit from a binary protocol, but I suspect it'd be a loss for most people.
if (c->argc == 5 && !strcasecmp(c->argv[4]->ptr,"withscores"))
and a quick grep suggests that's a common pattern % grep argv src/*.c | grep -c -e 'str\(case\)*cmp'
482
I guess this means someone would have to tackle creating an intermediate binary format first, rewriting the command handlers to expect that format, and then making client libraries that can produce the format. Perhaps still worth it in the end, but not trivial.[1] https://github.com/antirez/redis/blob/unstable/src/t_zset.c#...
SELECT foo FROM Table WHERE key = @mykey;
Then you bind the parameter to whatever you're interested in.In a webserver-like context it's once per query one way or another - the server process is stateless-ish between page loads, so each page load is either a from-scratch connection or a connection taken from a pool, but even if you're pooling you can't use prepared statements in practice (you can't leave a prepared statement on a connection that you return to the pool because you'll eventually exhaust the database server's memory that way, and you'd have to resubmit the prepared statement every time you took a connection out of the pool anyway because there's no way to know whether this connection has run this page already or not).
If you assume a page that's just displaying one database row, which is not the only use case but a common one, then each page load is one query and that query will have to be parsed for each page load, short of doing something like building a global set of all your application's queries and having your connection-pool logic initialise them for each connection.
I'm somewhat surprised at the mechanism you're describing, but now I read the documentation it does seem to be the case. I wonder if a small piece of middle-ware might be sufficient to replicate the behavior I'm describing on a connection pool, and whether that would be desirable.
https://www.martinfowler.com/articles/lmax.html
I believe other (grown-up/legacy!) exchanges work the same way.
I wonder how much of the direction of concurrency research is driven by the fact that there is much more publishable work to be done in managing concurrency rather than avoiding it!
I am sure it would be possible to provide these guaranties while offering concurrent execution but most likely at the expense of a simple design.
The goal is 100% Redis compatibility so I can’t compromise on atomicity.
It also isn't exactly single threaded. It calls fork when it wants to persist data to disk, which is functionally similar to starting up a thread to do disk IO.
Also, it might now have any benefit for you. Imagine a certain key is particularly hot. Having one multithreaded redis process handling access to it might speed things up. Running multiple sharded redis processes won't, since only one of them will have that key.
It would make a little difference if there was a single hot key, but that's a bit unusual. Typically there's some subset of keys that are hot, and you can get them to hash across instances. People also tend to cache those values on the clients, as banging on redis constantly is a waste.
It's pretty common when using a single Redis stream as a lightweight Kafka.
Because it seems like this guarantees that. It mostly parallelizes the command parsing and networking side. The actual core hash table is guarded by a global lock, so you could still get all those single-threaded guarantees.
Our website is definitely not "high traffic" but we get somewhere around 300,000 requests a day, mostly concentrated around business hours (We're a local clothing wholesaler).
I haven't tested it under production loads, but just swapping our redis for keydb (THANKS DOCKER!) I saw no improvement in my artificial load tests.
I didn't expect to see much real improvement for this use case, but I just thought it was worth mentioning that it isn't necessarily faster for all workloads.
> Another thing to note is that Redis is not Memcached, but, like memcached, is an in-memory system. To make multithreaded an in-memory system like memcached, with a very simple data model, makes a lot of sense. A multi-threaded on-disk store is mandatory. A multi-threaded complex in-memory system is in the middle where things become ugly: Redis clients are not isolated, and data structures are complex. A thread doing LPUSH need to serve other threads doing LPOP. There is less to gain, and a lot of complexity to add.
No offense to the creator(s) and I have a ton of gratitude and respect for them pushing the boundaries. But I also know Antirez is a smart dude and Redis has delivered insane performance thus far with few issues.
Previous discussion about keydb, commented by antirez.
Anecdotal, but it leaves me slightly confused
But, I'd guess that multi-threaded collection classes, due to the logic for locking or other means of concurrency control, would be slower and not faster.
So, any thoughts on why, how multi-threaded could be so much faster, e.g., the OP's 5X, not just for the OP here but in general and maybe general enough to apply to my code?
By the way, with my code I get a weak version of multi-threaded because the software interface to my key-value store is just via standard TCP/IP sockets moving byte arrays from object instance de/serialization. So, I'm taking advantage of the standard TCP/IP FIFO (first in, first out) queue for the incoming work to be done. I.e., more than one Web server can be sending a key-value request to my key-value server at the same time; TCP/IP handles that muli-threading; and I get a weak version of multi-threading. Broadly I'm wondering if having my actual code and the collection classes multi-threaded have any chance of being faster: Okay, the server has 8 cores so that MIGHT be the key to being faster.
Cloudflare stopped using KT because it wasn't much else other than simple and fast, and was missing a lot of other features.
Which is to say, if someone is looking for a "faster Redis", it's probably because they originally went with Redis as the "least software they can get away with" for their particular design needs.
Any, therefore, any software that has more narrow semantics than Redis itself is not, in fact, a viable "faster Redis", for anyone but those who had no reason to be running Redis in the first place.
But I still love Redis.
1. It is one of the most reliable software I used in production.
2. Not everyone wants 'faster than light' software.
3. Simplicity of redis blows my mind. It is very easy to maintain and almost never have a single issue.
4. These days everyone talks about scale but very few projects need FAANG-level scale. Point is there are so many small projects where current redis fits seamlessly. Just making case for Redis.
In many use cases that's not a problem, but I imagine for some it's a dealbreaker.
Look around. Who is not shaming the oil and coal industry right now?
"Please respond to the strongest plausible interpretation of what someone says, not a weaker one that's easier to criticize. Assume good faith."
Lame internet humor tends to grow like kudzu, and the users who post it underestimate how lame it is, as scott_s pointed out well long ago: https://news.ycombinator.com/item?id=7609289. Sarcasm isn't quite the same but it's related.
I do however agree about the kudzu and that it is better to just keep it at zero. The purpose of my two posts here is to better understand the community within which I work, not to argue for HN policy change.
What is the fucking point of a replicated KV store if the connections between nodes aren’t encrypted.