Environment variables are not just for user preferences. They're a generic mechanism for communicating a variety of setting information from a parent process to child processes it starts.
There are numerous cases where a process will set specific environment variables in order to influence just processes it starts. For example, a script may deliberately reset the locale settings for commands it starts, such that it can parse output from them. The build scripts for many large software packages use nested invocations of make
that coordinate with each other through environment variables. Specialized tools may need to change the working conditions of other programs they start by doing tricks with $LD_PRELOAD or $PATH.
If something a user does in a different window while a long compilation is running in another would just magically change the environment variables of all his processes behind their backs, madness and chaos would result.
Other environment variables contain information about the particular session a process is started in. Programs expect $TERM to describe the command set of the particular terminal (or terminal emulator) they are connected to; making that a general per-user setting would make it impossible to be logged into the same system with several different kinds of terminals. Even if you only have one piece of terminal hardware and never log in remotely, programs such as screen
depend on setting a different $TERM for the processes that run inside their session.
A better question would be, why do we use a process-to-subprocess communication mechanism for user preference settings, rather than a per-user database?
Answer: Because it works well enough and the benefits of making a per-user database are not large enough that the work of changing everything to use that instead of environment variables would be done.
(I can think of very few preference settings where there wouldn't be some use cases where it's convenient to change them just for executing a single script, for example. So in order not to lose functionality, everything would still need to be overridable by environment variables, which results in added complexity and more confused users).
It's not as if alternatives don't exist. For example, X resources are per display session rather than per process. But they are difficult to access for command-line programs -- and command-line programs usually need to work for remote logins that don't even have an X server to connect to.