Unicode defines a set of codes (usually noted as U+0065, for the character A, for instance). It does not define how to store those in memory.
That's where UTF, etc. comes in. There's UTF-8, UTF-16 and UTF-32. UTF-8 uses 8 bits as base unit. If a code fits in 8 bits (i.e., U+0000 - U+007F), it'll just store it in one byte. If a code does not fit in one byte, it'll use two, or three, or more bytes. The encoding scheme for this can found on the unicode website.
Similary, UTF-16 will store a code in a 16-bit word if it fits, otherwise it can use 2 words, or maybe even more. UTF-32 will always store a code in one 32-bit word, because there are less than 4 million codes so it will always fit.
As you noticed, this means that for UTF-8 and UTF-16, the string length in characters differs from the string length in bytes. This is very annoying, so they came up with UCS-2, which is UTF-16 without the multiple words. If the character code according to UTF-16 fits in 16 bits, then we store it, otherwise we don't support the character. This means you always get 2 bytes per character, but are unable to display several rare unicode characters.
Also note that UTF-8 is backwards compatible with ASCII, in that any string made up of U+0000 - U+007F stored in UTF-8 or ASCII will look the same.
Also, with UTF-16 and UTF-32, endianness plays a role, so strings or text files may be preceded with a Byte Order Mark (BOM), FEFF I think, that shows in what endianness the string was stored.
Edit: and fpsgamer's link is a very good one too :)