I have had a lot of success with SQLite, which is (by careful and deliberate design ...) a public-domain SQL system which stores everything in one file and requires no server. The very nice thing about it is that, well, “it’s a [self-describing(!)] random-access file,” and a whale of a lot more. All wrapped-up in a very small, high-performance engine that runs everywhere and with every language (not just Perl). Quite frankly, I don’t “roll my own random-access files” anymore. I can’t justify the effort, nor the bugs that I can avoid.
There is only one caveat, but it is an important one: use transactions. SQLite is also designed for fail-safe situations, so it will re-read everything that it has just written, quite by-design, unless you are using transactions, which allows more-“lazy” writes. But, hey, you get “a self-describing multi-user random-access file that is understandable everywhere,” with transactions! Not bad. Not bad at all.