Redis is single-threaded, so a command that takes a long time blocks every other client for that whole time. KEYS * walks the entire keyspace in one command, which on a few million keys is seconds of total unavailability.
# blocks the server for the duration
redis-cli KEYS 'session:*'
# incremental: returns a cursor, resume until it comes back 0
redis-cli --scan --pattern 'session:*'
SCAN returns a small batch and a cursor, so the work is spread over many short commands and nothing is blocked. The trade is weaker guarantees: keys added or removed mid-scan may or may not appear, and a key can be returned twice — so the consumer has to tolerate duplicates. For a delete sweep that is fine. KEYS remains reasonable on a development machine and nowhere else.