There's no need for a bash loop calling awk multiple times, just loop within 1 call to awk, e.g. with GNU awk which handles the potential "too many open files" problem for you automatically:
awk '{
    for (i=1; i<=21; i++) {
        print $(i+2) > ("col"i".csv")
    }
}' z.csv
and with any awk:
awk '{
    for (i=1; i<=21; i++) {
        close(out)
        out = "col"i".csv"
        print $(i+2) >> out
    }
}' z.csv
If closing all of the files on every iteration causes a performance problem and you find you can have, say, 11 output files open at once without getting a "too many open files" error then you could do something like this:
awk '{
    for (i=1; i<=21; i++) {
        if (i>11) close(out)
        out = "col"i".csv"
        print $(i+2) >> out
    }
}' z.csv
or slightly more efficiently but with a bit more code:
awk '{
    for (i=1; i<=10; i++) {
        print $(i+2) > ("col"i".csv")
    }
    for (; i<=21; i++) {
        close(out)
        out = "col"i".csv"
        print $(i+2) >> out
    }
}' z.csv