Forma correcta de hacer conexiones MySQL

Tengo una duda un tanto existencial con el tema de las conexiones MySQL. El tema lo he googleado bastante y no encuentro una respuesta clara al respecto, ya que algunos dicen A y otros dicen B.

Por ejemplo, en una página PHP que se espera que pueda llegar a tener muchos usuarios, ¿cual es la forma correcta de hacer las conexiones a una BD MySQL? Concretamente, ¿cual de estas dos sería la más correcta?
  1. Una conexión por operación (no confundir con consulta, por ejemplo un login o un registro tienen normalmente varias consultas), en donde la conexión se abre al empezar la operación, y se cierra al terminarla
  2. Una conexión por sesión, en donde la conexión queda abierta en todo lo que dura la sesión

Yo pensaba que la opción 1 era mejor ya que así se minimizaba el número de conexiones simultáneas. Sin embargo leí a alguien en un foro decir que establecer una conexión MySQL es algo bastante costoso (muchísimo más que una consulta decía), y que era mejor hacer una conexión por sesión. Vamos que dicha respuesta ya me ha dejado en la duda :-?
La forma "correcta" es abrir la conexion en un objeto de base de datos, ejecutar durante lo que dure el script las sentencias sobre ese objeto y por ultimo cerrar la conexión.
El abrir y cerrar conexiones muchas veces en un mismo script no es recomendable. (ya que estas creando y destruyendo objetos que vas a seguir utilizando, vamos que no tiene sentido)
Los scripts como PHP las conexiones mysql se cierran solas cada vez que termina el script, es decir, no duran toda la sesion sino el tiempo de ejecución de la página.

En un programa tipo cliente/servidor lo correcto seria (digamos en un programa que no termina, siendo un bucle infinito con hilos, en este conecepto php no entra a no ser que se ejecute en modo cli con hilos, es decir para programas no web desde consola) en el caso de querer tener una concurrencia de la leche para que fuera más rápido la carga de datos:
- Pool-cola de sentencias SQL
- X conexiones activas abiertas dependendiendo de la concurrencia/carga, que serian manejadas por el pool.
- El hilo del programa cuando necesita ejecutar sentencias las pondria en el pool y luego recogeria el resultado, esto se suele hacer de manera asincrona.
En un caso normal seria como con PHP, abres conexion con la base de datos y la cierras cuando ya no te haga falta de tal manera que solo se quede abierta un tiempo limitado.

Si quieres una pagina con alta concurrencia y asincrona lo logico seria usar bases de datos NOSQL como mongodb, couchdb, redis.. otra opcion son los cluster de bases de datos relacionales como los de mysql con master/esclavo.
la forma correcta es 'no hacer' conexiones mysql, intentar que todas las consultas esten cacheadas y 'serializadas' en disco, con un timeout de 10 minutos, por ejemplo. la forma mas o menos correcta de hacer esto es hacer un MD5 a la query mysql, y cachear el resultado serializado en disco con dicho numero MD5 como nombre de archivo. cuando tengas otra consulta, haces el MD5 y mira si existe dicho fichero en disco y si no ha pasado el timeout, en tal caso, deserializa y para adentro.

si eres mas puntilloso, programate una capa ORM (no es nada complicado, es una clase por tabla, y puedes extender gran parte de la funcionalidad de una a otra) que cumpla el interfaz CRUD, y que en caso de querys basadas en PK (primary key) haga la cache en disco 'serializado'. puesto que todo acceso a la BBDD se hace a traves de ORM, puedes detectar cuando se realiza una escritura a Mysql, y hacer 'shadow write', o sea, escribir al MYSQL y serializar a disco a la vez.

y ya, para el colmo de la velocidad, busca informacion sobre handlersocket, PERO OJO, solo para lecturas de la BBDD, la escritura en la BBDD a traves de handlersocket no invalida la cache y puedes obtener datos erroneos en posteriores lecturas.

el resto de la gente te dira que uses memcached. mi opinion es que no la uses, ya que con este sistema de cache de disco, puedes 'confiar' en que la cache de disco de linux haga su trabajo y mantenga en memoria los registros serializados que mas se usen.

la ultima alternativa es olvidarte de mysql y usar mongodb, que puede multiplicar facilmente por 5 tu rendimiento actual de mysql.
Muchas gracias por las respuestas. La verdad de mongodb no tengo ni pajolera idea, de ahí que me pregunte desde la ignorancia si al ser una DB no relacional sería la mejor opción para mi caso concreto, en donde hay muchas relaciones (por narices además) entre las diferentes tablas, y se ha de minimizar la duplicidad de datos.

Sobre lo de serializar en disco no sé yo si ayudaría mucho, ya que es tipo de aplicación en donde se espera que haya muchos usuarios, pero son del tipo que se conectan, hacen una búsqueda y se van. Aparte de que los resultados búsquedas van a tener mucha "variabilidad".

Lo que se voy a buscar información de como implementar una pool de conexiones en MySQL.


