I think you're operating under a false premise (namely, that the total draw call count is the ultimate arbiter of performance).
This isn't the case. Certainly, reducing draw calls is a good idea, but sacrificing everything else on that alter is going to lead you down a path where you eventually hurt performance instead.
For example, you're ignoring the costs of doing everything else with all of this extra geometry that, for the most part, probably isn't visible in the world. You may be paying a huge cost for overdraw or over-transformation of geometry, for example.
Chunking up the world is a classical divide-and-conquer strategy: reduce the instantaneous operable problem space by divvying up the total problem space into smaller parts that can be handled on their own (or most importantly, ignored on their own).
I think you will eventually simply hit a wall where you cannot solve performance issues if you remain adamant that you use only a single draw. That said, there are similar divide-and-conquer approaches you can try.
If it's possible that only certain subsets of the vertex attributes change when you need to reupload (for example, if it's only the texture coordinates that change to indicate that a block is now gone, or a new type of block, et cetera), you can put the dynamic aspects of that vertex data in its own vertex attribute stream. That way you have a static vertex buffer which never changes, and a dynamic one that uploads every frame. The size of the dynamic buffer will be drastically smaller (for example, around 60MB or so if all it contains are 2D texture coordinates). You can have multiple stream sources per draw call, so this still lets you use one draw call.
If your actual GPU workload is small (that is, your shaders are super simple), you can potentially offload some of the world handling dynamic aspects of the world to the GPU itself, possibly storing any input you need for that handling in a texture that you update every frame. Similar to the multiple vertex stream source option above, the idea here is to confine the data you upload every frame to only data that is potentially changing every frame and leave the static stuff alone, so you only need to upload it once.
Similar, it sounds like your world is essentially heightmap based (that's what I get from your statement that "the whole world is represented by 2d flat tiles;" thus there are no caves or anything like that. It's reasonable in such an implementation to represent each tile as a single vertex you expand to four in the shader pipeline, for example in a geometry shader. It's potentially possible to do this in a fully-3D (caves allowed) world as well, though harder. This can reduce the total data set (indices and vertices) significantly as well.
You can try to update only a portion of the vertex buffer based on where the player actually is; if you can know that the player won't be able to see certain parts of the world that are very far away, you can use a buffer that doesn't discard completely when mapped to the CPU and write only a portion. This may be a performance win, although it may not. You'd have to profile and see.