Programación y sistemas

Cassandra y PHP para desarrolladores SQL: PHPCassa (I)

Logo Cassandra

¡Por fin llego el día! ¡Hoy toca programar!

Para ello vamos a utilizar el cliente de alto nivel PHPCassa que nos ahorrará bastante tiempo para trabajar con Cassandra. Puedes descargarlo desde https://github.com/thobbs/phpcassa.

Ubicamos phpCassa en una carpeta dentro de nuestro servidor web y creamos dos archivos. Yo los he llamado test.php y cassandra.php, El segundo será una clase que nos ahorrará algunas lineas de código con las tareas habituales. El primero lo utilizaré para testear la clase y sus respectivos métodos.

Bien empecemos.

 

PRIMEROS PASOS

En primer lugar necesitamos incluir dos archivos de phpCassa en cassandra.php:

[codesyntax lang=»php»]

<?php

// CLASES NECESARIAS PARA CONECTAR CON CASSANDRA
require_once('phpcassa/connection.php');
require_once('phpcassa/columnfamily.php');

[/codesyntax]

El primero de ellos dispone de los métodos para conectar con Cassandra a través de Thirft; el segundo tiene los métodos para trabajar con las column families.

Justo debajo creamos la clase:

[codesyntax lang=»php»]

<?php

class cassandra {

}

[/codesyntax]

 

CONECTANDO

Para realizar la conexión a la BD, phpCassa nos pide tan solo un dato obligatorio: el nombre del keyspace con el que vamos a trabajar. También nos permite añadir las ips de los nodos con los que queramos trabajar, aunque por defecto su valor es localhost. Por tanto podemos crear un constructor como el siguiente:

[codesyntax lang=»php»]

<?php

  function __construct($keyspace, $nodos = array('localhost')){
    if (!empty($keyspace)){
      $this->conexion = new ConnectionPool($keyspace, $nodos);
    }else{
      print "El keyspace está vacío";
    }
  }

[/codesyntax]

Este constructor nos permite indicarle a la clase el keyspace con el que queremos trabajar y la ip o nombre de servidor donde se encuentre nuestra instancia de Cassandra. Por defecto el puerto al que se va a conectar es el 9160. Si hubieses configurado Cassandra para escuchar en otro puerto deberás especificarlo: localhost:9160.

Este código nos crea una variable llamada conexion que contendrá el objeto ConnectionPool, necesario para todas las consultas a la BD

 

GUARDANDO REGISTROS

Ahora procedemos a crear el método que guardará los registros. Para ello primero crearemos un objeto ColumnFamily al que le pasaremos el nombre de la column family en la que queremos trabajar:

[codesyntax lang=»php»]

<?php

  public function guardar($columnFamily, $key, $data = array()){
    try {
      $column_family = new ColumnFamily($this->conexion, $columnFamily);
      $column_family->insert($key, $data);
      return true;
    }catch(Exception $e){
     return false;
    }
  }

[/codesyntax]

Este método nos devolverá true si se ha guardado correctamente el registro, o false en caso de error.
Como puedes ver, al objeto ColumnFamily se le pasa como primer parámetro el objeto ConnectionPool que creamos en el constructor, después se le pasa el nombre de la column family.
Una vez creado el objeto ColumnFamily ya podemos utilizar el método insert para guardar el registro pasándole como parámetros la key y un array con las columnas y sus respectivos valores.

 

RECUPERANDO REGISTROS

Recuperar registros de Cassandra es algo más complejo que guardarlos:

[codesyntax lang=»php»]

<?php

  public function obtener($columnFamily, $key, $super_column=NULL, $columns= NULL, $range_start = "", $column_count = 100, $invertir_orden=false){

      try{
          $column_family = new ColumnFamily($this->conexion, $columnFamily);
          $result = $column_family->get($key, $columns, $range_start, "", $invertir_orden, $column_count, $super_column);
      }catch(Exception $e){
          return false;
      }

      return $result;

  }

[/codesyntax]

Este método tiene algunos parámetros más que al guardar, pero tienen su razón de ser:

