Hint Bits/es

El mecanismo MVCC en PostgreSQL provee muchas características útiles, pero hay algunos efectos secundarios de la implementación que son confusos. Uno de ellos tiene que ver con el procesamiento de los hint bits, que puede significar que haya muchas escrituras a una tabla de la base de datos aún cuando tu consulta solamente esté leyéndola.
Los hint bits se usan para marcar tuplas como creadas y/o eliminadas por transacciones que se sabe que están comprometidas (committed) o abortadas. Para determinar la visibilidad de una tupla que no tiene esos bits seteados, es necesario consultar pg_commit y posiblemente pg_subtrans, así que es una verificación cara de ejecutar. Por otro lado, si la tupla tiene los bits seteados, su estado es conocido (o en el peor caso, puede calcularse fácilmente usando tu snapshot actual, sin mirar pg_commit).
Hay cuatro hint bits:
*XMIN_COMMITTED -- sabemos que la transacción que creó la tupla está comprometida
*XMIN_ABORTED -- sabemos que la transacción que creó la tupla está abortada
*XMAX_COMMITTED -- lo mismo, para la trasacción que borró la tupla
*XMAX_ABORTED -- ídem
Si ninguno de los bits XMIN está seteado, entonces ya sea:
* La transacción que creó la tupla todavía está en progreso, lo cual puedes verificar examinando la lista de transacciones en ejecución, en memoria compartida;
* Eres el primero que está haciendo esta verificación desde que la transacción terminó, en cuyo caso necesitas consultar pg_commit para saber el estado de la transacción, y puedes actualizar los hint bits si averiguas su estado final.
Si la tupla ha sido marcada borrada, lo mismo aplica para los bits XMAX.
Cualquier examen de una tupla -- ya sea por vacuum o una operación DML normal -- va a actualizar sus hint bits para que coincida con el estado de commit/abort de la transacción que crea/borra la tabla al instante del examen. Un SELECT normal, count(*) o VACUUM de la tabla completa va a revisar la visibilidad de cada tupla, y setear sus hint bits.
Otra cosa a tomar en cuenta es que los hint bits se verifican y setean individualmente para cada tupla. Aunque un recorrido simple de la tabla visitará todas las tuplas de la página y actualizará todos los hint bits de una vez, los accesos unitarios (por ej. la lectura de tuplas individuales en un index scan) podrían resultar en muchas escrituras a la misma página, a medida que los hint bits de cada tupla se vayan actualizando con el tiempo.
==Registro de commits==
Algunos detalles de esto vienen de src/backend/access/transam/README:
* "pg_commit registra el estado de commit cada transacción a la que se le haya asignado un XID."
* "Sólo se asignan XIDs permanentes a transacciones y subtransacciones cuando/si hacen algo que requiera uno -- normalmente, insert/update/delete, aunque existen algunas otras razones para asignar XIDs."
pg_commit se actualiza sólo al término de una subtransacción o transacción principal. Cuando el ID de transacción se asigna, la página del commit log que la contiene es verificada; si no existe, se inicializa.
pg_commit se emplaza en páginas de 8kB cada una. Cada transacción necesita 2 bits, así que en una página de 8 kB hay espacio para 4 transacciones /byte * 8 k bytes = 32k transacciones.
Al initializarse, las páginas se llenan con ceros, que es el patrón de bits que indica "transacción en progreso". Cuando una transacción empieza, sólo necesita asegurarse que la página de pg_commit existe, pero no necesita escribir nada a ella. Desde Postgres 8.3 esto sucede no cuando la transacción empieza sino cuando el XID se asigna.
Esto significa que una transacción cada 32k transacciones de escritura tiene que hacer un poco de trabajo extra cuando se asigna su propio XID, a saber tiene que crear y rellenar con ceros la próxima página de pg_commit. Y esto no sólo ralentiza esa misma transacción, sino que además las siguientes sesiones que quieren un XID pero aparecen en medio de este proceso de llenado-con-ceros tendrán que esperar. Esto probablemente contribuye a generar retardos inpredecibles.
Las páginas CLOG no se escriben a disco hasta que se llenan los buffers internos de CLOG, punto en el cual el búfer menos recientemente usado se escribe a almacenamiento permanente.
Fuentes:
* http://archives.postgresql.org/pgsql-patches/2005-07/msg00390.php
* http://archives.postgresql.org/pgsql-performance/2008-05/msg00182.php
Category:Performance