The expectation should be reduce in database io, not getting rid of it entirely.
Overall you could issue tokens with short lifespan, say 5 minutes. And having the client refreshing tokens periodically. Reading database every 5 min is certainly an improvement over reading it on every API call. You could also only check revocation table if the performed operation is security-critical or sensitive enough. I wouldn't care if the user is trying to perform a read-only operation on some public data with a revoked token that is going to expire in a few minutes anyway.
>3. No way to revoke all of one user's tokens because there is no list of those tokens
I don't see why one would not maintain such a list, since generally issuing a token requires database io anyway. Storing a token when issued is just one additional write op.