I have obtained good results as far as performance goes by using compound primary keys for tables that describe relationships. There are two effects of declaring a primary key:
You get constraints that require each of the participating columns to be non-null, and require uniqueness of the participating columns, taken together.
You get an index that provides rapid access to the individual row, given the primary key. Most DBMSes make this index for you.
How useful this index is depends on the query optimizer, the order of the columns in your primary key declaration, and the pattern of use of your data. Sometimes, it can be useful to supplement the automatic index by indexes you create yourself, on columns other than the first one in a compound primary key.
The constraints you get by declaring a compound primary key are generally more useful than the ones you get by creating a surrogate key and declaring that the primary key.
Again, all of the above pertains to tables that describe relationships between or among entities. Tables that describe entities should have a simple primary key. Preferably a natural key, but in cases where the given data does not provide a reliable key, a surrogate key can be necessary.