{"id":15322113,"url":"https://github.com/tompston/time-series-db-benchmark","last_synced_at":"2026-02-25T22:31:17.372Z","repository":{"id":255454869,"uuid":"824442454","full_name":"tompston/time-series-db-benchmark","owner":"tompston","description":"Benchmarking mysql, mongodb, postgre, postgres + timescale \u0026 duckdb","archived":false,"fork":false,"pushed_at":"2025-09-11T11:39:57.000Z","size":181,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-11T14:06:18.574Z","etag":null,"topics":["benchmark","duckdb","golang","mysql","postgresql","time-series","timescaledb"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tompston.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-07-05T06:33:27.000Z","updated_at":"2025-09-11T11:45:53.000Z","dependencies_parsed_at":"2025-04-14T23:34:05.730Z","dependency_job_id":"1d964e52-c047-4aec-93d4-678169ff9dbf","html_url":"https://github.com/tompston/time-series-db-benchmark","commit_stats":{"total_commits":27,"total_committers":2,"mean_commits":13.5,"dds":0.03703703703703709,"last_synced_commit":"3854b8306428425d2fc61298b09598fd0b4da1c9"},"previous_names":["tompston/time-series-db-benchmark"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tompston/time-series-db-benchmark","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tompston%2Ftime-series-db-benchmark","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tompston%2Ftime-series-db-benchmark/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tompston%2Ftime-series-db-benchmark/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tompston%2Ftime-series-db-benchmark/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tompston","download_url":"https://codeload.github.com/tompston/time-series-db-benchmark/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tompston%2Ftime-series-db-benchmark/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29843392,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-25T21:18:31.832Z","status":"ssl_error","status_checked_at":"2026-02-25T21:18:29.265Z","response_time":61,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["benchmark","duckdb","golang","mysql","postgresql","time-series","timescaledb"],"created_at":"2024-10-01T09:14:15.000Z","updated_at":"2026-02-25T22:31:17.362Z","avatar_url":"https://github.com/tompston.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Time series database benchmarking\n\nThe following project runs benchmarks for the following databases:\n\n- mysql\n- mongodb\n- postgresql\n- postgresql with timescale extension (version 2.16.1)\n\nEach database instance is ran through docker and uses the non-default ports to avoid conflicts with local database instances.\n\n**NOTE.** Create an issue if you see a mistake or have a suggestion.\n\nBenchmarked methods:\n\n- insert x rows (aka upserts on empty table, with indexes)\n- upsert single row at a time\n- upsert a bulk of rows\n- read x rows with a limit of y and sort descending by start_time.\n\nThe data format for all of the tables is the same (excluding the id field between mongodb and postgres implementations, and the name of the interval field in mysql).\n\n```json\n{\n  \"created_at\": \"2021-09-01T00:00:00Z\", // TIMESTAMPTZ | mongodb ISODate\n  \"updated_at\": \"2021-09-01T00:00:00Z\", // TIMESTAMPTZ | mongodb ISODate\n  \"start_time\": \"2021-09-01T00:00:00Z\", // TIMESTAMPTZ | mongodb ISODate\n  \"interval\": TIME_IN_MILLISECONDS,     // BIGINT (renamed to resolution in mysql)\n  \"area\": \"area\",                       // TEXT\n  \"source\": \"source\",                   // TEXT\n  \"value\": 0.0,                         // double precision\n}\n```\n\nUpserts are done using an `start_time`, `interval` and `area` filter.\n\nThe same data chunks get inserted into all of the databases.\n\nAs the benchmark results between timescale and postgres did not match the results of the official timescale [blogpost](https://www.timescale.com/blog/postgresql-timescaledb-1000x-faster-queries-90-data-compression-and-much-more/), I asked for help / validation on [reddit](https://www.reddit.com/r/PostgreSQL/comments/1ftnlu3/native_postgresql_version_faster_than_timescaledb/).\n\n## Commands\n\n```bash\n# start docker in the background\nsudo docker compose up -d\n# run the go benchmarks\ncd go\ngo test -benchmem -run=^$ -bench ^BenchmarkTimeseries$ timeseries-benchmark -v -count=1 -timeout=0\n\n# reset docker (uninstall every image and container)\nsudo docker stop $(sudo docker ps -aq)\nsudo docker rm $(sudo docker ps -aq)\nsudo docker rmi $(sudo docker images -q)\nsudo docker rmi -f $(sudo docker images -q)\nsudo docker volume rm $(docker volume ls -q)\n```\n\n## Results\n\n```bash\ngo test -benchmem -run=^$ -bench ^BenchmarkTimeseries$ timeseries-benchmark -v -count=1 -timeout=0\ngoos: darwin\ngoarch: arm64\npkg: timeseries-benchmark\ncpu: Apple M1 Pro\nBenchmarkTimeseries\n    benchmark_test.go:59: Populating 200000 rows into each database\n    benchmark_test.go:70:       1.7348 ms/insert, total time: 346.95 sec, mysql\n    benchmark_test.go:70:       0.5204 ms/insert, total time: 104.08 sec, mongodb\n    benchmark_test.go:70:       0.3988 ms/insert, total time: 79.76 sec, pg-ntv\n    benchmark_test.go:70:       0.5309 ms/insert, total time: 106.18 sec, pg-tsc\n    benchmark_test.go:70:       0.3721 ms/insert, total time: 74.42 sec, duckdb\n\nBenchmarkTimeseries/mysql-upsert-single-4000-rows-10                   1        1895951333 ns/op         4322152 B/op      76007 allocs/op\nBenchmarkTimeseries/mongodb-upsert-single-4000-rows-10                 1        1681902583 ns/op        31308792 B/op     416045 allocs/op\nBenchmarkTimeseries/pg-ntv-upsert-single-4000-rows-10                  1        1614828208 ns/op         1600000 B/op      52000 allocs/op\nBenchmarkTimeseries/pg-tsc-upsert-single-4000-rows-10                  1        2032403750 ns/op         1600000 B/op      52000 allocs/op\nBenchmarkTimeseries/duckdb-upsert-single-4000-rows-10                  1        1514375458 ns/op         3335672 B/op     104052 allocs/op\n\nBenchmarkTimeseries/mysql-upsert-bulk-4000-rows-10                     2         890039334 ns/op         2529900 B/op      56023 allocs/op\nBenchmarkTimeseries/mongodb-upsert-bulk-4000-rows-10                   5         206790992 ns/op        16489673 B/op     176111 allocs/op\nBenchmarkTimeseries/pg-ntv-upsert-bulk-4000-rows-10                   19          57280428 ns/op         5768684 B/op      52042 allocs/op\nBenchmarkTimeseries/pg-tsc-upsert-bulk-4000-rows-10                    4         256532646 ns/op         5768684 B/op      52042 allocs/op\nBenchmarkTimeseries/duckdb-upsert-bulk-4000-rows-10                    2         628354416 ns/op         3208692 B/op      92101 allocs/op\n\n    benchmark_test.go:102:  * storage size for pg-tsc, 200000 rows, before compression: 46560\n\nBenchmarkTimeseries/mysql-get-4000-10                                368           3303898 ns/op         3030120 B/op      40048 allocs/op\nBenchmarkTimeseries/mongodb-get-4000-10                               85          13928981 ns/op         5003023 B/op      80194 allocs/op\nBenchmarkTimeseries/pg-ntv-get-4000-10                               402           3032235 ns/op         2933465 B/op      16030 allocs/op\nBenchmarkTimeseries/pg-tsc-get-4000-10                               242           5082635 ns/op         2933477 B/op      16030 allocs/op\nBenchmarkTimeseries/duckdb-get-4000-10                                54          19347856 ns/op         3192555 B/op      56076 allocs/op\n\n    benchmark_test.go:125: sleeping for 30 sec to get the correct mongodb collection storage size\n    benchmark_test.go:128:  * storage size for 200000 rows\n    benchmark_test.go:137:      - mysql: 18496 KB\n    benchmark_test.go:137:      - mongodb: 17860 KB\n    benchmark_test.go:137:      - pg-ntv: 33288 KB\n    benchmark_test.go:137:      - pg-tsc: 16760 KB\n    benchmark_test.go:143:  * read speed after populating 200000 rows (avg over 10 runs)\n    benchmark_test.go:159:      - 0.0007 ms/row -\u003e mysql\n    benchmark_test.go:159:      - 0.0035 ms/row -\u003e mongodb\n    benchmark_test.go:159:      - 0.0007 ms/row -\u003e pg-ntv\n    benchmark_test.go:159:      - 0.0012 ms/row -\u003e pg-tsc\n    benchmark_test.go:159:      - 0.0048 ms/row -\u003e duckdb\n    benchmark_test.go:162:  * upsert speed after populating 200000 rows (avg over 10 runs)\n    benchmark_test.go:178:      - 0.2191 ms/row - mysql\n    benchmark_test.go:178:      - 0.0515 ms/row - mongodb\n    benchmark_test.go:178:      - 0.0150 ms/row - pg-ntv\n    benchmark_test.go:178:      - 0.0889 ms/row - pg-tsc\n    benchmark_test.go:178:      - 0.1600 ms/row - duckdb\n\n```\n\n- in terms of inserts (upserts on initial run), mysql was the slowest and native postgres was the fastest. Timescale took ~1.6x longer than the native postgres.\n- for single upserts, mongodb was the fastest and timescale was the slowest.\n- for bulk upserts, the native postgresql is the fastest, while the timescale version was ~8 times slower than the native postgresql version.\n- for read speeds, the native postgresql version ~1.5x faster than the timescale version while running from go.\n  - for some reason the EXPLAIN ANALYZE queries show that the native postgresql reads take roughly 2.5ms, while\n    - compressed version takes ~70ms (28x slower)\n    - decompressed version takes ~20ms (8x slower)\n- for table sizes, the timescale version can have a smaller size than the native postgresql version, but the efficiency of the compression is vastly dependent on the chunk size.\n\nNotes:\n\n- the default value of chunck compression in timescale is changed to one which gives better compression\n- mongodb does not use the time series collections because they can't be queried by a single row at a time\n- the empty benchmark lines are omitted.\n- The mysql version uses a field called `resolution` instead of `interval` because `interval` is a reserved keyword.\n\n### EXPLAIN ANALYZE queries\n\nTo get specific info about how long the queries took on the database level, i ran the `read_test.sh` script post benchmarking. The results between native postgres and timescale are not great. Native postgres `SELECT *` queries otperform timescale by at least 2x. All of the logs of the explain queries can be inspected in the file.\n\n```bash\n* postgres select with limit\n Planning Time: 0.323 ms\n Execution Time: 1.932 ms\n(4 rows)\n\n\n ~ timescaledb version\n default_version | installed_version\n-----------------+-------------------\n 2.16.1          | 2.16.1\n(1 row)\n\n* timescaledb compressed -\u003e select with limit\n Planning Time: 63.068 ms\n Execution Time: 7.791 ms\n(293 rows)\n\n* timescale decompressed -\u003e select with limit\n Planning Time: 16.949 ms\n Execution Time: 3.828 ms\n(75 rows)\n```\n\n### Debug commands\n\n```bash\n# find the version of timescaledb\ndocker exec timeseries_timescaledb psql -U test -d timeseries_benchmark -c \"SELECT default_version, installed_version FROM pg_available_extensions where name = 'timescaledb';\"\n\n# see size of table in timescale + timing of query\ndocker exec timeseries_timescaledb psql -U test -d timeseries_benchmark -c \"SELECT pg_size_pretty(hypertable_size('data_objects')) AS total_size;\"\ndocker exec timeseries_timescaledb psql -U test -d timeseries_benchmark -c \"\\d data_objects\"\n\n# see size of table in postgres + timing of query\ndocker exec timeseries_postgres psql -U test -d timeseries_benchmark -c \"SELECT pg_size_pretty(pg_total_relation_size('data_objects')) AS total_size;\"\ndocker exec timeseries_postgres psql -U test -d timeseries_benchmark -c \"\\d data_objects\"\n\ndocker exec timeseries_postgres psql -U test -d timeseries_benchmark -c 'SELECT *\n  FROM timescaledb_information.dimensions\n  WHERE hypertable_name = 'metrics';'\n```\n\nSELECT default_version, installed_version FROM pg_available_extensions where name = 'timescaledb';\n\n### Gotchas\n\n- mongodb\n  - The statistics about the mongodb collection seem to be incorrect just after inserting the data. The `totalSize` value updates after some time, once the records are inserted. This is why there is a pause before reading the collection size.\n  - **The displayed storage size may not be correct.** While running the benchmarks, i found that in some cases the displayed storage of the mongodb collection did not increase when the number of records increased by 10x. So i don't think the displayed storage size can be trusted fully.\n- timescale\n  - the size of the chunk matters. From my understanding the default is 7 days. In this benchmark we save 1 hour resolution data, for which `30 days` otperforms compression of `7 days` with a big margin.\n    - 7 days -\u003e 4864 kb\n    - 30 days -\u003e 1576 kb\n    - 60 days -\u003e 1064 kb\n  - To get the table size of the timescaledb, the default `SELECT pg_size_pretty(pg_total_relation_size($1)) AS total_size;` query does not return the correct. I found this out when the table size returned from this query did not change once i benchmarked the size on varying number of rows (1k -\u003e 10k).\n  - The compression of timescale does not get applied immediately after the inserts. That's why we need to trigger it manually.\n  - possible cause for concern (not sure if this is fixed) [compress_chunk() blocks other queries on the table for a long time](https://github.com/timescale/timescaledb/issues/2732)\n  - adjustments based on interval blog post[link](https://mail-dpant.medium.com/my-experience-with-timescaledb-compression-68405425827)\n- mysql\n  - Use `DATETIME` instead of `TIMESTAMP` because `TIMESTAMP` has a range of `1970-2038` and `DATETIME` has a range of `1000-9999` (Error 1292 (22007): Incorrect datetime value: '2038-01-19 04:00:00' for column 'start_time' at row 1).\n\n\u003c!--\n\n# see chunk info and compression status\nSELECT chunk_schema, chunk_name, compression_status,\n        pg_size_pretty(before_compression_total_bytes) AS size_total_before,\n        pg_size_pretty(after_compression_total_bytes) AS size_total_after\n    FROM chunk_compression_stats('public.data_objects')\n    ORDER BY chunk_name;\n\n# get the total compression\nSELECT\n    pg_size_pretty(before_compression_total_bytes) as before,\n    pg_size_pretty(after_compression_total_bytes) as after\n FROM hypertable_compression_stats('public.data_objects');\n\n\ndocker exec timeseries_postgres psql -U test -d timeseries_benchmark -c 'SELECT\n    pg_size_pretty(before_compression_total_bytes) as before,\n    pg_size_pretty(after_compression_total_bytes) as after\n FROM hypertable_compression_stats('public.data_objects');'\n\n --\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftompston%2Ftime-series-db-benchmark","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftompston%2Ftime-series-db-benchmark","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftompston%2Ftime-series-db-benchmark/lists"}