If you need to map each user to any other user, the O (N ^ 2) algorithm, whatever you do.
If you can use some kind of 1-dimensional "metric", you can try to associate each user with one synthetic value. But this is inconvenient and may be impossible.
But what you can do is to note which users require changes in their profiles (when any of the parameters on which the mapping is based changes). At this point, you can recalculate the table only for these users, working in O (N): if you have 10,000 users and only 10 require recalculation, you need to study 100,000 records instead of 100,000,000.
Other strategies are to run only the main algorithm for records that have a greater chance of comparison: in your example, "the same city." Or when updating records (but this will require saving (user_1, user_2, ranking, last_calculated), just recount these records with a high rating, very old or never calculated. .
UPDATE
The problem also works with O (N ^ 2) disk space.
How to reduce this space? I think I see two approaches. One is not to put some information in the correspondence table at all. The “match” function makes it more tangible, all the more rigid and cool; having ten thousand “good coincidences” would mean that coincidence means very little. Thus, we still need multiple recounts when User1 changes some key data, in case some of User1's no-no elements return to the possible zone. But we would save a smaller click of active matches for each user.
Storage will still grow quadratically, but less abruptly.
Another strategy would be to recount the match, and then we would need to develop some method to quickly select which users are likely to have a good match (thus limiting the number of lines received by the JOIN), and some method to quickly calculate the match; which may entail rewriting the correspondence between User1 and User2 with a very simple function of a subset of DataUser1, DataUser2 (possibly using additional columns).
The task would be to use the capabilities of MySQL and unload some calculations with the MySQL engine.
To do this, you can possibly “map” some data during input (therefore, in O (k)), in spatial information or in lines, and use the Levenshtein distance.
Storage for one user will grow, but it will grow linearly, not quadratically, and MySQL SPATIAL indexes are very efficient.