What does an index actually do, and what does it cost?
An index is a second, sorted copy of some of a table's data, keyed on the values you index. Its whole purpose is to let the engine answer a predicate by seeking straight to the matching rows instead of reading every row and testing each one.
Without an index, a filter on customer_id means the engine reads the table from the beginning, applies the predicate to every row, and keeps the ones that match. That cost is proportional to the size of the table. With a sorted index on customer_id, the engine can binary search to the right position and read only the matching entries — a cost proportional to the number of matches plus a logarithmic search. On a table with a million rows where forty match, that is the difference between reading a million rows and reading forty.
The cost is on the write side. Every index has to be kept in step with the table, so every INSERT has to add an entry to every index, and every UPDATE that changes an indexed column has to update it. Every DELETE has to remove an entry from each one. An index also takes disk space, and a large index is slower to keep cached in memory than a small table, which can push other work out of the buffer pool.
There is also a subtler cost: the planner has more choices. With ten indexes on a table, the optimizer has to evaluate ten possible access paths against the table's statistics, and a bad estimate can lead it to a worse plan than it would have found otherwise. Indexes are not free in planning time either.
The practical framing is that an index is a bet: you pay a little on every write in exchange for making a particular read dramatically cheaper. That bet pays off when the read is frequent, the read touches a large fraction of the table, and the index is selective. It loses when the indexed column changes constantly or when almost every query reads most of the rows anyway.