$columnFamily: Nombre de la column family donde buscar.
$key: Clave del registro donde buscar.
$super_column: Key o clave de la super column que se debe buscar. No es obligatorio. Por defecto NULL.
$columns: Columnas a buscar. No es obligatorio. Por defecto NULL.
$range_start: Key por la que Cassandra debe empezar a recuperar registros. No es obligatorio. Por defecto «».
$column_count: Número de registros a obtener, Por defecto 100.
$invertir_orden: Invierte el orden en el que se recuperar los registros. De mayor a menor o viceversa, de la A-Z o viceversa, etc.

Como ves tienes bastantes opciones para recuperar registros. Este método devuelve un array con los registros.

 

PROBANDO LA CLASE

Vamos a probar el código a ver que tal funciona. Nos vamos al archivo test.php y escribimos el siguiente código:

[codesyntax lang=»php»]

<?php

include_once "cassandra.php";

$cass = new cassandra('my_keyspace', array('192.168.1.10'));

$data = array(
    'nombre' => 'pepito',
    'ciudad' => 'Madrid',
    'vehiculo' => 'coche',
);

if ( $cass->guardar('my_column_family_standard', 1, $data)){
  print "El registro se ha guardado correctamente<br />";
}else{
  print "Error al guardar el registro<br />";
}

$result = $cass->obtener('my_column_family_standard', 1);

print_r($result);
?>

[/codesyntax]

Explico un poco el código anterior:

  1. Incluimos la clase en el archivo.
  2. Creamos el objeto indicándole el keyspace con el que trabajar (my_keyspace), y dentro de un array, la ip donde se encuentra en nodo de Cassandra. Si tuvieses Cassandra instalado en localhost no sería necesario indicar el segundo parámetro.
  3. Creamos el array que contendrá los datos a guardar.
  4. Creamos un condicional que guardará los datos en Cassandra y nos indicará si se han guardado correctamente o ha habido algún error.
  5. Guardamos en una variable ($result) los registros que recuperamos a través del método obtener del objeto $cass. A este método le indicamos la column family a buscar y la key a obtener.
  6. Mostramos los registros.

Si todo ha salido correctamente verás un texto como el siguiente cuando ejecutes el script:

El registro se ha guardado correctamente
Array ( [ciudad] => Madrid [nombre] => pepito [vehiculo] => coche )

Si te aparecen más registros es posible que no vaciases las column family con la que trabajas. Simplemente ves a cassAdmin, entra en la column family y pulsa en Truncate Column Family. Así eliminarás todos los datos de la column family.

 

ACTUALIZAR UN REGISTRO

Ahora que ya funciona nuestra clase podemos ampliarla con nuevas características.
Aunque pienses que para actualizar un registro es necesario un nuevo método, en Cassandra no es necesario, simplemente utilizaremos el método guardar indicandole la key a modificar y el array con los datos a guardar.

Justo debajo del código que ya tenemos en el archivo test.php escribimos lo siguiente:

[codesyntax lang=»php»]

<?php

$data = array(
    'ciudad' => 'Barcelona',
);

if ( $cass->guardar('my_column_family_standard', 1, $data)){
  print "El registro se ha guardado correctamente<br />";
}else{
  print "Error al guardar el registro<br />";
}

$result = $cass->obtener('my_column_family_standard', 1);

print_r($result);

[/codesyntax]

Como observarás el array no tiene todos los datos de la key que vamos a modificar, ya que no es necesario, tan solo pasaremos los datos que queremos actualizar. El resultado de este código es el siguiente:

El registro se ha guardado correctamente
Array ( [ciudad] => Madrid [nombre] => pepito [vehiculo] => coche )
El registro se ha guardado correctamente
Array ( [ciudad] => Barcelona [nombre] => pepito [vehiculo] => coche )

La columna ciudad a cambiado, el resto sigue igual, la razón está en que cuando le pasas a Cassandra una key que ya existe, lo que hace es actualizar las columnas que se correspondan con las keys del array. Si en ese array hubiera keys que no existen como columnas en el registro, Cassandra simplemente las crearía nuevas.

 

GUARDANDO UNA SUPER COLUMN

Aquí tampoco nos hace falta crear un nuevo método ya que nos sirve perfectamente el método guardar.

Añadimos el siguiente código a nuestro archivo test.php:

[codesyntax lang=»php»]

<?php

$data = array('vecino_1' => array(
    'nombre' => 'pepito',
    'ciudad' => 'Madrid',
    'vehiculo' => 'coche',
));

