Hace poco más de un mes, en Moscú, se celebró la conferencia más grande de la comunidad post-PG PGConf.Rusia 2019, que reunió a más de 700 personas en la Universidad Estatal de Moscú. Decidimos publicar un video y una transcripción de los mejores informes. La presentación de
Ivan Frolkov sobre los errores típicos cuando se trabaja con PostgreSQL fue considerada como la mejor en la conferencia, por lo que comenzaremos con ella.
Por conveniencia, dividimos el descifrado en dos partes. En este artículo, hablaremos sobre nombres inconsistentes, sobre restricciones, sobre dónde es mejor concentrar la lógica: en la base de datos o en la aplicación. La segunda parte tratará con el manejo de errores, acceso concurrente, operaciones no cancelables, CTE y JSON.

En nuestra empresa, me dedico a la atención al cliente en temas relacionados con las aplicaciones, es decir, ayudo en casos de problemas con las conexiones, con la optimización de consultas y otras cosas similares. He visto suficientes de las aplicaciones más diversas. Lo que simplemente no vi! Quizás incluso más de lo que nos gustaría. Parte de lo que contaré se aplica no solo a PostgreSQL, sino a cualquier base de datos, pero algo principalmente a PostgreSQL.
La conclusión principal que pude extraer de lo que vi fue bastante inesperada: de hecho, cualquier aplicación con la debida persistencia puede funcionar. Hubo un proyecto maravilloso (no puedo mencionar todas las empresas con las que trabajamos) en el que una aplicación aún más maravillosa creó tablas por millones. Se veía así: el lunes, el sistema funciona bien, y el viernes prácticamente no funciona. Los fines de semana, lanzan VACUUM FULL y el lunes vuelve a funcionar bien. Resulta que puedes burlarte de PostgreSQL de esta manera, y todo esto vivirá y funcionará durante bastante tiempo. Otro compañero hizo algo extraño: todo se basó en factores desencadenantes en él, no hubo procedimientos en absoluto. Es decir, la mayoría de las mesas no se pueden tocar, algo no se puede hacer, pero esta base también vivió.
Lo explicó de esta manera: “la base se mueve de un estado consistente a otro consistente. Si vuelvo a cargar los datos, se romperá. Pero como tengo disparadores y una clave única, no puedo volver a enrollar los datos ". El enfoque es salvaje, pero al mismo tiempo tiene sentido. Tal vez fue necesario hacerlo de manera diferente, pero también es necesario tener en cuenta las características de los clientes. El primer error del que hablaré es:

Aquí hay un ejemplo real que encontré. En la diapositiva, verá cómo se nombró la misma entidad en diferentes columnas. También se podría con espacios. Otros objetos también fueron nombrados inconsistentemente. Si necesita tomar algo en otra tabla, entonces necesita ver cómo se llama allí, ¿es lo mismo? Si tiene id_user y user_id en la misma tabla, el trabajo comienza con la investigación: ¿qué significaría todo?
Para otros clientes, todos los objetos se nombraron así: dos letras, luego cinco dígitos. Debo decir que no fue "1C". Por qué hicieron esto, no lo sé: no había lógica en esto, pero es mi negocio optimizar las consultas.
Otro ejemplo: parte de los nombres en ruso, parte en no ruso, pero con algún tipo de acento ruso. Esto dificulta la comprensión y crea nuevos errores. Yo mismo trato de nombrar las columnas como si estuviera contando con un servicio, cuál de estos nombres de columna hará automáticamente nombres de columna normales en algún informe. En la vida real, desafortunadamente, no es muy exitoso nombrar consistentemente, incluido el mío. Esto es especialmente difícil con el desarrollo colectivo. Pero debemos esforzarnos.
Otra razón importante para nombrar secuencialmente: los nombres de objetos están disponibles a través de solicitudes de metadatos, es decir, los nombres también son datos. Podrá escribir una solicitud y seleccionar, por ejemplo, todas las imágenes, en general, todas las imágenes, de la base de datos.

Los metadatos claros son muy convenientes. Especialmente cuando considera los problemas típicos con la documentación, y en mi experiencia la documentación generalmente está ausente, incompleta o incorrecta, o ambas: porque la tarea de escribir buena documentación es comparable en complejidad a la tarea de escribir el código en sí. Por lo tanto, es mejor cuando el código se auto documenta. Y un nombre lógico y coherente de los objetos contribuye a esto, y cuando algo no está claro, debe escribir un código de fragmento y observar cómo funciona. Una vez que no es nada, dos nada, pero cuando lo haces todo el día, es agotador.

