It could be that the table is so small that the query optimizer is picking something "wrong".
Could we try these:
EXPLAIN
select id, date, sender_name, title, is_read, sender_id
from letter
inner join
( SELECT id
from letter i
where receiver_id = 3
and receiver_del = 0
order by is_read DESC, -- changed this
id desc
limit 0, 15
) jt using(id)
order by is_read , id desc
# and...
EXPLAIN
select id, date, sender_name, title, is_read, sender_id
from letter
inner join
( SELECT id
from letter i
where receiver_id = 3
and receiver_del = 0
order by is_read ASC,
id ASC -- both ASC
limit 0, 15
) jt using(id)
order by is_read , id desc
I am not sure if it is smart enough to efficiently do one column ASC and the other DESC.