A computer-implemented method for multi-version concurrency control for database transactions, comprising: performing a first transaction on a first row in a table stored in a database; in response to performing the first transaction, retrieving, based at least on a first row position of the first row in a range of rows, a first timestamp block associated with the range of rows including the first row; in response to determining that the first timestamp block is a sparse timestamp block allocated with sufficient storage space for storing some but not all of a plurality of timestamps associated with the range of rows, the sparse timestamp block storing timestamps of transactions as well as the row positions of the rows in the range of rows affected by the transactions, determining whether the first timestamp block has reached maximum capacity; in response to determining that the first timestamp block has reached maximum capacity, allocating a second timestamp block for storing a first timestamp associated with the first transaction, the second timestamp block being a dense timestamp block having sufficient storage space for storing all of the plurality of timestamps associated with the range of rows; storing, in the second timestamp block, the first timestamp associated with the first transaction, wherein the first timestamp is stored at an offset in the second timestamp block corresponding to the first row position of the first row; replacing the first timestamp block with the second timestamp block by migrating, to the second timestamp block, a second timestamp stored in the first timestamp block, the second timestamp being stored in the first timestamp block along with a second row position of a second row in the range of rows, and the migrating of the second timestamp includes storing the second timestamp at an offset in the second timestamp block corresponding to the second row position; in response to determining that the first timestamp block has not reached maximum capacity, storing the first timestamp in the first timestamp block, the first timestamp being stored in the first timestamp block along with the first row position of the first row; and in response to determining that the first timestamp block is the dense timestamp block having sufficient storage space for storing all of the plurality of timestamps associated with the range of rows, storing the first timestamp in the first timestamp block. performing a first transaction on a first row in a table stored in a database; in response to performing the first transaction, retrieving, based at least on a first row position of the first row in a range of rows, a first timestamp block associated with the range of rows including the first row; in response to determining that the first timestamp block is a sparse timestamp block allocated with sufficient storage space for storing some but not all of a plurality of timestamps associated with the range of rows, the sparse timestamp block storing timestamps of transactions as well as the row positions of the rows in the range of rows affected by the transactions, determining whether the first timestamp block has reached maximum capacity; in response to determining that the first timestamp block has reached maximum capacity, allocating a second timestamp block for storing a first timestamp associated with the first transaction, the second timestamp block being a dense timestamp block having sufficient storage space for storing all of the plurality of timestamps associated with the range of rows; storing, in the second timestamp block, the first timestamp associated with the first transaction, wherein the first timestamp is stored at an offset in the second timestamp block corresponding to the first row position of the first row; replacing the first timestamp block with the second timestamp block by migrating, to the second timestamp block, a second timestamp stored in the first timestamp block, the second timestamp being stored in the first timestamp block along with a second row position of a second row in the range of rows, and the migrating of the second timestamp includes storing the second timestamp at an offset in the second timestamp block corresponding to the second row position; in response to determining that the first timestamp block has not reached maximum capacity, storing the first timestamp in the first timestamp block, the first timestamp being stored in the first timestamp block along with the first row position of the first row; and in response to determining that the first timestamp block is the dense timestamp block having sufficient storage space for storing all of the plurality of timestamps associated with the range of rows, storing the first timestamp in the first timestamp block.