Gracias de nuevo
precisamente si vas a tener muchas lecturas 'metralleta' es donde la cache de disco serializada mas te va a ayudar (sobre todo si las busquedas son muy repetidas). haces la consulta mysql, guardas el resultado serializado a un archivo con su nombre MD5. cuando llega otra consulta miras si ya existe ese archivo, y en tal caso, deserializa y sigue trabajando. al no tocar mysql, aumentas la velocidad de ejecucion en 2-3 magnitudes.

no hagas un pool de conexiones mysql si vas a trabajar en PHP, no vale de nada ya que cada request es un request 'nuevo', y sobre todo, si esperas mucho volumen de trafico NO USES CONEXIONES PERSISTENTES.
Gracias

Una duda, ¿si hay colisiones en el MD5 como se resuelven?

Otra incógnita que me surge es que la aplicación va a tener dos partes, una parte en PHP minoritaria, y otra bastante más mayoritaria que va a ser en en una aplicación para Android. Yo entiendo que lo que me comentas es por ejemplo en PHP, porque esa caché funcionaría a nivel global. Sin embargo en la aplicación Android funcionaría a nivel local sólo, y no sé si eso ayudaría lo suficiente.
xzibit escribió:Gracias

Una duda, ¿si hay colisiones en el MD5 como se resuelven?

Otra incógnita que me surge es que la aplicación va a tener dos partes, una parte en PHP minoritaria, y otra bastante más mayoritaria que va a ser en en una aplicación para Android. Yo entiendo que lo que me comentas es por ejemplo en PHP, porque esa caché funcionaría a nivel global. Sin embargo en la aplicación Android funcionaría a nivel local sólo, y no sé si eso ayudaría lo suficiente.


Supongo que en PHP quieres tener un backend para hacer cosas tipo REST y asi servir datos desde la pagina web al programa android.
Para eso yo te recomiendo que uses node.js (muy escalable y es trivial montar un servicio REST) y mongoDB puesto que ambas son soluciones muy ligeras/asincronas, mongoDB aunque no sea relacional se puede simular hasta cierto punto, yo de ti le daba un vistazo, pero en cualquier caso no creo que tengas excesivos problemas con myqsl. De todas maneras tampoco te rayes mucho que seguramente te sobrara rendimiento, simplemente intenta que tu servicio pueda utilizar tecnicas de CACHE como te hemos dicho.
En el movil android para hacer fluido yo usaria tambien datos cacheados, tambien puede valer una base de datos local en sqlite, de esto tienes tutoriales por la red (si se trata de cargar informacion semiestatica, yo para eso uso marcas de tiempo para recargar lo justo y necesario)
xzibit escribió:Gracias

Una duda, ¿si hay colisiones en el MD5 como se resuelven?

Otra incógnita que me surge es que la aplicación va a tener dos partes, una parte en PHP minoritaria, y otra bastante más mayoritaria que va a ser en en una aplicación para Android. Yo entiendo que lo que me comentas es por ejemplo en PHP, porque esa caché funcionaría a nivel global. Sin embargo en la aplicación Android funcionaría a nivel local sólo, y no sé si eso ayudaría lo suficiente.


es altamente improbable la colision en MD5. 160bits de resume da para mucho. me niego a explicarte o calcularte las posibles colisiones MD5, porque esta mas que documentado por otros sitios, busca si aun te quedas con la duda.

todo lo que te he dicho va orientado a PHP, y aunque la parte PHP sea minoritaria, dudo que quieras que desde una aplicacion android ataquen directamente al mysql, vamos, yo al menos no lo haria, a no sea que tuviera los dispositivos android en intranet. me niego a abrir el puerto mysql a otra cosa que no sea localhost...
Buff pues yo en Android estaba usando el conector jdbc:mysql de Java directamente. Me imagino que esta opción no me la recomendáis ni de coña, no?


EDITO: He estado googleando y me parece un disparate en términos de seguridad que la información viaje "plana". Para no tener que que cambiar medio código y seguir usando el conector, estaba pensando en hacer una de dos:
  1. Un tunel SSH
  2. Usar una conexión SSL

Pero no sé yo cual de las dos opciones es mejor...
No es para nada recomendable abrir una BD a algo que no sea localhost, como muchisimo a alguna maquina con IP fija (y ni aun asi en produccion XD).
Para recibir datos remotos en un movil, lo mejor es hacerse un backend tipo REST/SOAP con json o xml para interactuar con la base de datos.
Para que veas unos ejemplos básicos del tutorial de sgoliver para novatos.
http://www.sgoliver.net/blog/?p=2665 (REST)
http://www.sgoliver.net/blog/?p=2594 (SOAP)
Si los datos pueden ser locales tienes sqlite como DB local.
xzibit escribió:Buff pues yo en Android estaba usando el conector jdbc:mysql de Java directamente. Me imagino que esta opción no me la recomendáis ni de coña, no?


EDITO: He estado googleando y me parece un disparate en términos de seguridad que la información viaje "plana". Para no tener que que cambiar medio código y seguir usando el conector, estaba pensando en hacer una de dos:
  1. Un tunel SSH
  2. Usar una conexión SSL

