I'm no MySQL expert, but it looks like MySQL primary keys are clustered -- you'll want to make sure that's the case with your primary keys; clustered indexes will definitely help speed things up.
One thing, though -- I don't believe you can have two "primary" keys on any table; your urls table looks rather suspect to me for that reason. Above all, you should make absolutely sure those two columns in the urls table are indexed to the hilt -- a single numeric index on each one should be fine -- because you're joining on them, so the DBMS needs to know how to find them quickly; that could be what's going on in your case. If you're full-table-scanning that many rows, then yes, you could be sitting there for quite some time while the server tries to find everything you asked for.
I'd also suggest removing that CONCAT function from the select statement, and seeing how that affects your results. I'd be amazed if that weren't a contributing factor somehow. Just retrieve both columns and handle the concatenation afterward, and see how that goes.
Lastly, have you figured out where the bottleneck is? Just joining on three several-million-row tables shouldn't take much time at all (I'd expect maybe a second or so, just eyeballing your tables and query), provided the tables are properly indexed. But if you're pushing those rows over a slow or already-pegged NIC, to a memory-starved app server, etc., the slowness could have nothing to do with your query at all, but instead with what happens after the query. Seven million rows is quite a bit of data to be assembling and moving around, regardless of how long the finding of those rows happens to take. Try selecting just one row instead, rather than all seven million, and see how that looks by contrast. If that's fast, then the problem isn't the query, it's the result set.