El caso real: una organización muy seria con la que trabajamos tenía una base: el flujo de trabajo en Oracle. Lo trasladamos a Postgres. Uno de los términos del contrato era que imponíamos CLAVES EXTRANJERAS. No estaban allí y, desafortunadamente, no pudimos imponerlos: resultó que las mesas tenían muchas filas "izquierdas", y nadie sabe qué hacer con ellas, incluido el cliente.
Cuando no necesita mirar las barras de progreso, pero trabaja con documentos para pagar dinero, entonces la situación es triste. Ayuda mucho cuando, según el contrato, el programador paga los errores por sí mismo, y es deseable que las cantidades sean grandes; luego, la iluminación ocurre en minutos, probablemente quince. Las restricciones aparecen inmediatamente, inmediatamente todo comienza a ser verificado.
Ni siquiera se imagina (bueno, tal vez alguien ya se imagina) lo mucho más conveniente que es lidiar con el caso cuando el pago falló, que cuando pasó, pero no allí. Especialmente si la cantidad es grande. Esto es por experiencia personal.

Por otro lado, a menudo se puede escuchar que la restricción reduce el rendimiento. Sí, lo hacen, pero si desea tener los datos correctos, simplemente no hay otras opciones para usted. Si tiene una aplicación que tiene en cuenta el número de visitas a la tienda por parte de los clientes, puede haber imprecisiones que no afectarán especialmente las estadísticas, y si contamos el dinero, las restricciones son necesarias.
Los nombres de restricciones generalmente son generados por un ORM o sistema, y generalmente nadie se molesta específicamente en nombrar restricciones, ¡pero en vano! Cuando continúa procesando el error, con el nombre de restricción, puede enviar un mensaje claro al usuario, clasificar el error y hacerle saber si debe intentar realizar la operación nuevamente, o si esta operación ya no es necesaria o simplemente no puede repetirse.
Otra cosa que no he visto, pero que recomiendo encarecidamente: para todas las operaciones importantes de auditoría financiera (y no solo financiera), debe haber al menos dos. El hecho es que tarde o temprano se encontrará con algo para cambiar en el código, y es muy posible que rompa una de las verificaciones. Entonces el segundo te salvará. Si haces tres, tampoco está mal.

A menudo surge la pregunta: dónde verificar la exactitud de los datos. ¿En el cliente o en el servidor? En mi opinión, es obvio que debe verificar tanto allí como allí. Tiene un error en el cliente, entonces el servidor no está
fallará, o si tiene un error en el servidor, entonces al menos el cliente ayudará a rastrearlo. La pregunta es algo discutible, y pasamos sin problemas al tema: ¿dónde mantener la lógica base: en la aplicación o en la base de datos?
Es conveniente en la base de datos porque, en mi experiencia, una empresa regularmente emite cambios urgentes: elimine o inserte esto y eso es lo segundo. Si tiene lógica en el código compilado, debe recopilar, implementar y ver qué sucedió. A menudo esto es simplemente imposible. En la base de datos, esto es más conveniente. Pero hay un aforismo bien conocido: los programadores experimentados de Fortran escriben en Fortran en cualquier idioma. El 80 por ciento del código del servidor está escrito en un estilo completamente de procedimiento: tenemos la función "get_user ()" y devuelve el tipo "usuario", y si "get_list_users ()", devuelve una matriz de "usuarios". Es realmente más conveniente escribir tales cosas en Java que en SQL o pgsql.

Por otro lado: ¿por qué necesita la función "get_user ()"? Simplemente lo toma en una mesa o en una vista. Como tiene una base de datos relacional, debe escribir, como me parece, relacional. Es importante, en primer lugar, determinar claramente con qué datos estamos trabajando: si nuestros datos son basura o semi-basura, entonces el resultado será apropiado y probablemente no debería eliminarse. Si los datos son importantes para nosotros, si se trata de dinero, propiedad u operaciones legales, entonces se necesita restricción y cuanto más mejor. Repito: es mejor no realizar la operación que realizarla incorrectamente. Y no escriba código de procedimiento en una base de datos relacional: lo lamentará mucho.