Pero no sé yo cual de las dos opciones es mejor...


por supuesto que es un disparate en terminos de seguridad que la informacion viaje plana... si supieras el mogollon de charlas jugosas de whatsapp y de messenger que he capturado con wireshark, te daria miedo. amen de clientes de correo que aun envian la contraseña en texto plano al POP y el mogollon de correo que he leido teniendo una puta wifi abierta...

si no quieres cambiar mucho tu estructura actual, usar SSL (vamos, un TLS de toda la vida) sera lo que menos trabajo te costara, y aun asi deberas contratar un certificado a una autoridad certificadora decente.

o eso, o usar un interfaz REST/SOAP como te dice el compañero de arriba, y cifrar el trafico usando AES o blowfish, que son dos cifrados simetricos faciles de implementar.
Muchas gracias por las respuestas. Ya he implementado unos servicios web SOAP con Axis2 (a través de un servidor Tomcat) y he vuelto a poner la BD MySQL accesible sólo al localhost. Me ha costado lo suyo montarlo todo y hacerlo funcionar porque es la primera vez que tocaba servicios web, pero al final lo he conseguido y funciona todo perfecto. Lo bueno es que al ser Java he aprovechado el 90% del código que tenía.

Eso si, de momento la conexión de los servicios web es http a pelo. Realmente no me importa que así sea, salvo cuando viaja la contraseña de usuario desde el programa cliente al servicio (o sea el login y otro servicio que tengo para cambiar la contraseña). Con lo que comenta f5inet del cifrado tengo la duda entre si cifrar toda la conexión o bien cifrar sólo las contraseñas para más simplicidad. ¿Que opinan?

Y ya que estamos hablando de seguridad. Me gustaría conocer por alguien sabe, como sería posible esconder las contraseñas dentro del código Java. Lo que quiero decir es que si abrimos un .class ya compilado (o precompilado mejor dicho) con un editor, podremos ver todas las inicializaciones de los Strings todas juntas en una porción del archivo. Si una de ellas es por ejemplo alguna contraseña de cualquier tipo pues mal asunto porque se podría ver tal cual. Y aunque en cierta manera sería como encontrar una aguja en un pajar, no me quedo muy tranquilo sabiendo que la contraseña está ahí sin cifrar. ¿Alguna forma para evitar esto?


PD: Por cierto que el cifrado a nivel de aplicación/base de datos es asimétrico. Concretamente:
sha256( sha256(password) + salt)
en donde el "salt" son 64 bits provenientes de un random criptográficamente seguro que sólo cambia cuando se genera o cambia la contraseña. ¿Les parece suficiente?
xzibit escribió:Muchas gracias por las respuestas. Ya he implementado unos servicios web SOAP con Axis2 (a través de un servidor Tomcat) y he vuelto a poner la BD MySQL accesible sólo al localhost. Me ha costado lo suyo montarlo todo y hacerlo funcionar porque es la primera vez que tocaba servicios web, pero al final lo he conseguido y funciona todo perfecto. Lo bueno es que al ser Java he aprovechado el 90% del código que tenía.

Eso si, de momento la conexión de los servicios web es http a pelo. Realmente no me importa que así sea, salvo cuando viaja la contraseña de usuario desde el programa cliente al servicio (o sea el login y otro servicio que tengo para cambiar la contraseña). Con lo que comenta f5inet del cifrado tengo la duda entre si cifrar toda la conexión o bien cifrar sólo las contraseñas para más simplicidad. ¿Que opinan?

Y ya que estamos hablando de seguridad. Me gustaría conocer por alguien sabe, como sería posible esconder las contraseñas dentro del código Java. Lo que quiero decir es que si abrimos un .class ya compilado (o precompilado mejor dicho) con un editor, podremos ver todas las inicializaciones de los Strings todas juntas en una porción del archivo. Si una de ellas es por ejemplo alguna contraseña de cualquier tipo pues mal asunto porque se podría ver tal cual. Y aunque en cierta manera sería como encontrar una aguja en un pajar, no me quedo muy tranquilo sabiendo que la contraseña está ahí sin cifrar. ¿Alguna forma para evitar esto?


PD: Por cierto que el cifrado a nivel de aplicación/base de datos es asimétrico. Concretamente:
sha256( sha256(password) + salt)
en donde el "salt" son 64 bits provenientes de un random criptográficamente seguro que sólo cambia cuando se genera o cambia la contraseña. ¿Les parece suficiente?


Es que depende de la información, si no es importante te la sopla y con solo el usuario si quieres te sobraría. De todas maneras, el problema es para cuando la gente se conecta a WIFI o red donde te puedan hacer un "man in the middle" y bueno... lo importante es que la contraseña vaya cifrada en la aplicación antes de mandarla por una petición de internet, el como te lo montes ya es cosa tuya xD En cualquier caso toda la conexión puede ir por https.

Otra opción que esta guay con respecto a funcionalidad es el OAuth que esta completamente integrado en android con las cuentas de gmail y así solo te preocupas de los permisos, pero bueno esto depende del perfil de programa/usuario xD
12 respuestas