I use lmdb pretty extensively. It is single-file (well, it has a lock file) key/value store, can be read-from/written-to by multiple processes, and embeds nicely. It has Python bindings, but I've only used it from C.
It's worth noting that sqlite's typing is very flexible, so you probably want to add a `check (data is null or json_valid(data) = 1)` to ensure the data is _actually_ valid json.
You can then index based on json queries, eg `create index some_index on something((json_extract(data, '$.someField')))` and then do an indexed query, eg `select * from something where json_extract(data, '$.someField') = 'blah'`.
In our experience it's all working quite well :-)
(Groking json_each and json_tree took a few goes, but now is working well (eg we join onto json_each to allow a row "labels" containing a list of label (rather than a one-to-many table join for something_labels)).)
It supports querying, see json_extract function.
https://www.sqlite.org/json1.html
I use it a lot to return JSON from normalized data, for apps to consume.
My 2 cents: Best to keep data normalized and in separate columns. If you gotta keep data in jsonb, keep only the data that you don't need to query on. Anything you need to query, you better put it as a column. You'll thank me later.