if ( $cass->guardar('my_column_family_super', 1, $data)){
  print "El registro se ha guardado correctamente<br />";
}else{
  print "Error al guardar el registro<br />";
}

$result = $cass->obtener('my_column_family_super', 1);

print_r($result);

[/codesyntax]

El único cambio además del nombre de la column family donde vamos a guardar el registro, se encuentra en el array con los datos. Este tiene un elemento que es la key de la super column, y como valor, el array de las columns a guardar.

Si ejecutamos el código anterior nos devolverá lo siguiente:

El registro se ha guardado correctamente
Array ( [vecino_1] => Array ( [ciudad] => Madrid [nombre] => pepito [vehiculo] => coche ) )

 

ACTUALIZANDO UN SUPER COLUMN

La actualización de una super column se limita a las columnas que contienen los valores del registro no a su key. Si modificáramos la key , lo que en realidad estaríamos haciendo sería crear una nueva super column. Añadimos el siguiente código a nuestro archivo test.php:

[codesyntax lang=»php»]

<?php

$data = array('vecino_1' => array(
    'nombre' => 'pepito',
    'ciudad' => 'Barcelona',
    'vehiculo' => 'coche',
));

if ( $cass->guardar('my_column_family_super', 1, $data)){
  print "El registro se ha guardado correctamente<br />";
}else{
  print "Error al guardar el registro<br />";
}

$result = $cass->obtener('my_column_family_super', 1);

print_r($result);

[/codesyntax]

Y el resultado es:

El registro se ha guardado correctamente
Array ( [vecino_1] => Array ( [ciudad] => Barcelona [nombre] => pepito [vehiculo] => coche ) )

 

ELIMINAR REGISTROS

Sí, aquí toca crear un nuevo método para nuestra clase en cassandra.php, aunque tampoco nos vamos a matar programando:

[codesyntax lang=»php»]

<?php

  public function eliminar($columnFamily, $key, $super_column = null){

    try{
      $column_family = new ColumnFamily($this->conexion, $columnFamily);
      $column_family->remove($key, null, $super_column);
      return true;
    }catch(Exception $e){
      return false;
    }
  }

[/codesyntax]

El método es bastante sencillo: se le pasan tres parámetros que ya conocemos.

Dentro del método se crea un objeto ColumnFamily y se utiliza el método remove para eliminar el registro pasándole la key correspondiente, y si fuese una super column, la key de este.
Observarás que el segundo parámetro del método remove tiene un valor null, en él se pueden añadir como array los nombres de las columnas a eliminar. Como no es habitual ese proceso he creído adecuado dejarlo como null.

Una vez añadido esté método a nuestra clase modificamos el archivo test.php con el siguiente código:

[codesyntax lang=»php»]

<?php

if ( $cass->eliminar('my_column_family_standard', 1)){
  print "El registro se ha eliminado correctamente<br />";
}else{
  print "Error al eliminar el registro<br />";
}

$result = $cass->obtener('my_column_family_standard', 1);

print_r($result);

[/codesyntax]

Y el resultado es:

El registro se ha eliminado correctamente

 

De momento con esto es suficiente, dejo en tus manos estos métodos para que juegues con ellos todo lo que quieras y te familiarices con phpCassa.

En el próximo post trataré las Counter Columns que nos servirán para contar registros, los Secondary Index que nos permitirán realizar búsquedas por columnas concretas, y algunas cosas más.

