NOTA: Debido a que la pregunta específicamente pide explicar el esquema en términos simples, me abstendré de dar una explicación en el contexto de los sistemas de bases de datos transaccionales donde MVCC se usa comúnmente. En cambio, mi respuesta es puramente desde el punto de vista del “control de concurrencia”.
MVCC (Control de concurrencia de versiones múltiples) es un mecanismo de control de concurrencia utilizado principalmente en bases de datos. Cada mecanismo de control de concurrencia tiene que resolver de alguna manera los siguientes problemas:
- carrera escritor-escritor
- raza lector-escritor
Los dos puntos anteriores implican que el escritor debe tener un acceso exclusivo a datos mutables compartidos mediante un bloqueo exclusivo. Del mismo modo, uno o más lectores deberían poder leer los datos compartidos mediante el bloqueo compartido / lector.
- ¿Por qué mi código JavaScript muestra un error de bucle infinito en la línea 7? ¿Por qué no está eliminando los elementos de la matriz de entrada?
- ¿Cuáles son los mejores proyectos de estructura de datos para los estudiantes?
- ¿Cuáles son las desventajas del algoritmo genético?
- Si sabemos cómo funciona un algoritmo de hash de contraseña en particular, ¿por qué no podemos simplemente crear una contraseña que genere el mismo hash?
- ¿Cuáles son algunos algoritmos conocidos para encontrar una coincidencia perfecta en un gráfico bipartito?
Entonces un escritor bloqueará a cualquier otro lector o escritor concurrente. Del mismo modo, un lector bloqueará a cualquier otro escritor concurrente.
Los bloqueos / Mutexes (verifique el bloqueo de lectores y escritores de pthread) son la forma más sencilla de obtener este tipo de comportamiento mediante la implementación de la exclusión mutua y la prevención de condiciones de carrera en la sección crítica. Incluso un simple bloqueo pthread_mutex_t también proporcionará exclusión mutua, pero su semántica es muy fuerte en el sentido de que un lector bloqueará a otro lector.
MVCC se usa en sistemas de bases de datos (también se puede usar en otros sistemas) para proporcionar una mejor escala para las lecturas. En otras palabras, un lector y escritor nunca se bloquearán entre sí. Esto proporciona lecturas sin bloqueo / sin enclavamiento donde el lector no tiene que tomar bloqueos o mutexes explícitos antes de acceder a los datos compartidos.
Los lectores siempre ven alguna instantánea en el tiempo de los datos. Las lecturas y escrituras están asociadas con una marca de tiempo (o alguna forma de marca de tiempo administrada internamente por la base de datos).
Entonces, ¿qué lee realmente el lector y cómo evitamos las condiciones de carrera cuando los lectores no toman ningún candado?
Si los datos que interesan al lector están siendo modificados por un escritor concurrente, entonces el sistema puede realizar uno de los siguientes procedimientos internos para completar la operación de lectura:
- VERSIÓN: devuelve una versión anterior de los datos porque un escritor concurrente está cambiando la versión actual / más reciente. Esto sucede en los sistemas basados en Copy-OnWrite donde los datos nunca se modifican en su lugar. Un objeto (elemento de datos) es una entidad inmutable y cualquier cambio / actualización del objeto dará como resultado una versión inmutable diferente del objeto. Cada versión inmutable del objeto está asociada a una versión # monotónicamente creciente correspondiente.
- Cuando decimos que puede haber múltiples versiones diferentes del objeto, ¿cómo determinamos qué versión devolver para una operación de lectura determinada?
- Esto depende de la marca de tiempo asociada con la llamada de lectura. El usuario pasa explícitamente una marca de tiempo o un número de versión en la llamada de lectura o el sistema registra internamente una marca de tiempo cuando se recibió la llamada de lectura y devuelve la versión correspondiente a esa marca de tiempo.
- DESHACER – En este caso el sistema tiene suficiente información (o metadatos) para reconstruir una imagen antigua de los datos sin tener que registrar o registrar explícitamente diferentes versiones de los datos. Esto sucede cuando el elemento de datos está sujeto a modificación en el lugar. Aquí un cambio / actualización de un objeto no da como resultado una nueva versión. En cambio, cada cambio registra UNDO que son vectores de cambio (vectores de cambio hacia atrás) que ayudan a reconstruir una imagen antigua de los datos a partir de los datos actuales.
- Por ejemplo, si el escritor cambia un valor de datos de A a B, la UNDO correspondiente sería algo que reconstruya A de B.
- Luego, UNDO se utiliza para reconstruir datos antiguos y devolverlos como resultado de la operación de lectura.
La diferencia en estos dos enfoques es que en el caso del control de versiones, los datos son inmutables y cada cambio da como resultado una nueva versión, mientras que en caso de deshacer solo registramos información “suficiente” para reconstruir los datos antiguos.