If you use Postgres, this three-part series, starting here, is a great explainer on how transactions are not fully isolated, what the consequences of that might be, and how to deal with it.
With thanks to @Dirk Cleenwerck for pointed this out on Twitter.
https://www.postgresql.org/docs/9.1/static/transaction-iso.html
Read Committed is general suitable Read Committed is the default isolation level in PostgreSQL. When a transaction uses this isolation level, a SELECT query (without a FOR UPDATE/SHARE clause) sees only data committed before the query began; it never sees either uncommitted data or changes committed during query execution by concurrent transactions. In effect, a SELECT query sees a snapshot of the database as of the instant the query begins to run. However, SELECT does see the effects of previous updates executed within its own transaction, even though they are not yet committed. Also note that two successive SELECT commands can see different data, even though they are within a single transaction, if other transactions commit changes during execution of the first SELECT.