Primary Index VS Unique Index Performance
Indexes with the columns in the order given:

actions: INDEX(user_id_to, user_id_from) -- 'covers' first query
actions: INDEX(user_id_to, counter) -- may subsume ORDER BY and LIMIT in 3nd query


Either index will be useful for 2nd query.  DROP KEY user_id_to (user_id_to) as redundant when you add my two suggestions.

For various reasons, it may be better to change to PRIMARY KEY(user_id_to, user_id_from, action_type).  This would


replace the first index suggested above
mostly eliminate the need for the second index above, thereby speeding up inserts (by having fewer indexes)
If you need user_id_from in the WHERE, but without user_id_to, then you would need some index starting with user_id_from.  (I don't yet see a SELECT that begs for such.)


In summary (for actions indexes):

PRIMARY KEY(user_id_to, user_id_from, action_type)
INDEX(user_id_from, ...) -- if needed.


"action_type" -- Do you really need 4 bytes for it?  How many distinct values are there?  If very few, consider an ENUM, otherwise a TINYINT UNSIGNED or SMALLINT UNSIGNED (1,1,2 bytes, respectively).  Shrinking 4 bytes to 1 would save 8GB (3 bytes * 2 copies (data+secondary key) * 1.3B rows).

There is a perfectly nice 2-byte countryCode; don't use a separate Id:

countryCode CHAR(2) CHARACTER SET ascii


How many "locations" are there?  If not millions, I would not bother normalizing city, state, country.  If there are a lot of locations rows in each city, then consider normalizing only city, then have the state and country spelled out in the city table.  (I oppose "over-normalizing".)

If users has PRIMARY KEY(user_id), I have no further suggestion for indexing that table.

The 3rd query says LEFT JOIN users and users.something....  You probably did not want LEFT.