10 Comentarios

  • jose 15/05/2012

    Muy bueno el post!
    Me ha surgido una duda y a ver si me puedes ayudar,Hay alguna manera de ordenar los registros o de saber cual ha sido el ultimo registro insertado?

  • Mikel 15/05/2012

    Hola José, sí, hay varias formas de ordenar los registros, de hecho utilizando UTF-8 en el campo «Comparator Type» al crear la column family te ordena los registros en orden alfabético. Pero si quieres hacer algo parecido a un autoincrement de MySQL es un engorro. Para ello Cassandra proporciona varios generadores de ids basados en el tiempo (PHPCassa utiliza la función microtime() para generarlos). Las veces que he probado estos ids me han dado problemas al ordenar los registros, de todas formas haré varias pruebas para un próximo post sobre este tema.

    Por mi parte no utilizo las ids que genera PHPCassa sino un id personalizado basado en segundos.microsegundos obtenido con microtime(), de esta forma al añadirlo a la key del registro Cassandra lo ordena correctamente.

    Esta key tiene un pequeño problema, que en un mismo microsegundo podría haber varios registros (depende de la cantidad de datos a guardar en la BD) para guardar, por tanto se pueden añadir varios elementos más para evitar este problema, como el id del usuario.

    Saludos.

  • jose 15/05/2012

    Hola mikel,

    Una vez que los tienes ordenados,se puede saber cual ha sido el ultimo registro insertado?

    Gracias por la respuesta.

  • Mikel 15/05/2012

    Hola José, sí, tendrás que cambiar el parámetro $invertir_orden del método obtener a true.

  • Antonio Espadas 17/05/2012

    Buenas mikel,

    He estado trasteando con phpcassa gracias a tus ejemplos.
    Tenia entendido que cassandra al ser noSql era mas adecuada para manejar grandes cantidades de datos.Pero al intentar mostrar muchos registros no funciona bien,me muestra solo unos 2500 y salta un timeout exception a los 30 segundos,tengo como unos 10000 en la base de datos que tampoco son demasiados a mi entender.
    Podrias decirme a que es debido.

    Gracias

  • Mikel 17/05/2012

    Hola Antonio, es probable que sea debido a falta de memoria RAM. Cassandra necesita como mínimo 1GB de RAM para funcionar, pero para ponerlo en producción necesitas mínimo entre 2 y 4GB de RAM.

    Cuando a Cassandra se le piden datos, la BD crea una memtable para guardar todos los datos, y desde ahí enviarlos a la aplicación. Si esa memtable no tiene suficiente RAM disponible lo primero que hará Cassandra será eliminar las memtables que hubiera creado anteriormente, hasta llegar a un punto en el que la consulta necesite más RAM de la disponible, en ese momento entra en un estado catatónico sin enviar respuesta a la aplicación hasta que se cuelga Cassandra.

    Además de esto está el problemita del recolector de basura de Java que consume bastante y en este tipo de consultas da bastante por culo (con perdón).

    Hay varios archivos de configuración en /etc/cassandra para poder trastear con la RAM, entre ellos cassandra-env.sh es el que ajusta la RAM tanto del recolector de basura de Java como de las memtables.

    Por último, si tienes bastante RAM libre es posible que la limitación del tiempo de ejecución de scripts de PHP te esté dando problemas. Aquí puedes o bien subirle el límite de tiempo de ejecución entrando en el archivo de configuración de PHP o bien utilizar set_time_limit(0); en tu script PHP para saltarte la limitación sin tocar la configuración.

    Ahora recuerdo que una prueba que realicé con Cassandra fue la de recuperar de Twitter los tweets del hashtag de los premios Goya, guardó más de 300.000 tweets en poco más de hora y media que estuvo funcionando hasta que se colgó, al ir a recuperar esos datos, Cassandra no era capaz de localizar suficiente memoria para mostrar todos los tweets de una sola vez. Haciendo alguna prueba más llegue a la conclusión que limitando las consultas a 2000 tweets se podían recuperar correctamente sin saturar la RAM.

    Puedes consultar otras fuentes en internet, las que yo consulte en su momento, cuando hice la prueba anterior, todos recomendaban subir RAM al servidor.

    Espero haberte ayudado, o al menos haberte orientado xD.

    Saludos.

  • Angeles 28/03/2016

    Hola buenas tardes
    Disculpe soy nueva usando cassandra, me podria decir como hacer un registro desde una vista en php
    Gracias por su atencion
    Espero su respuesta

  • Mikel 28/03/2016

    Buenas tardes Ángeles, gracias por comentar.

    Si estas usando una arquitectura MVC (Modelo-Vista-Controlador) para tu proyecto, lo ideal es que los datos de la vista (por ejemplo desde un formulario) los pases al controlador, y ahí crear el código para guardar el registro.

    Un saludo.

  • gloria 03/04/2016

    hola, buen tutorial pero no tendras algun video tutorial por que como no tengo ni idea de java, no se ni como empezar. Uso netbeans con cassandra pero no se ni realizar la conexion con la base de datos

  • Mikel 04/04/2016

    Buenos días Gloria, lo siento no tengo ningún videotutorial.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Descubre más desde Interadictos

Suscríbete ahora para seguir leyendo y obtener acceso al archivo completo.

Seguir leyendo