Vi una tabla con 30 mil filas (productos), en la que la solicitud "mostrar una lista de productos relevantes" se ejecutó durante aproximadamente un segundo. Aparentemente, lograron crear un esquema de base de datos "hermoso y complejo". Personalmente, creo que si está haciendo algo muy complicado, lo más probable es que esté haciendo algo mal o que realmente tenga una tarea muy, muy difícil. Si tiene algún tipo de tienda o una aplicación regular para contadores, entonces es poco probable que existan relaciones muy complejas entre entidades.
Cuando comencé mi carrera profesional, la tabla en un archivo DBF de 60 megabytes en el sistema bancario parecía muy grande, y ahora 60 megabytes no es nada en absoluto: el hardware es mejor, el software es mejor, todo funciona más rápido, pero la pregunta sigue siendo: ¿dónde obtienes tanto? datos? Las bases muy grandes e hinchadas generalmente se vuelven así debido a los archivos. En cualquier DBMS y en PostgreSQL, se ha invertido mucho esfuerzo para garantizar el funcionamiento competitivo de la aplicación consistente. Lo más probable es que el archivo no cambie, y la mayoría de las capacidades de DBMS para trabajar con él no son necesarias en absoluto. Vale la pena pensar en sacarlo del DBMS.

De vez en cuando, con una especie de estrabismo del comisario, hacen la pregunta: ¿PostgreSQL extraerá una base de tal o cual volumen? Pero aquí la pregunta en sí es extraña: puede poner datos en la base de datos tanto como desee, siempre que haya suficiente espacio en disco, habrá mucho. La pregunta es, por ejemplo, cómo hacer una copia de seguridad de los archivos en petabytes, dónde colocará la copia de seguridad completa y cuánto la quitará. Sospecho firmemente que al menos parcialmente estos requisitos de volumen están relacionados con el deseo de los vendedores de equipos de venderle más.
Si almacena documentos en la base de datos, es poco probable que los procese allí: la hoja de cálculo de Excel, por supuesto, puede modificarse en el servidor, pero esta es una ocupación extraña. Lo más probable es que tales archivos sean generalmente de solo lectura. Es mejor almacenar enlaces a documentos y ellos mismos en algún almacenamiento externo. Al final, puede mantener la firma digital de la tabla, para que no cambie (si decide los asuntos legislativos relevantes).
Otra observación: si no tiene un mega-mega negocio, no una especie de, por ejemplo, una empresa federal, entonces es poco probable que tenga una base muy grande. Si no almacena video en él, por supuesto.

Otra razón por la que la base de datos es grande son los índices innecesarios. No encontré bases sin índices, pero a menudo encontré bases donde varios índices en las mismas columnas en el mismo orden. La base te permite hacer esto. Cuando cree un índice, vea si duplica uno existente. Puede ver qué índices no son necesarios mirando pg_stat_user_indexes para ver qué tan activamente se usa el índice. Tal vez no se requiere en absoluto.
Me encontré con una situación (por cierto, típica), cuando una tabla muy grande no está particionada. En todos los DBMS, las tablas grandes se dividen mejor, pero en PostgreSQL esto es especialmente cierto debido a nuestro querido VACUUM. Aconsejaría particionar tablas comenzando probablemente con 100 gigabytes. Tal vez a partir de los 50. Vi tablas de terabytes no particionadas y, sin embargo, vivían de SSD. Pero esto es un poco demasiado, sería mejor cortarlos.

Y una observación más: casi todas las bases de datos de un gran volumen son solo archivos adjuntos. En vivo, los datos cambiantes rara vez se encuentran en tales bases de datos. Un determinante con lo que tiene: si es el archivo, entonces puede pensar en cómo llevarlo a algún lado. Y, por cierto, puede proporcionarle acceso desde la base de datos. Entonces la aplicación no necesita ser cambiada: nada cambiará por ello.
Algunas de estas observaciones pertenecen a la categoría "es mejor ser rico y saludable que pobre y enfermo". A menudo, en primer lugar, hay código heredado. En segundo lugar, sucedió algo inesperado, no pensaron en algo y resulta que no todo es tan hermoso como nos gustaría. Pero sin embargo: no seas muy inteligente. Recuerda que si eres muy inteligente, lo más probable es que estés haciendo algo mal.
[Continuará]