
Introducción
Si trabajas con PHP y bases de datos MySQL/MariaDB, tarde o temprano surge la duda: ¿MySQLi o PDO? Ambas extensiones permiten conectarte, hacer consultas y usar prepared statements, pero no son equivalentes en alcance ni en portabilidad. En esta guía verás cuándo conviene cada una, sus ventajas/desventajas, y patrones recomendados para que tu código sea más seguro, mantenible y listo para crecer.
¿Qué son MySQLi y PDO?
- MySQLi (“MySQL Improved”): extensión específica para MySQL/MariaDB. Ofrece APIs procedimental y orientada a objetos, soporte de prepared statements y funciones propias del ecosistema MySQL.
- PDO (PHP Data Objects): capa de acceso genérica y orientada a objetos que funciona con múltiples motores (MySQL/MariaDB, PostgreSQL, SQLite, SQL Server, etc.). Misma API para distintos drivers.
Tabla comparativa rápida
| Criterio | MySQLi | PDO |
|---|---|---|
| Motores soportados | Solo MySQL/MariaDB | Múltiples (MySQL, PostgreSQL, SQLite, etc.) |
| API | Procedimental y OO | Solo OO |
| Prepared statements | Sí | Sí |
| Placeholders | ? posicionales | ? y nombrados :id |
| Portabilidad | Baja (amarrado a MySQL) | Alta (cambiar motor con mínimo refactor) |
| Funciones específicas MySQL | Amplias (e.g. mysqli_stmt_get_result) | Depende del driver; API común |
| Curva de aprendizaje | Baja si vienes de MySQL | Leve, pero más consistente |
| Errores/Excepciones | Estilo tradicional; configurable | Excepciones por defecto (recomendado ERRMODE_EXCEPTION) |
| Transacciones | Sí | Sí (API uniforme entre motores) |
¿Cuándo usar MySQLi?
Elige MySQLi si:
- Tu proyecto solo usará MySQL/MariaDB y no planeas cambiar de motor.
- Quieres aprovechar funciones específicas de MySQL o rendimiento muy fino con su API nativa.
- Mantienes código heredado procedimental que ya usa MySQLi y no justifica una migración.
Ventajas
- Sencillez para proyectos 100% MySQL.
- API procedimental o OO.
- Gran cobertura de funciones específicas de MySQL.
Desventajas
- Cero portabilidad a otros motores.
- Placeholders solo posicionales (a veces menos legibles).
- Manejo de errores más heterogéneo si no estandarizas.
¿Cuándo usar PDO?
Usa PDO si:
- Deseas portabilidad entre motores (o mantener abierta esa opción).
- Priorizas consistencia de API, excepciones y un estilo OO claro.
- Requieres placeholders nombrados para consultas complejas o plantillas legibles.
- Necesitas transacciones con API uniforme en varios motores.
Ventajas
- Portabilidad real: cambiar driver ≈ ajustar DSN y puntuales SQL específicos.
- Placeholders nombrados (más claridad en consultas largas).
- Excepciones y atributos globales de conexión sencillos (
ERRMODE_EXCEPTION,DEFAULT_FETCH_MODE). - Transacciones coherentes entre motores.
Desventajas
- Dependes de lo que expone cada driver (para features muy específicas quizá vuelvas a SQL nativo).
- Requiere pensamiento OO desde el inicio (no hay modo procedimental).
Seguridad: prepared statements obligatorios
Con MySQLi o PDO, evita la inyección SQL usando consultas preparadas.
No interpoles variables directamente en el SQL.
Ejemplo inseguro (NO hacerlo):
// ❌ Vulnerable
$user = $_GET['user'];
$sql = "SELECT * FROM users WHERE username = '$user'";
MySQLi (seguro):
$mysqli = new mysqli('localhost', 'user', 'pass', 'db');
$stmt = $mysqli->prepare("SELECT id, username FROM users WHERE username = ?");
$stmt->bind_param("s", $user);
$stmt->execute();
$res = $stmt->get_result();
$row = $res->fetch_assoc();
PDO (seguro con placeholders nombrados):
$pdo = new PDO('mysql:host=localhost;dbname=db;charset=utf8mb4','user','pass',[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
$stmt = $pdo->prepare("SELECT id, username FROM users WHERE username = :user");
$stmt->execute([':user' => $user]);
$row = $stmt->fetch();
Transacciones: patrón recomendado
MySQLi (OO):
$mysqli->begin_transaction();
try {
$stmt = $mysqli->prepare("UPDATE cuentas SET saldo = saldo - ? WHERE id = ?");
$stmt->bind_param("di", $monto, $idOrigen);
$stmt->execute();
$stmt = $mysqli->prepare("UPDATE cuentas SET saldo = saldo + ? WHERE id = ?");
$stmt->bind_param("di", $monto, $idDestino);
$stmt->execute();
$mysqli->commit();
} catch (Throwable $e) {
$mysqli->rollback();
// log error
}
PDO:
$pdo->beginTransaction();
try {
$stmt = $pdo->prepare("UPDATE cuentas SET saldo = saldo - :m WHERE id = :id");
$stmt->execute([':m'=>$monto, ':id'=>$idOrigen]);
$stmt = $pdo->prepare("UPDATE cuentas SET saldo = saldo + :m WHERE id = :id");
$stmt->execute([':m'=>$monto, ':id'=>$idDestino]);
$pdo->commit();
} catch (Throwable $e) {
$pdo->rollBack();
// log error
}
Tip: en PDO activa
ERRMODE_EXCEPTIONyDEFAULT_FETCH_MODE => FETCH_ASSOCal crear la conexión. En MySQLi, normaliza el manejo de errores y fetch con funciones consistentes en tu proyecto.
Rendimiento: ¿hay diferencia real?
Para la gran mayoría de aplicaciones CRUD y APIs, no notarás diferencias significativas si usas prepared statements correctamente. El mayor impacto de rendimiento está en diseño de índices, consultas SQL bien pensadas y pooling/persistencia de conexiones (o usar un proxy como ProxySQL). Elige la API por mantenibilidad y portabilidad, no por micro-benchmarks aislados.
Patrones de arquitectura
- Capa de Datos (DAO/Repository): encapsula MySQLi/PDO; tu dominio no debe “saber” qué extensión usas.
- Inyección de Dependencias: pasa el handler (
mysqlioPDO) al constructor de tus repositorios. - Errores centralizados: en PDO, captura excepciones en la capa DAO; en MySQLi, unifica
return codes/mensajes. - Migraciones a futuro: si hoy usas MySQLi, diseña tu DAO para poder migrar a PDO sin reescribir el dominio.
Decisión práctica
- Proyecto 100% MySQL/MariaDB, pequeño o heredado: MySQLi es válido y directo.
- Proyecto con miras a crecer, multi-motor, o buscas API uniforme y limpia: elige PDO.
- Equipo mixto o estándares de empresa: define un solo estilo (recomendación general: PDO con excepciones y placeholders nombrados).
Conclusión
MySQLi brilla en proyectos exclusivos de MySQL con necesidad de funciones nativas y entrada rápida. PDO te da consistencia, portabilidad y claridad a medio/largo plazo. Sin importar tu elección, usa siempre consultas preparadas, transacciones y manejo de errores robusto. Tu base de código será más segura y mantenible.
