For _that_ query, you need _this_ index:
INDEX(mphone, dn_status, send_date)
Rule of thumb: The first field(s) in the index are compared with "=", then you get one crack at a something messier: '>', IN, BETWEEN, GROUP BY, ORDER BY, etc, before the index becomes useless.
Run EXPLAIN SELECT ...
It probably picked
KEY `mphone` (`mphone`),
although it might have picked this
KEY `check_dn` (`mphone`,`keyword`,`send_date`,`dn_status`)
"keyword" is in the way of being efficient. Either of these indexes would require searching the entire list of mphone = 'xxxxx' rows.
InnoDB -- what is your value of innodb_buffer_pool_size? That setting is often much lower than it can/should be, thereby leading to inefficiency in accessing tables bigger than it is.
Caution: It will take a few hours to add the index, but it will be well worth it. Suggest you look for other indexes and add them at the same time; adding multiple indexes is the same cost. Also drop KEY `mphone` (`mphone`), it is handled by check_dn and my suggested index.
ALTER TABLE MT
DROP INDEX mphone,
ADD INDEX (mphone, dn_status, send_date),
ADD INDEX (dn_status, mphone, send_date)
etc;
Raise innodb_buffer_pool_size to 70% of _available_ ram before doing the ALTER.
Adding 60K rows per day is not much -- avg of 1/second. Even if you do them all at once, it should take only an hour or two. The buffer_pool setting should improve on that.
If you want to get all the data at once:
INDEX(dn_status, mphone, send_date)
select mphone, count(*) as total from MT
WHERE dn_status = '123'
AND send_date > '2009-09-01'
GROUP BY mphone
will run "Using index" (see EXPLAIN) and probably be a faster than your loop.
Bug? Do you want
send_date > '2009-09-01'
or
send_date >= '2009-09-01'