mirror of
https://github.com/dragonflydb/dragonfly.git
synced 2025-05-10 18:05:44 +02:00
docs(readme): Fix up grammar and consistency (#982)
* README.md language edit Signed-off-by: Gareth Dwyer <sixhobbits@users.noreply.github.com> --------- Signed-off-by: worktheclock <85885287+worktheclock@users.noreply.github.com> Signed-off-by: Gareth Dwyer <sixhobbits@users.noreply.github.com> Co-authored-by: worktheclock <85885287+worktheclock@users.noreply.github.com>
This commit is contained in:
parent
c271e13176
commit
b8b84c0749
1 changed files with 69 additions and 82 deletions
151
README.md
151
README.md
|
@ -13,24 +13,26 @@ Other languages: [简体中文](README.zh-CN.md)
|
|||
|
||||
## The world's fastest in-memory data store
|
||||
|
||||
Dragonfly is an in-memory data store built for modern application workloads. It is fully compatible with the Redis and Memcached APIs, required no code changes to adopt. When compared to these legacy in-memory datastores, Dragonfly delivers 25X more throughput, higher cache hit rates, with lower tail latency, and effortless vertical scalability.
|
||||
Dragonfly is an in-memory data store built for modern application workloads.
|
||||
|
||||
Fully compatible with Redis and Memcached APIs, Dragonfly requires no code changes to adopt. Compared to legacy in-memory datastores, Dragonfly delivers 25X more throughput, higher cache hit rates with lower tail latency, and effortless vertical scalability.
|
||||
|
||||
## Contents
|
||||
|
||||
- [Benchmarks](#benchmarks)
|
||||
- [Quick Start](https://github.com/dragonflydb/dragonfly/tree/main/docs/quick-start)
|
||||
- [Quick start](https://github.com/dragonflydb/dragonfly/tree/main/docs/quick-start)
|
||||
- [Configuration](#configuration)
|
||||
- [Roadmap and Status](#roadmap-status)
|
||||
- [Design Decisions](#design-decisions)
|
||||
- [Roadmap and status](#roadmap-status)
|
||||
- [Design decisions](#design-decisions)
|
||||
- [Background](#background)
|
||||
|
||||
## <a name="benchmarks"><a/>Benchmarks
|
||||
|
||||
<img src="http://static.dragonflydb.io/repo-assets/aws-throughput.svg" width="80%" border="0"/>
|
||||
|
||||
Dragonfly is crossing 3.8M QPS on c6gn.16xlarge reaching x25 increase in throughput compared to Redis.
|
||||
In benchmarks, Dragonfly showed a 25X increase in throughput compared to Redis, crossing 3.8M QPS on c6gn.16xlarge.
|
||||
|
||||
99th latency percentile of Dragonfly at its peak throughput:
|
||||
Dragonfly's 99th percentile latency metrics at its peak throughput:
|
||||
|
||||
| op | r6g | c6gn | c7g |
|
||||
|-------|-------|-------|-------|
|
||||
|
@ -38,20 +40,20 @@ Dragonfly is crossing 3.8M QPS on c6gn.16xlarge reaching x25 increase in through
|
|||
| get | 0.9ms | 0.9ms | 0.8ms |
|
||||
| setex | 0.9ms | 1.1ms | 1.3ms |
|
||||
|
||||
*All benchmarks were performed using `memtier_benchmark` (see below) with number of threads tuned per server type and the instance type. `memtier` was running on a separate c6gn.16xlarge machine. For setex benchmark we used expiry-range of 500, so it would survive the end of the test.*
|
||||
*All benchmarks were performed using `memtier_benchmark` (see below) with number of threads tuned per server and instance type. `memtier` was run on a separate c6gn.16xlarge machine. We set the expiry time to 500 for the SETEX benchmark to ensure it would survive the end of the test.*
|
||||
|
||||
```bash
|
||||
memtier_benchmark --ratio ... -t <threads> -c 30 -n 200000 --distinct-client-seed -d 256 \
|
||||
--expiry-range=...
|
||||
```
|
||||
|
||||
When running in pipeline mode `--pipeline=30`, Dragonfly reaches **10M qps** for SET and **15M qps** for GET operations.
|
||||
In pipeline mode `--pipeline=30`, Dragonfly reaches **10M QPS** for SET and **15M QPS** for GET operations.
|
||||
|
||||
### Memcached / Dragonfly
|
||||
### Dragonfly vs. Memcached
|
||||
|
||||
We compared memcached with Dragonfly on `c6gn.16xlarge` instance on AWS.
|
||||
As you can see below Dragonfly dominates memcached for both write and read workloads
|
||||
in terms of throughput with a comparable latency. For write workloads, Dragonfly has also better latency, due to contention on the [write path in memcached](docs/memcached_benchmark.md).
|
||||
We compared Dragonfly with Memcached on a c6gn.16xlarge instance on AWS.
|
||||
|
||||
With a comparable latency, Dragonfly throughput outperformed Memcached throughput in both write and read workloads. Dragonfly demonstrated better latency in write workloads due to contention on the [write path in Memcached](docs/memcached_benchmark.md).
|
||||
|
||||
#### SET benchmark
|
||||
|
||||
|
@ -72,120 +74,105 @@ Memcached exhibited lower latency for the read benchmark, but also lower through
|
|||
|
||||
### Memory efficiency
|
||||
|
||||
In the following test, we filled Dragonfly and Redis with ~5GB of data
|
||||
using `debug populate 5000000 key 1024` command. Then we started sending the update traffic with `memtier` and kicked off the snapshotting with the
|
||||
"bgsave" command. The following figure demonstrates clearly how both servers behave in terms of memory efficiency.
|
||||
To test memory efficiency, we filled Dragonfly and Redis with ~5GB of data using the `debug populate 5000000 key 1024` command, sent update traffic with `memtier`, and kicked off the snapshotting with the `bgsave` command.
|
||||
|
||||
This figure demonstrates how each server behaved in terms of memory efficiency.
|
||||
|
||||
<img src="http://static.dragonflydb.io/repo-assets/bgsave-memusage.svg" width="70%" border="0"/>
|
||||
|
||||
Dragonfly was 30% more memory efficient than Redis in the idle state.
|
||||
It also did not show any visible memory increase during the snapshot phase.
|
||||
Meanwhile, Redis reached almost x3 memory increase at peak compared to Dragonfly.
|
||||
Dragonfly also finished the snapshot much faster, just a few seconds after it started.
|
||||
For more info about memory efficiency in Dragonfly see [dashtable doc](/docs/dashtable.md)
|
||||
Dragonfly was 30% more memory efficient than Redis in the idle state and did not show any visible increase in memory use during the snapshot phase. At peak, Redis memory use increased to almost 3X that of Dragonfly.
|
||||
|
||||
Dragonfly finished the snapshot faster, within a few seconds.
|
||||
|
||||
For more info about memory efficiency in Dragonfly, see our [Dashtable doc](/docs/dashtable.md).
|
||||
|
||||
|
||||
|
||||
## <a name="configuration"><a/>Configuration
|
||||
Dragonfly supports common Redis arguments where applicable.
|
||||
For example, you can run: `dragonfly --requirepass=foo --bind localhost`.
|
||||
|
||||
Dragonfly supports common Redis arguments where applicable. For example, you can run: `dragonfly --requirepass=foo --bind localhost`.
|
||||
|
||||
Dragonfly currently supports the following Redis-specific arguments:
|
||||
* `port` redis connection port, default: 6379
|
||||
* `bind` localhost to only allow locahost connections, Public IP ADDRESS , to allow connections **to that ip** address (aka from outside too)
|
||||
* `requirepass` password for AUTH authentication, default: ""
|
||||
* `maxmemory` Limit on maximum-memory (in human-readble bytes) that is used by the database. 0 - means the program will automatically determine its maximum memory usage. default: 0
|
||||
* `dir` - by default, dragonfly docker uses `/data` folder for snapshotting. the CLI uses: ""
|
||||
You can use `-v` docker option to map it to your host folder.
|
||||
* `dbfilename` the filename to save/load the DB. default: "dump";
|
||||
* `port`: Redis connection port (`default: 6379`).
|
||||
* `bind`: Use `localhost` to only allow localhost connections or a public IP address to allow connections **to that IP** address (i.e. from outside too).
|
||||
* `requirepass`: The password for AUTH authentication (`default: ""`).
|
||||
* `maxmemory`: Limit on maximum memory (in human-readable bytes) used by the database (`default: 0`). A `maxmemory` value of `0` means the program will automatically determine its maximum memory usage.
|
||||
* `dir`: Dragonfly Docker uses the `/data` folder for snapshotting by default, the CLI uses `""`. You can use the `-v` Docker option to map it to your host folder.
|
||||
* `dbfilename`: The filename to save and load the database (`default: dump`).
|
||||
|
||||
In addition, it has Dragonfly specific arguments options:
|
||||
* `memcache_port` - to enable memcached compatible API on this port. Disabled by default.
|
||||
* `keys_output_limit` - maximum number of returned keys in `keys` command. Default is 8192.
|
||||
`keys` is a dangerous command. We truncate its result to avoid blowup in memory when fetching too many keys.
|
||||
* `dbnum` - maximum number of supported databases for `select`.
|
||||
* `cache_mode` - see [Cache](#novel-cache-design) section below.
|
||||
* `hz` - key expiry evaluation frequency. Default is 100. Lower frequency uses less cpu when
|
||||
idle at the expense of slower eviction rate.
|
||||
* `save_schedule` - glob spec for the UTC time to save a snapshot which matches HH:MM (24h time). default: ""
|
||||
* `primary_port_http_enabled` - If true allows accessing http console on main TCP port, default: true
|
||||
* `admin_port` - If set, would enable admin access to console on the assigned port. This supports both HTTP and RESP protocols. default disabled
|
||||
* `admin_bind` - If set, the admin consol TCP connection would be bind the given address. This supports both HTTP and RESP protocols. default any
|
||||
* `cluster_mode` - cluster mode supported. Currently supports only `emulated`. default: ""
|
||||
* `cluster_announce_ip` - ip that cluster commands announce to the client.
|
||||
There are also some Dragonfly-specific arguments:
|
||||
* `memcache_port`: The port to enable Memcached-compatible API on (`default: disabled`).
|
||||
* `keys_output_limit`: Maximum number of returned keys in `keys` command (`default: 8192`). Note that `keys` is a dangerous command. We truncate its result to avoid a blowup in memory use when fetching too many keys.
|
||||
* `dbnum`: Maximum number of supported databases for `select`.
|
||||
* `cache_mode`: See the [novel cache design](#novel-cache-design) section below.
|
||||
* `hz`: Key expiry evaluation frequency (`default: 100`). Lower frequency uses less CPU when idle at the expense of a slower eviction rate.
|
||||
* `save_schedule`: Glob spec for the UTC to save a snapshot in HH:MM (24h time) format (`default: ""`).
|
||||
* `primary_port_http_enabled`: Allows accessing HTTP console on main TCP port if `true` (`default: true`).
|
||||
* `admin_port`: To enable admin access to the console on the assigned port (`default: disabled`). Supports both HTTP and RESP protocols.
|
||||
* `admin_bind`: To bind the admin console TCP connection to a given address (`default: any`). Supports both HTTP and RESP protocols.
|
||||
* `cluster_mode`: Cluster mode supported (`default: ""`). Currently supports only `emulated`.
|
||||
* `cluster_announce_ip`: The IP that cluster commands announce to the client.
|
||||
|
||||
### Example Start Script, with popular options:
|
||||
### Example start script with popular options:
|
||||
|
||||
```bash
|
||||
./dragonfly-x86_64 --logtostderr --requirepass=youshallnotpass --cache_mode=true -dbnum 1 --bind localhost --port 6379 --save_schedule "*:30" --maxmemory=12gb --keys_output_limit=12288 --dbfilename dump.rdb
|
||||
```
|
||||
|
||||
for more options like logs management or tls support, run `dragonfly --help`.
|
||||
For more options like logs management or TLS support, run `dragonfly --help`.
|
||||
|
||||
## <a name="roadmap-status"><a/>Roadmap and status
|
||||
|
||||
Currently, Dragonfly supports ~185 Redis commands and all memcache commands besides `cas`.
|
||||
We are almost on par with Redis 5 API. Our next milestone will be to stabilize basic
|
||||
functionality and implement the replication API.
|
||||
If you see that a command you need, is not implemented yet, please open an issue.
|
||||
Dragonfly currently supports ~185 Redis commands and all Memcache commands besides `cas`. Almost on par with the Redis 5 API, Dragonfly's next milestone will be to stabilize basic functionality and implement the replication API. If there is a command you need that is not implemented yet, please open an issue.
|
||||
|
||||
For dragonfly-native replication, we are designing a distributed log format that will support order of magnitude higher speeds.
|
||||
For Dragonfly-native replication, we are designing a distributed log format that will support order-of-magnitude higher speeds.
|
||||
|
||||
After the replication feature we will continue with other Redis missing commands from
|
||||
APIs 3-6.
|
||||
Following the replication feature, we will continue adding missing commands for Redis versions 3-6 APIs.
|
||||
|
||||
Please see [The Command Reference](https://dragonflydb.io/docs/category/command-reference) for the current commands supported by Dragonfly.
|
||||
Please see our [Command Reference](https://dragonflydb.io/docs/category/command-reference) for the current commands supported by Dragonfly.
|
||||
|
||||
## <a name="design-decisions"><a/> Design decisions
|
||||
|
||||
### Novel cache design
|
||||
Dragonfly has a single unified adaptive caching algorithm that is very simple and memory efficient.
|
||||
You can enable caching mode by passing `--cache_mode=true` flag. Once this mode
|
||||
is on, Dragonfly will evict items least likely to be stumbled upon in the future but only when
|
||||
it is near maxmemory limit.
|
||||
|
||||
Dragonfly has a single, unified, adaptive caching algorithm that is simple and memory efficient.
|
||||
|
||||
You can enable caching mode by passing the `--cache_mode=true` flag. Once this mode is on, Dragonfly will evict items least likely to be stumbled upon in the future but only when it is near the `maxmemory` limit.
|
||||
|
||||
### Expiration deadlines with relative accuracy
|
||||
Expiration ranges are limited to ~4 years. Moreover, expiration deadlines
|
||||
with millisecond precision (PEXPIRE/PSETEX etc) will be rounded to closest second
|
||||
**for deadlines greater than 134217727ms (approximately 37 hours)**.
|
||||
Such rounding has less than 0.001% error which I hope is acceptable for large ranges.
|
||||
If it breaks your use-cases - talk to me or open an issue and explain your case.
|
||||
|
||||
Expiration ranges are limited to ~4 years.
|
||||
|
||||
Expiration deadlines with millisecond precision (PEXPIRE, PSETEX, etc.) are rounded to the closest second **for deadlines greater than 134217727ms (approximately 37 hours)**, which has less than 0.001% error and should be acceptable for large ranges. If this is not suitable for your use case, get in touch or open an issue explaining your case.
|
||||
|
||||
For more detailed differences between this and Redis implementations [see here](docs/differences.md).
|
||||
For more detailed differences between Dragonfly expiration deadlines and Redis implementations, [see here](docs/differences.md).
|
||||
|
||||
### Native Http console and Prometheus compatible metrics
|
||||
By default, Dragonfly allows http access via its main TCP port (6379). That's right, you
|
||||
can connect to Dragonfly via Redis protocol and via HTTP protocol - the server recognizes
|
||||
the protocol automatically during the connection initiation. Go ahead and try it with your browser.
|
||||
Right now it does not have much info but in the future, we are planning to add there useful
|
||||
debugging and management info. If you go to `:6379/metrics` url you will see some prometheus
|
||||
compatible metrics.
|
||||
### Native HTTP console and Prometheus-compatible metrics
|
||||
|
||||
By default, Dragonfly allows HTTP access via its main TCP port (6379). That's right, you can connect to Dragonfly via Redis protocol and via HTTP protocol — the server recognizes the protocol automatically during the connection initiation. Go ahead and try it with your browser. HTTP access currently does not have much info but will include useful debugging and management info in the future.
|
||||
|
||||
Go to the URL `:6379/metrics` to view Prometheus-compatible metrics.
|
||||
|
||||
The Prometheus exported metrics are compatible with the Grafana dashboard [see here](tools/local/monitoring/grafana/provisioning/dashboards/dashboard.json).
|
||||
The Prometheus exported metrics are compatible with the Grafana dashboard, [see here](tools/local/monitoring/grafana/provisioning/dashboards/dashboard.json).
|
||||
|
||||
|
||||
Important! The http console is meant to be accessed within a safe network.
|
||||
If you expose Dragonfly's TCP port externally, it is advised to disable the console
|
||||
with `--http_admin_console=false` or `--nohttp_admin_console`.
|
||||
Important! The HTTP console is meant to be accessed within a safe network. If you expose Dragonfly's TCP port externally, we advise you disable the console with `--http_admin_console=false` or `--nohttp_admin_console`.
|
||||
|
||||
|
||||
## <a name="background"><a/>Background
|
||||
|
||||
Dragonfly started as an experiment to see how an in-memory datastore could look like if it was designed in 2022. Based on lessons learned from our experience as users of memory stores and as engineers who worked for cloud companies, we knew that we need to preserve two key properties for Dragonfly: a) to provide atomicity guarantees for all its operations, and b) to guarantee low, sub-millisecond latency over very high throughput.
|
||||
Dragonfly started as an experiment to see how an in-memory datastore could look if it was designed in 2022. Based on lessons learned from our experience as users of memory stores and engineers who worked for cloud companies, we knew that we need to preserve two key properties for Dragonfly: Atomicity guarantees for all operations and low, sub-millisecond latency over very high throughput.
|
||||
|
||||
Our first challenge was how to fully utilize CPU, memory, and i/o resources using servers that are available today in public clouds. To solve this, we used [shared-nothing architecture](https://en.wikipedia.org/wiki/Shared-nothing_architecture), which allows us to partition the keyspace of the memory store between threads so that each thread would manage its own slice of dictionary data. We call these slices - shards. The library that powers thread and I/O management for shared-nothing architecture is open-sourced [here](https://github.com/romange/helio).
|
||||
Our first challenge was how to fully utilize CPU, memory, and I/O resources using servers that are available today in public clouds. To solve this, we use [shared-nothing architecture](https://en.wikipedia.org/wiki/Shared-nothing_architecture), which allows us to partition the keyspace of the memory store between threads so that each thread can manage its own slice of dictionary data. We call these slices "shards". The library that powers thread and I/O management for shared-nothing architecture is open-sourced [here](https://github.com/romange/helio).
|
||||
|
||||
To provide atomicity guarantees for multi-key operations, we used the advancements from recent academic research. We chose the paper ["VLL: a lock manager redesign for main memory database systems”](https://www.cs.umd.edu/~abadi/papers/vldbj-vll.pdf) to develop the transactional framework for Dragonfly. The choice of shared-nothing architecture and VLL allowed us to compose atomic multi-key operations without using mutexes or spinlocks. This was a major milestone for our PoC and its performance stood out from other commercial and open-source solutions.
|
||||
To provide atomicity guarantees for multi-key operations, we use the advancements from recent academic research. We chose the paper ["VLL: a lock manager redesign for main memory database systems”](https://www.cs.umd.edu/~abadi/papers/vldbj-vll.pdf) to develop the transactional framework for Dragonfly. The choice of shared-nothing architecture and VLL allowed us to compose atomic multi-key operations without using mutexes or spinlocks. This was a major milestone for our PoC and its performance stood out from other commercial and open-source solutions.
|
||||
|
||||
Our second challenge was to engineer more efficient data structures for the new store. To achieve this goal, we based our core hashtable structure on paper ["Dash: Scalable Hashing on Persistent Memory"](https://arxiv.org/pdf/2003.07302.pdf). The paper itself is centered around the persistent memory domain and is not directly related to main-memory stores.
|
||||
Nevertheless, it's very much applicable to our problem. It suggested a hashtable design that allowed us to maintain two special properties that are present in the Redis dictionary: a) its incremental hashing ability during datastore growth b) its ability to traverse the dictionary under changes using a stateless scan operation. Besides these 2 properties,
|
||||
Dash is much more efficient in CPU and memory. By leveraging Dash's design, we were able to innovate further with the following features:
|
||||
Our second challenge was to engineer more efficient data structures for the new store. To achieve this goal, we based our core hashtable structure on the paper ["Dash: Scalable Hashing on Persistent Memory"](https://arxiv.org/pdf/2003.07302.pdf). The paper itself is centered around the persistent memory domain and is not directly related to main-memory stores, but it's still most applicable to our problem. The hashtable design suggested in the paper allowed us to maintain two special properties that are present in the Redis dictionary: The incremental hashing ability during datastore growth the ability to traverse the dictionary under changes using a stateless scan operation. In addition to these two properties, Dash is more efficient in CPU and memory use. By leveraging Dash's design, we were able to innovate further with the following features:
|
||||
* Efficient record expiry for TTL records.
|
||||
* A novel cache eviction algorithm that achieves higher hit rates than other caching strategies like LRU and LFU with **zero memory overhead**.
|
||||
* A novel **fork-less** snapshotting algorithm.
|
||||
|
||||
After we built the foundation for Dragonfly and [we were happy with its performance](#benchmarks),
|
||||
we went on to implement the Redis and Memcached functionality. By now, we have implemented ~185 Redis commands (roughly equivalent to Redis 5.0 API) and 13 Memcached commands.
|
||||
Once we had built the foundation for Dragonfly and [we were happy with its performance](#benchmarks), we went on to implement the Redis and Memcached functionality. We have to date implemented ~185 Redis commands (roughly equivalent to Redis 5.0 API) and 13 Memcached commands.
|
||||
|
||||
And finally, <br>
|
||||
<em>Our mission is to build a well-designed, ultra-fast, cost-efficient in-memory datastore for cloud workloads that takes advantage of the latest hardware advancements. We intend to address the pain points of current solutions while preserving their product APIs and propositions.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue