Mostar la cantidad de procesos apache que se están ejecutando

Muchas veces necesitamos monitorear la cantidad de proceso de apache que hay ejecutandose para comprobar la salud de nuestro servidor y de esta forma saber si un servidor esta sobrecargado o no.
La cantidad de procesos Apache ejecutandose podríamos compararla contra la cantidad de procesos configurados en Apache/Httpd en la variable MaxClients.
MaxClients: Número máximo de conexiones que apache procesa simultáneamente.

Ejemplo de como contar el número de procesos Apache/httpd en ejecución

#CentOs, RHEL, Fedora
pgrep httpd | wc -l
#En debian o Ubuntu puede que el proceso no se llame httpd sinó apache2
pgrep apache2 | wc -l

Con el comando top o htop si lo tenemos instalado podemos fijarnos si el nombre del proceso es apache2 o httpd.
También podemos crear un script que monitoree la cantidad de procesos apache2/httpd y si la cantidad de procesos ejecutandose supera un umbral notificamos vía e-mail.

#Creamos el script
touch apache-monitor.sh

#Asignamos permisos de ejecución al script
chmod +x apache-monitor.sh

Abrimos el archivo con vim y le pegamos el siguiente código

vim apache-monitor.sh
#!/usr/bin/env bash
#En la variable APTHREADS cargamos la cantidad de procesos apache en ejecucion
APTHREADS=$(pgrep httpd | wc -l)
#Creamos una variable con el valor = al MaxClients
MCLIENTES=100;

#Ahora definimos el e-mail de notificacion
EMAILTO=tucorreo@gmail.com

#Validamos si la cantidad de procesos apache superan el umbral establecido
#si es así enviamos un mail de notificacion
(( $MCLIENTES < $APTHREADS )) &&\
echo "Cantidad de procesos httpd/apache2: $APTHREADS ( $(date +'%Y-%m-%d %T') )" |\
mail -s 'Atencion!!. La cantidad de procesos apache han superado el umbral de configuracion establecido' $EMAILTO

Una vez finalizada la creación del scrip, lo ejecutamos de la siguiente manera.

./apache-monitor.sh

Finalmente podríamos configurar para que nuestro script sea ejecutado cada x tiempo por el sistema (en el crontab) y con esto estariamos monitoreando los procesos apache de nuestro servidor Web.

Agregamos la llamada en nuestro crontab para que el script se ejecute cada 5 minutos

#En Ubuntu/Debian
vim /etc/crontab

#agregamos la siguiente línea
*/5 * * * * root /root/apache-monitor.sh

Formatear una fecha en MySQL al español

En este ejemplo mostraremos como formatear una fecha de un campo datetime, en este caso usaremos la funcion NOW() de MySQL para la demostración.
La idea es formatear una fecha para que se pueda usar en una vista, con el formato correcto para nuestro idioma, por ejemplo para el día de hoy el resultado debería ser (Martes 22 de Mayo). Primero establecemos el idioma para el resultado de nuestra consulta y posteriormente la formateamos.

SET lc_time_names = 'es_UY';
SELECT
CONCAT_WS(' ',
              CONCAT(UCASE(SUBSTRING(DAYNAME(a.MyDate), 1, 1)),LCASE(SUBSTRING(DAYNAME(a.MyDate), 2))),
              DATE_FORMAT(a.MyDate,'%d de '),
              CONCAT(UCASE(SUBSTRING(MONTHNAME(a.MyDate), 1, 1)),LCASE(SUBSTRING(MONTHNAME(a.MyDate), 2)))
            ) AS fecha_con_formato
FROM (SELECT NOW() AS MyDate) AS a;

Ver imagen de ejemplo:

Hacer un SPLIT en MySQL

En el siguiente código se presenta un ejemplo de como hacer un split en MySQL, en el ejemplo se hace uso de un sub-select donde a un texto que pretende ser una IP se le agrega un alias para poder ser usado por una consulta superior y para que nuestro ejemplo funcione tan solo ejecutando el código en cualquier cliente MySQL.
Sobre nuestra IP lo que queremos es dividirla por sus 4 octetos y devolverlo en cuatro culumans diferentes.

SELECT my_table.my_ip ,
    SUBSTRING_INDEX( my_table.my_ip , '.', 1 ) AS primer_octeto,
    SUBSTRING_INDEX(SUBSTRING_INDEX( my_table.my_ip , '.', 2 ),'.',-1) AS segundo_octeto,
    SUBSTRING_INDEX(SUBSTRING_INDEX( my_table.my_ip , '.', -2 ),'.',1) AS tercer_octeto,
    SUBSTRING_INDEX( my_table.my_ip , '.', -1 ) AS cuarto_octeto
FROM (SELECT '192.168.109.121' AS my_ip) AS my_table;

Ver imagen de ejemplo:
En este ejemplo se muestra como hacer un SPLIT en MySQL y se trabaja sobre una IP

Eliminar procesos MySQL que están en estado SLEEP por determinado tiempo

Con el presente script, monitoreamos los procesos MySQL que están en estado SLEEP por un tiempo mayor al configurado como aceptable.  Si encontramos un proceso que cumpla con esta regla lo eliminamos con la sentencia KILL ProcessID de MySql.

  1. Copiar el código en un archivo
  2. Darle permiso de ejecución
  3. Configurar datos de conexión MySql y notificación por e-mail.
  4. Crear un usuario que tenga permisos sobre las tablas que queremos trabajar
  5. Agendar la ejecución del script en el sistema.
$mysql_host = "127.0.0.1";
$mysql_user = "system";
$mysql_pass = "12345";

//Definimos el tiempo limite para una query en sleep in seconds
$mysql_query_sleep_limit = 250;

//Definimos contra que comando monitorearemos
$mysql_query_name = "Sleep";

//Definimos un array con el usuario/base a monitorear
$mysql_query_user_db_monitoring  = array(
					  array('myusuario','mydb')
					);

//Definimos las variables para el envio de e-mails
$NotifySystemFrom 	= "script-monitoring@mydominio.com";
$ArrNotifySystemTo 	= array("soporte@mydominio.com","otro@mydominio.com");
$NotifySystemSubject	= "Notificacion, Queryes in Sleep Killed!";

$SendNotification       = false;

$conn = mysql_connect($mysql_host,$mysql_user,$mysql_pass);

If($conn){
	$result = mysql_query("show full processlist",$conn);
	while( $Record = mysql_fetch_array($result) ){
 		$Ret[] = $Record;
	}
	if(count($Ret)>0){
		$HtmlToMail  = "<table  BGCOLOR='#FFE4B5'>";
		$HtmlToMail .= "<tr bgcolor='#FFD700'>";
		$HtmlToMail .= "<th align='left'>ID		</th>";
		$HtmlToMail .= "<th align='left'>User		</th>";
		$HtmlToMail .= "<th align='left'>Host		</th>";
		$HtmlToMail .= "<th align='left'>DB		</th>";
		$HtmlToMail .= "<th align='left'>Command	</th>";
		$HtmlToMail .= "<th align='left'>Time		</th>";
		$HtmlToMail .= "<th align='left'>State		</th>";
		$HtmlToMail .= "<th align='left'>Info		</th>";
		$HtmlToMail .= "<th align='left'>Action applied	</th>";
		$HtmlToMail .= "</tr>";
	
		while(list(,$val)=each($Ret)){
			if($val['Command'] == $mysql_query_name AND $val['Time']>=$mysql_query_limit AND in_array(array($val['User'],$val['db']),$mysql_query_user_db_monitoring)){
				//Eliminamos la query
				mysql_query("KILL $val[Id]",$conn);
				echo "Se elimina la consulta con ID:$val[Id]\n";
				$HtmlToMail .= "<tr>";
				$HtmlToMail .= "<td> $val[Id]			</td>";
				$HtmlToMail .= "<td> $val[User]			</td>";
				$HtmlToMail .= "<td> $val[Host]			</td>";
				$HtmlToMail .= "<td> $val[db]			</td>";
				$HtmlToMail .= "<td> $val[Command]	        </td>";
				$HtmlToMail .= "<td> $val[Time] 		</td>";
				$HtmlToMail .= "<td> $val[State]		</td>";
				$HtmlToMail .= "<td> $val[Info] 		</td>";
				$HtmlToMail .= "<td> PID Killed 		</td>";
				$HtmlToMail .= "</tr>";
				$SendNotification = true;
			}
		}
		
		$HtmlToMail .= "</table>";
		if($SendNotification){
		   SendMail($HtmlToMail);
		}
	}
}else{
	echo "Unable to connect - Mysql Error: ".mysql_error()."\n";
}

Function SendMail($Body){
    Global $NotifySystemFrom, $ArrNotifySystemTo, $NotifySystemSubject;
    //Definimos el header del mail de notificacion
    $Header = "From: $NotifySystemFrom \r\n";
    $Header .= "Mime-Version: 1.0 \r\n";
    $Header .= "Content-Type: text/html;charset=UTF-8";
    $NotyfyList = implode(",", $ArrNotifySystemTo);
    mail($NotyfyList,"$NotifySystemSubject [".date("d/M/Y H:i:s")."]",$Body, $Header);
}

Insertar datos en una tabla a partir de otra en MySQL

Hay veces que queremos volcar un grupo de datos en una tabla y el contenido se encuentra en otra tabla, entonces lo que tenemos que hacer es levantar el contenido de la tabla que contiene los datos a copiar he insertarlos en la nueva tabla.

A continuación la sintaxis de como hacerlo.

--Ejemplo:
INSERT INTO sales_2008 (date, seller_id, buyer_id, status, ip)
       SELECT 
          date, seller_id, buyer_id, status, ip
       FROM   sales
       WHERE  sales_date >= '2008-01-01 00:00:00' AND sales_date < '2009-01-01 00:00:00';
--

Crear una tabla a partir de otra en MySQL con y sin su contenido

Hay veces que queremos crear una tabla igual a otra con parte de su contenido, un caso sería cuando tenemos una tabla de transacciones que tiene varios millones de registros y las consultas para los reportes demoran, lo que hacemos en estos casos (después de estar seguros que están creados los indices) es crear tablas de Histórico, o sea que separaríamos el contenido de la tabla de transacciones en varias tablas, puede ser una tabla por año, por semestre, por trimestre o mensual, todo depende la cantidad de registros que generemos por día. En MySQL por mi experiencia con tablas bien optimizadas consultar datos sobre una tabla de mas de 5 millones de registros tiende a tornarse en respuestas lentas.

Por eso una solución es separar el contenido en fracciones y en la lógica de nuestra aplicación resolver según los filtros de nuestros informes sobre las transacciones para saber si consultamos sobre la tabla corriente, una tabla de histórico o varias tablas usando la cláusuala UNION en nuestras consultas SELECT para obtener todos los registros de ese periodo.

Por lo general el set de datos que se suelen consultar en sistemas son de un periodo corto, ya que la información se usa por lo general para cerrar balances mensuales, trimestrales, saber cuando dinero se está ganando, etc; depende de la empresa y el rubro, pero generalmente las personas que manejan esta información quieren tener toda la información disponible y que sus reportes sean rápidos y eficientes.

Con esta solución lo que hacemos es mejorar el tiempo de respuestas para el 99% de las consultas, ya que las consultas de histórico se usan para detectar algún patrón o para analizar sobre algún problema presentando en el negocio.

A continuación presento la sintaxis de como crear una tabla a partir de otra (Tabla de Histórico) + varios ejemplos.

-- Ejemplo 1: Crear una tabla a partir de otra sin su contenido, solo la estructura.
-- Sintaxis:
CREATE TABLE NombreNuevaTabla LIKE NombreTablaOrigen ;
CREATE TABLE sales_2008 LIKE sales;
--

Después de ejecutar esta sentencia SQL podremos ver que se crea una nueva tabla llamada sales_2008 con la misma estructura que sales pero vacía. Podremos llenarla posteriormente con una consulta del tip INSERT INTO SELECT.

Tal ves lo que queramos es crear una tabla identica a la original, puede ser para respalo, con todo los datos a la fecha;

-- Ejemplo 2: Crear una tabla a partir de otro con la copia de todo su contenido
-- Sintaxis:
CREATE TABLE NombreNuevaTabla SELECT * FROM NombreTablaOrigen;
CREATE TABLE sales_2009 SELECT * FROM sales;
--

En el siguiente ejemplo se presenta la forma de crear una tabla a partir de otra con parte de su contenido, este caso podría ser para crear una tabla de histórico del año 2010.

-- Ejemplo 3: Crear una tabla de histórico limitando a un set de datos con un filtro por fecha.
-- Sintaxis:
CREATE TABLE NombreNuevaTabla SELECT * FROM NombreTablaOrigen WHERE Field_Date >= '2010-01-01 00:00:00' AND Field_Date < '2011-01-01 00:00:00'; -- Campo tipo datetime
CREATE TABLE sales_2010 SELECT * FROM sales WHERE sales_date >= '2010-01-01 00:00:00' AND sales_date < '2011-01-01 00:00:00'; -- Campo tipo datetime -- 

En el próximo ejemplo veremos la forma de crear una tabla a partir de otra, con parte de su contenido y especificando que campos tendrá la nueva tabla.

 
-- Ejemplo 4: Crear una tabla de histórico limitando a un set de datos con un filtro por fecha y especificando los campos que tendrá 
-- Sintaxis: 
CREATE TABLE NombreNuevaTabla SELECT Campo1, Campo2, Campo3, CampoN FROM NombreTablaOrigen WHERE `Field_Date` >= '2010-01-01 00:00:00' AND `date` < '2011-01-01 00:00:00'; 
CREATE TABLE sales_2010 SELECT id,date,external_id,status,ip FROM sales WHERE sales_date >= '2010-01-01 00:00:00' AND sales_date < '2011-01-01 00:00:00';
--Para ver los campos de la nueva tabla
DESC sales_2010;
--