Esecuele Sin Fronteras

SQL Server, Reporting Services y Biztalk Server
  • Esecuele Sin Fronteras

    ¿Por qué hay un retraso en la ejecución de mi subscripción de Reporting Services?

    • 0 Comments

     

    Un retraso en el envío de subscripciones podría ser debido a que las theads del procesamiento del servicio de Reporting Services no estén procesando los eventos a tiempo, quizás porque estén sobrecargadas y no les de tiempo a ser ejecutadas tan pronto como llegan. En este caso, los Jobs se ejecutarían en el SQL Agent pero las tablas “Events” y “Notifications” mostrarían un numero muy alto de entradas. Como el número de threads es fijo, no se puede incrementar, entonces, convendría averiguar por qué no se están procesando a tiempo, o acumulando.

    • Primero podríamos ejecutar la consulta utilizada por el informe de la subscripción y comprobar si 6se ejecuta en un tiempo razonable respecto a la frecuencia en que se ejecutan las subscripciones. Por ejemplo, podríamos llegar a una situación parecida en el caso de que la subscrición/Snapshot se ejecutase cada media hora y el informe tardase 1 hora en ejecutarse. Esto provocaría que varias instancias del informe se ejecutasen simultáneamente y llegasen a ocupar todas las threads, impidiendo ejecutar nada más.

     

    • También podemos observar, si todo funciona de manera razonable y no hay indicaciones de que el punto anterior ocurra, es que el problema ocurre a ciertas horas del día (por ejemplo a las 8 de la mañana). Cuando se programan muchas subscripciones a la vez, y lleva un tiempo en completarse, el procesamiento se puede retrasar. En este caso se aconseja espaciar las subscripciones, o añadir un segundo servidor en scale out (NLB) para trabajar en paralelo.

     

    • Otra posibilidad sería, si las subscripciones son por email, es que las subscripciones se procesen a tiempo en Reporting Services pero que haya un retraso en el servidor de correo utilizado. En ese caso, convendría hacer pruebas ejecutando las subscripciones depositando el informe en un directorio de recogida, y ver si se procesan a tiempo. Podrás encontrar más información sobre como configurarlo en el siguiente blog:

    El email de mi subscripción no llega al destinatario

    http://blogs.msdn.com/b/esecuelesinfronteras/archive/2009/10/19/el-email-de-mi-subscripci-n-no-llega-al-destinatario.aspx

     

    Como ves en el siguiente blog, hay un par de consultas que se pueden utilizar para obtener la información sobre las subscriciones de las tablas de la base de datos de ReportServer, y así poder hacer un mejor diagnostico:

    1. Primera consulta:

    select

    'SubnDesc'= s.Description,

    'SubnOwner= us.UserName,

    'LastStatus'= s.LastStatus,

    'LastRun'= s.LastRunTime,

    'ReportPath'= c.Path,

    'ReportModifiedBy'= uc.UserName,

    'ScheduleId'= rs.ScheduleId,

    'SubscriptionId'= s.SubscriptionID

    from

    ReportServer.dbo.Subscriptions s

    join

    ReportServer.dbo.Catalog c on c.ItemID = s.Report_OID

    join

    ReportServer.dbo.ReportSchedule rs on rs.SubscriptionID = s.SubscriptionID

    join

    ReportServer.dbo.Users uc on uc.UserID = c.ModifiedByID

    join

    ReportServer.dbo.Users us on us.UserID = s.OwnerId

    Join

    msdb.dbo.sysjobs j on j.name = CONVERT(nvarchar(128),rs.ScheduleId)

     

    2. Segunda consulta:

    select

    'Report'= c.Path,

    'Subscription'= s.Description,

    'SubscriptionOwner'= uo.UserName,

    'SubscriptionModBy'= um.UserName,

    'SubscriptionModDate'= s.ModifiedDate,

    'ProcessStart'= dateadd(hh,DATEDIFF(hh,Getutcdate(),Getdate()),n.ProcessStart),

    'NotificationEntered'= dateadd(hh,DATEDIFF(hh,Getutcdate(),Getdate()),n.NotificationEntered),

    'ProcessAfter'= dateadd(hh,DATEDIFF(hh,Getutcdate(),Getdate()),n.ProcessAfter),

    n.Attempt,

    'SubscriptionLastRunTime'= dateadd(hh,DATEDIFF(hh,Getutcdate(),Getdate()),n.SubscriptionLastRunTime),

    n.IsDataDriven,

    'ProcessHeartbeat'= dateadd(hh,DATEDIFF(hh,Getutcdate(),Getdate()),n.ProcessHeartbeat),

    n.Version,

    n.SubscriptionID

    from

    Notifications n

    join

    Subscriptions s on n.SubscriptionID = s.SubscriptionID

    join

    Catalog c on c.ItemID = n.ReportID

    join

    Users uo on uo.UserID = s.OwnerID

    join

    Users um on um.UserID = s.ModifiedByID

     

    Más información en:

    Dean Kalanquin's Reporting Services Blog

    http://blogs.msdn.com/b/deanka/

     

     

    Maria Esteban

    Reporting Services Support Engineer

  • Esecuele Sin Fronteras

    Rendimiento de proveedores de acceso – Parte 2 Nativo

    • 0 Comments

    En la anterior entrada hicimos una breve comparación de rendimiento de los diferentes proveedores de acceso bajo código manejado, esta vez vamos a enfrentarlos en el mundo nativo y ver cual sale vencedor.

    OleDB

    Como en el articulo anterior empiezo pegando el código usado para la prueba, es el siguiente:

    printf("\nNative code and OLEDB\n");
    //OLEDB
    HRESULT hr;
    IDBInitialize* pIDBInitialize = NULL;
    IDBProperties* pIDBProperties = NULL;
    IDBCreateSession* pIDBCreateSession = NULL;
    IDBCreateCommand* pIDBCreateCommand = NULL;
    ICommandText* pICommandText = NULL;
    ICommandProperties* pICommandProperties = NULL;


    DBPROPSET dbPropSet;
    DBPROP dbProp[5];

    //Fire up COM
    hr = CoInitialize(0);

    for (int i = 0; i<10; i++)
    {
        //Create provider instance
        hr = CoCreateInstance(CLSID_SQLNCLI10, NULL, CLSCTX_INPROC_SERVER, IID_IDBProperties, (void **) &pIDBProperties);

        //Setup properites
        dbProp[0].dwPropertyID = DBPROP_INIT_DATASOURCE;
        dbProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
        dbProp[0].colid = DB_NULLID;
        V_VT(&(dbProp[0].vValue)) = VT_BSTR;
        V_BSTR(&(dbProp[0].vValue)) = SysAllocString(L"PGAVELA-02\\KAWORU");

        dbProp[1].dwPropertyID = DBPROP_AUTH_INTEGRATED;
        dbProp[1].dwOptions = DBPROPOPTIONS_REQUIRED;
        dbProp[1].colid = DB_NULLID;
        V_VT(&(dbProp[1].vValue)) = VT_BSTR;
        V_BSTR(&(dbProp[1].vValue)) = SysAllocString( L"SSPI" );


        dbProp[2].dwPropertyID = DBPROP_INIT_CATALOG;
        dbProp[2].dwOptions = DBPROPOPTIONS_REQUIRED;
        dbProp[2].colid = DB_NULLID;
        V_VT(&(dbProp[2].vValue)) = VT_BSTR;
        V_BSTR(&(dbProp[2].vValue)) = SysAllocString( L"PerfTest" );

        dbPropSet.rgProperties = dbProp;
        dbPropSet.cProperties = 3;
        dbPropSet.guidPropertySet = DBPROPSET_DBINIT;

        hr = pIDBProperties->SetProperties(1, &dbPropSet);

        SysFreeString( V_BSTR(&(dbProp[0].vValue)) );
        SysFreeString( V_BSTR(&(dbProp[1].vValue)) );
        SysFreeString( V_BSTR(&(dbProp[2].vValue)) );

        //Create the Session and Command
        hr = pIDBProperties->QueryInterface( IID_IDBInitialize, (void**) &pIDBInitialize);

        hr = pIDBInitialize->Initialize();

        hr = pIDBInitialize->QueryInterface( IID_IDBCreateSession, (void**) &pIDBCreateSession );

        hr = pIDBCreateSession->CreateSession(NULL, IID_IDBCreateCommand, (IUnknown **) &pIDBCreateCommand);

        hr = pIDBCreateCommand->CreateCommand(NULL, IID_ICommandText, (IUnknown **) &pICommandText);

        //Setup Command Properties
        pICommandText->QueryInterface( IID_ICommandProperties, (void **) &pICommandProperties );

       dbPropSet.rgProperties       = dbProp;
       dbPropSet.cProperties        = 5;
       dbPropSet.guidPropertySet    = DBPROPSET_ROWSET;

       hr = pICommandProperties->SetProperties( 1, &dbPropSet);

       pICommandProperties->Release();
       pICommandProperties=NULL;

       IRowset* pIRowset = NULL;
       IAccessor* pIAccessor = NULL;

       wchar_t* pData = NULL;
       DBCOUNTITEM cRowsObtained = 0;
       ULONG cCount = 0;

       HROW* pRows = new HROW[1000000];
       HACCESSOR hAccessor = 0;
       DBBINDING Bind[1];
      
       hr = pICommandText->SetCommandText(DBGUID_SQL, L"SELECT TOP 5000000 * FROM HashTable");
       startTicks = GetTickCount();
       hr = pICommandText->Execute(NULL, IID_IRowset, NULL, NULL, (IUnknown**)&pIRowset);
     
       //Bind data
        Bind[0].dwPart      = DBPART_VALUE;
        Bind[0].eParamIO    = DBPARAMIO_NOTPARAM;
        Bind[0].iOrdinal    = 1;
        Bind[0].pTypeInfo   = NULL;
        Bind[0].pObject     = NULL;
        Bind[0].pBindExt    = NULL;
        Bind[0].dwFlags     = 0;
        Bind[0].dwMemOwner  = DBMEMOWNER_CLIENTOWNED;
        Bind[0].obLength    = 0;
        Bind[0].obStatus    = 0;
        Bind[0].obValue     = 0;
        Bind[0].cbMaxLen    = 500;
        Bind[0].wType       = DBTYPE_WSTR;
        Bind[0].bPrecision  = 0;
        Bind[0].bScale      = 0;

        hr = pIRowset->QueryInterface(IID_IAccessor, (void**)&pIAccessor);

        hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, Bind, 0, &hAccessor, NULL);

        pData = new wchar_t[500];

        do
        {
            //Get Next 10 rows
            hr = pIRowset->GetNextRows(NULL,0,10,&cRowsObtained, &pRows);

            for (int cCount =0; cCount < cRowsObtained; cCount++)
            {
                hr = pIRowset->GetData(pRows[cCount], hAccessor, pData);
            }
            pIRowset->ReleaseRows(cRowsObtained,pRows,NULL,NULL,NULL);

        }while(cRowsObtained != 0);
        endTicks = GetTickCount();
        totalTicks = endTicks - startTicks;
        printf("Round %d Time: %d ms\n",i,totalTicks);
        totalOLEDB += totalTicks;
       
        //Release all objects
        pIAccessor->Release();
        pIRowset->Release();
        pICommandText->Release();
        pIDBCreateCommand->Release();
        pIDBCreateSession->Release();
        pIDBInitialize->Release();
        pIDBProperties->Release();
    }
    printf("Avg Time: %d ms\n",totalOLEDB/10);

    Como podemos ver hacemos lo mismo que con el código manejado, diez iteraciones y luego obtenemos la media, los resultados son los siguientes:

    Native code and OLEDB
    Round 0 Time: 23743 ms
    Round 1 Time: 24165 ms
    Round 2 Time: 16068 ms
    Round 3 Time: 23385 ms
    Round 4 Time: 22870 ms
    Round 5 Time: 24445 ms
    Round 6 Time: 24383 ms
    Round 7 Time: 26395 ms
    Round 8 Time: 23572 ms
    Round 9 Time: 23822 ms
    Avg Time: 23284 ms

    Como podemos ver el tiempo medio es de 23,3 segundos una mejora importante respecto a la version de .NET pero aún así más lento que SQLClient.

    ODBC 

    Y por último tenemos ODBC, a continuación pego el código utilizado:

            SQLRETURN ret;
        SQLHDBC dbc;
        SQLHENV env;
        SQLHSTMT stmt;
        DWORD startTicks;
        DWORD endTicks;
        DWORD totalTicks;
        SQLWCHAR texto[500];
        wchar_t resultado[500];

        DWORD totalODBC = 0;
        DWORD totalOLEDB = 0;

        SQLINTEGER cursorOptions = SQL_CO_FFO;
        printf("Native code and ODBC\n");
        for (int i=0; i<10;i++)
        {
            SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&env);
            SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
            SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
            ret = SQLDriverConnect(dbc, NULL, L"Driver={SQL Server Native Client 10.0};Server=PGAVELA-02\\KAWORU;Database=PerfTest;Trusted_Connection=yes", SQL_NTS,NULL,0,NULL,SQL_DRIVER_COMPLETE);
            if (SQL_SUCCEEDED(ret))
            {
                SQLAllocHandle(SQL_HANDLE_STMT, dbc,&stmt);
                SQLPrepare(stmt, L"SELECT TOP 5000000 * FROM HashTable", SQL_NTS);
                SQLSetStmtAttr(stmt, SQL_SOPT_SS_CURSOR_OPTIONS,&cursorOptions,NULL);
                SQLBindCol(stmt, 1, SQL_C_WCHAR, texto, 500, NULL);
                startTicks = GetTickCount();
                SQLExecute(stmt);
                while (SQL_SUCCEEDED(ret = SQLFetch(stmt)))
                {
                    wsprintf(resultado,texto);
                }
                endTicks = GetTickCount();
                totalTicks = endTicks - startTicks;
                printf("Round %d Time: %d ms\n",i,totalTicks);
                totalODBC += totalTicks;
                SQLDisconnect(dbc);
                SQLFreeHandle(SQL_HANDLE_STMT, stmt);
                SQLFreeHandle(SQL_HANDLE_DBC, dbc);
                SQLFreeHandle(SQL_HANDLE_ENV, env);
            }
        }

        printf("Avg Time: %d ms\n",totalODBC / 10);

    A continuación los tiempos:

    Native code and ODBC
    Round 0 Time: 10686 ms
    Round 1 Time: 10452 ms
    Round 2 Time: 10343 ms
    Round 3 Time: 10436 ms
    Round 4 Time: 8939 ms
    Round 5 Time: 11107 ms
    Round 6 Time: 10327 ms
    Round 7 Time: 9718 ms
    Round 8 Time: 10406 ms
    Round 9 Time: 10686 ms
    Avg Time: 10310 ms

    Wow! 10 segundos, bastante más rápido que SQLClient. Por tanto en nativo no hay color, ¡ODBC es el camino a seguir!

    Conclusiones

    Como podemos ver OleDB es el proveedor más lento de todos accediendo a SQL Server, además teniendo en cuenta que se considera deprecado no se recomienda usarlo, en aquellos desarrollos que ya tengamos os recomendamos cambiar a ODBC o SQLClient(Solo en manejado) lo antes posible.

    Con esta recomendación podéis ver que en nativo ODBC es el camino a seguir sin ninguna duda, pero en manejado, usamos SQLClient u ODBC. Pues depende de lo que busquemos, ODBC al tener que traducir de nativo a manejado y viceversa sufre cierta penalización cuando lo usamos bajo código manejado, por tanto si lo que queremos es rendimiento la respuesta es sencilla, SQLClient.

    Si nos interesa la portabilidad ya que en un futuro puede que cambiemos de SQL Server a otra base de datos, entonces la respuesta no es tan sencilla. Si tenemos proveedores manejados para otras bases de datos que se ajusten al modelo de acceso de System.Data entonces puede que necesitemos cambiar no muchas lineas de codigo. Pero si usamos ODBC el cambio puede ser muchisimo más sencillo (si no usamos comandos SQL “extraños”) bastaría con cambiar la cadena de conexión y en principio,si hay suerte, puede que nada más.

    Pablo Gavela López – Microsoft Customer Support Services

  • Esecuele Sin Fronteras

    Rendimiento de proveedores de acceso – Parte 1 .NET

    • 0 Comments

    En ocasiones nos podemos preguntar que proveedor usar al desarrollar nuestra capa de acceso a datos, usamos OLEDB, ODBC o SQLClient. que ventajas tienen unos, cuales otros… En estos post intentaré ver la diferencia de rendimiento entre los diferentes proveedores y que ventajas podemos tener si usamos unos u otros.

    Para esta prueba vamos a utilizar una tabla de una sola columna (nvarchar(max) y varios millones de columnas. La query que vamos a ejecutar es la siguiente:

    SELECT TOP 5000000 * FROM HashTable

    En este primer articulo vamos a centrarnos en código manejado y las diferencias de rendimiento entre usar SQLClient, Odbc, Oledb. Todas las pruebas se realizan en local y conectando con TCP.

    SQLClient

    Para empezar veremos la que probablemente sea forma más común de acceder a SQL Server desde .NET, SQLClient. El código que vamos a ejecutar es el siguiente:

    for (int j = 0; j < 10; j++)
    {
        SqlConnection conn = new SqlConnection("Data Source=PGAVELA-02\\KAWORU;Initial Catalog=PerfTest;Integrated Security=SSPI");
        SqlCommand cmd = new SqlCommand("SELECT TOP 5000000 * FROM HashTable");
        cmd.Connection = conn;
        cmd.CommandTimeout = 0;
        conn.Open();
        startTick = Environment.TickCount;
        cmd.Prepare();
        SqlDataReader reader = cmd.ExecuteReader();
        while (reader.Read())
        {
            reader.GetString(0);
        }
        endTick = Environment.TickCount;
        Console.WriteLine(String.Format("Round {0} Time: {1} ms", j, endTick - startTick));
        SQLClient += (endTick - startTick);
        conn.Close();
    }

    Console.WriteLine(String.Format("Avg Time: {0} ms", SQLClient / 10.0));

    Como podemos ver ejecutamos 10 veces la query y al final obtenemos el tiempo medio.

    Managed Code And SQLClient
    Round 0 Time: 15803 ms
    Round 1 Time: 15709 ms
    Round 2 Time: 15694 ms
    Round 3 Time: 15802 ms
    Round 4 Time: 15757 ms
    Round 5 Time: 15818 ms
    Round 6 Time: 15585 ms
    Round 7 Time: 15568 ms
    Round 8 Time: 15601 ms
    Round 9 Time: 15678 ms
    Avg Time: 15701,5 ms

    Como podemos ver el el tiempo medio es de 15.7 segundos.

    OleDB

    La segunda prueba la vamos a realizar con OleDB, el código que vamos a ejecutar es practicamente el mismo que con SQLClient, pero cambiando a las clases apropiadas de OleDB.

    Console.WriteLine("Managed Code And OLEDB");
                for (int j = 0; j < 10; j++)
                {
                    OleDbConnection OleDbConn = new OleDbConnection("Provider=SQLNCLI10.1;Integrated Security=SSPI;Initial Catalog=PerfTest;Data Source=PGAVELA-02\\KAWORU");
                    OleDbCommand OleDbCmd = new OleDbCommand("SELECT TOP 5000000 * FROM HashTable");
                    OleDbCmd.Connection = OleDbConn;
                    OleDbCmd.CommandTimeout = 0;
                    OleDbConn.Open();
                    startTick = Environment.TickCount;
                    OleDbCmd.Prepare();
                    OleDbDataReader OleDbReader = OleDbCmd.ExecuteReader();
                    while (OleDbReader.Read())
                    {
                        OleDbReader.GetString(0);
                    }
                    endTick = Environment.TickCount;
                    Console.WriteLine(String.Format("Round {0} Time: {1} ms", j, endTick - startTick));
                    OleDb += (endTick - startTick);
                    OleDbConn.Close();
                }
                Console.WriteLine(String.Format("Avg Time: {0} ms", OleDb / 10.0));

    Los tiempos para OleDB son los siguientes:

    Managed Code And OLEDB
    Round 0 Time: 35334 ms
    Round 1 Time: 34492 ms
    Round 2 Time: 34913 ms
    Round 3 Time: 33961 ms
    Round 4 Time: 33618 ms
    Round 5 Time: 33634 ms
    Round 6 Time: 34242 ms
    Round 7 Time: 34024 ms
    Round 8 Time: 33962 ms
    Round 9 Time: 34040 ms
    Avg Time: 34222 ms

    El tiempo medio es de 34.2 segundos, mucho más alto que el SQLClient.

    ODBC

    Como en las otras pruebas usaremos el mismo código cambiando solo las clases relativas al proveedor, el código usado es:

    Console.WriteLine("Managed Code and ODBC");

                for (int j = 0; j < 10; j++)
                {
                    OdbcConnection OdbcConn = new OdbcConnection("Driver={SQL Server Native Client 10.0};Server=PGAVELA-02\\KAWORU;Database=PerfTest;Trusted_Connection=yes");
                    OdbcCommand OdbcCmd = new OdbcCommand("SELECT TOP 5000000 * FROM HashTable");
                    OdbcCmd.CommandTimeout = 0;
                    OdbcCmd.Connection = OdbcConn;
                    OdbcConn.Open();
                    startTick = Environment.TickCount;
                    OdbcCmd.Prepare();
                    OdbcDataReader OdbcReader = OdbcCmd.ExecuteReader();
                   
                    while (OdbcReader.Read())
                    {
                       OdbcReader.GetString(0);
                    }
                    endTick = Environment.TickCount;
                    Console.WriteLine(String.Format("Round {0} Time: {1} ms", j, endTick - startTick));
                    Odbc += (endTick - startTick);
                    OdbcConn.Close();
                }
                Console.WriteLine(String.Format("Avg Time: {0} ms", Odbc / 10.0));

    Los tiempos para este proveedor son los siguientes:

    Managed Code and ODBC
    Round 0 Time: 23603 ms
    Round 1 Time: 23572 ms
    Round 2 Time: 23697 ms
    Round 3 Time: 23603 ms
    Round 4 Time: 23821 ms
    Round 5 Time: 23634 ms
    Round 6 Time: 23587 ms
    Round 7 Time: 23619 ms
    Round 8 Time: 23728 ms
    Round 9 Time: 23462 ms
    Avg Time: 23632,6 ms

    Como podemos ver el tiempo medio es de 23.6 segundos.

     

    Conclusiones

    Como podemos ver el método que más rendimiento ofrece SQLClient sin ninguna duda, seguido de ODBC. OLEDB se queda con mucho retraso en último lugar. Además el proveedor OLEDB para SQL Server se considera deprecado (http://blogs.msdn.com/b/sqlnativeclient/archive/2011/08/29/microsoft-is-aligning-with-odbc-for-native-relational-data-access.aspx) por tanto no recomendamos utilizar OLEDB para nuevos desarrollos.

    Ahora entre SQLClient o ODBC con cual nos quedamos. Si necesitamos rendimiento sin ninguna duda optaremos por SQLClient.

    Pablo Gavela López – Microsoft Customer Support Services

  • Esecuele Sin Fronteras

    Mejores practicas para usar SQL Profiler

    • 0 Comments


    Dentro de SQL Server, la herramienta SQL Profiler es una gran aliada para poder capturar, entre otras cosas, que consultas son las mas costosas. Pero esta herramienta tiene un coste en recursos alto.

    Quiero compartir con vosotros ciertas practicas con las que podremos minimizar esta carga:

        - Si podemos, utilizaremos Extended Events (a partir de SQL Server 2008) en vez de utilizar SQL Trace o SQL Profiler en entornos productivos. La utilización de recursos por parte de SQL Trace, o SQL Profiler se incrementa al aumentar el numero de Cores de la maquina.
       
        - Si necesitamos capturar una traza en un entorno productivo, debemos limitar el numero de eventos a un mínimo. Elige y comprueba cuidadosamente los eventos capturados bajo carga, e intenta evitar combinaciones que aumenten significativamente la carga
       
        - Si tenemos habilitado Hyper-Threading, tenemos que tener cuidado con el siguiente contador: Context Switches/sec
            Este contador nos podrá indicar como de saturados están los cores de cada procesador. Un valor alto (>5000 context swithces por segundo) muestra que estamos saturando la CPU
           
        - La captura de SQL Profiler incrementa la carga del sistema entre un 15 y un 25%, generalmente. Si utilizamos los stored procedures (SQL Trace), el impacto es menor.
            Estos porcentajes dependerán de que estemos capturando, la carga de la instancia, el sistema, y como está siendo usada la instancia.
           
        - Evita capturar eventos que ocurren frecuentemente. Si es posible, ajusta la captura con eventos específicos y filtros. Si se capturan menos eventos, se necesitan menos recursos
       
        - Enfoca la traza para capturar solo eventos que recojan información relevante para nuestro problema. Por ejemplo, si deseas capturar deadlocks, podemos incluir el evento Lock:Deadlock, evitando el evento Lock:Acquired. Si incluimos ambos eventos, la traza tiene que responder a todos los bloqueos que son adquiridos, y el coste de ejecución puede doblarse.
       
        - Evita recolectar datos duplicados. Por ejemplo, podemos evitar recoger SQL:BatchStarted y SQL:BatchCompleted, recogiendo solo SQL:BatchCompleted, que nos mostrara toda la informacion que tiene SQL:BatchStarted
       
        - Utiliza filtros en la definición de traza. Por ejemplo, si sabes que el problema lo está teniendo un usuario especifico, crea un filtro por el nombre de usuario.

    Links interesantes:

    Optimizing SQL Trace
    http://msdn.microsoft.com/en-us/library/ms187023(v=sql.105).aspx

    Best practices for running SQL Server on computers that have more than 64 CPUs
    http://msdn.microsoft.com/en-us/library/ee210547(v=sql.105).aspx

    Optimizing SQL Server CPU Performance
    http://technet.microsoft.com/en-us/magazine/2007.10.sqlcpu.aspx

    Introducing Extended events
    http://msdn.microsoft.com/en-us/library/bb630354(v=sql.105).aspx

    Moisés Romero Senosiain – Microsoft Customer Support Services

  • Esecuele Sin Fronteras

    Recomendaciones para actualizar SQL Server 2005

    • 0 Comments

    Hoy os voy a hablar sobre el proceso de actualización de SQL Server 2005, en un entorno de cluster, aunque ciertos pasos también aplican en un entorno StandAlone.

    El proceso de actualización dentro de esta versión de SQL Server es un proceso complejo, donde podemos encontrar diferentes puntos de fallo, de los que suelen poder evitarse muchos, siguiendo las siguientes recomendaciones.

    Tareas previas:

    1.- Como tarea preventiva, y debido a que esto podría retrasarnos la instalación, vamos a comprobar que los ficheros MSI/MSP que se encuentran disponibles en cada uno de los nodos, son los necesarios para el proceso de actualización:

                    Para ello, accederemos a la siguiente URL, http://support.microsoft.com/kb/969052/en-us

                    Y generaremos el script FindSQLInstallsOnly.vbs

                    Una vez generado, lanzaremos el script de la siguiente manera:

    Cscript FindSQLInstallsOnly.vbs %computername%_sql_install_details.txt

    Revisaremos la salida del script buscando que cada uno de los MSI/MSP están presentes en la cache de sistema, si encontramos la siguiente salida:

    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    !!!! C:\WINDOWS\Installer\XXXXX DOES NOT exist in the Installer cache. !!!!

    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                    Tendremos que revisar antes de lanzar el proceso de instalación los paquetes MSI/MSP.

    2.- Además, sería aconsejable confirmar que los permisos de los diferentes servicios de SQL Server son correctos.

                    Para una lista de los mismos, tanto permisos a nivel de sistema, como permisos a nivel NTFS, se aconseja revisar la siguiente lista:

    http://msdn.microsoft.com/en-US/library/ms143504(v=sql.90).aspx#Review_NT_rights

    *3.- Comprobar con que formato tienen los servicios de SQL Server aplicada la cuenta de usuario que levanta los mismos.

                    En caso de que el usuario que figura en esta cuenta está del siguiente modo:

                                    usuario@dominio

                    Modificar por dominio\usuario

    Tareas inmediatamente previas:

    1.- Comprobaremos que haya backups de las bases de datos de sistema y de usuario, y que son correctos.

    *2.- Comprobaremos que desde los 2 nodos, la resolución directa e inversa de los nodos del cluster, más el recurso de cluster, son correctas:

                    Para ello lanzaremos un “ping” hacia los 3 objetos, más un “ping –a”

    • Un nslookup no nos valdría, debido a que podemos tener la resolución en DNS correcta, pero podemos tener alguna entrada en el fichero de host, o alguna entrada en la cache de direcciones que no sea correcto.

    *3.- Comprobaremos que los servicios”Cryptographic Service”, "Remote Registry Service", "Remote Procedure Call Locator", "Remote Procedure Call Service", "Server" y “Task Scheduler” están levantados y funcionando en ambos nodos.

    *4.- Comprobaremos que el shared folder “tasks” del nodo pasivo es accesible desde el activo, y que no hay ninguna tarea creada fuera de las habituales (Buscaremos que no existan tareas de “SQL Server Setup”).

    *5.- Comprobaremos que podemos acceder desde el nodo1 a la ruta tasks, dentro de la instalación de Windows (como ejemplo, \\nodo2\c$\Windows\tasks) y que podemos escribir en esa ruta, y viceversa con el usuario que lanza el setup.

    *6.- Comprobaremos que no existen usuarios conectados a través de escritorio remoto al nodo secundario.

    7.- Comprobaremos que el usuario que lanza el setup de SQL Server es administrador local en todas las maquinas involucradas para esa instancia de SQL Server, y que dispone de los siguientes permisos locales como mínimo:

                    a. Act as Part of the Operating System

                    b. Bypass Traverse Checking

                    c. Log on as Batch Job

                    d. Log on as Service

                    e. Replace a Process Level Token

    8.- Comprobaremos que SQL Server está online.

    9.- Tenemos que tener en cuenta que una actualización de SQL Server implica parada de servicio mientras actualiza las BD de sistema.

    *10.- Lanzaremos la actualización desde el nodo activo.

    Tareas inmediatamente posteriores:

    *1.- Una vez realizada la actualización, reiniciaremos el nodo pasivo. Una vez reiniciado, realizaremos un failover sobre este nodo, y reiniciaremos el nodo pendiente de reiniciar.

    2.- Comprobamos que la actualización se ha realizado correctamente, comprobando el fichero summary.txt dentro del directorio de setup bootstrap.

    Todas las tareas marcadas con un *, son exclusivas de un cluster.

    Moisés Romero Senosiain – Microsoft Customer Support Services

  • Esecuele Sin Fronteras

    No se puede añadir un nuevo nodo a un cluster de SQL Server 2008

    • 0 Comments

    Estamos instalando un nuevo cluster de SQL Server 2008, el primer nodo parece que se ha instalado bien, pero cuando procedemos a instalar el segundo nodo y vamos a través del Wizard de instalación nos encontramos de pronto con lo siguiente:

    image

    Nos sale el error de que el servicio de SQL Server Agent no tiene una cuenta de servicio valida, si intentamos editarla no nos deja ya que el textbox está deshabilitado.

    Si puslamos “Next” nos aparece el error de la captura y no podemos continuar…

    Y ahora que hacemos.

    Volvemos al primer nodo donde se ha instalado correctamente SQL Server y abrimos la consola de clúster, veremos lo siguiente:

    image

    Falta el recurso del agente de SQL.

    Si nos vamos al Configuration manager veremos que el servicio del agente si está instalado.

    image

    Si intentamos añadir el recurso manualmente al grupo de clúster podemos encontrarnos con que el recurso del agente no existe.

    image

    Por tanto deberemos añadirlo a mano, para ello en una ventana de comandos con permisos de administrador navegamos a C:\Windows\System32 y ejecutamos lo siguiente

    Cluster restype "SQL Server Agent" /CREATE /DLL:SQAGTRES.DLL

    El siguiente mensaje aparecerá para indicar que el recurso se ha creado correctamente

    Resource type ‘SQL Server Agent’ created

    Ahora si intentamos añadir el recurso al cluster de SQL Server este aparecerá en la lista.

    image

    Lo añadimos y posteriormente abrimos las propiedades con doble click.

    Cambiamos el nombre del recurso a algo más significativo como SQL Server Agent (NombreInstancia)

    En Advanced Policies nos aseguramos que solo el nodo donde está instalado SQL Server es el posible owner

    Vamos a la pestaña de dependencias y hacemos que dependa del servicio de SQL Server

    image

    Vamos a la pestaña de propiedades y rellenamos las dos opciones, en VirtualServerName pondremos el nombre virtual de la instancia de SQL Server (En mi caso SQL2008R2). En InstanceName pondremos el nombre de la instancia de SQL Server que estamos instalando (En mi caso KILIMANJARO64)

    image

     

    Pulsamos aceptar e intentamos levantar el recurso del agente. Si todo a ido bien el recurso se pondrá online.

    En este punto antes de continuar deberemos modificar el registro. Abrimos regedit y navegamos a la siguiente clave: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL<Version>.<NombreInstancia>\ConfigurationState

    Donde <Version> es 10 para SQL 2008 y 10_50 para SQL 2008 R2 y <NombreInstancia> es el nombre de nuestra instancia.

    Aquí encontraremos varios entradas que posiblemente estén a 2.

    image

    Deberemos cambiar todas ellas a 1

    Para evitar posible sorpresas es recomendable lanzar una reparación en este nodo con el instalador de SQL Server (Necesitaremos poner el grupo de cluster de SQL Server offline).

    Una vez hecho todo esto volvemos al otro nodo y lanzamos la instalación para añadir un nuevo nodo.

    Y esta vez nos dejara proseguir y finalizar la instalación correctamente.

    Pablo Gavela López – Microsoft Customer Support Services

  • Esecuele Sin Fronteras

    FILESTREAM, ¿Qué debemos conocer?

    • 0 Comments

    Hoy vamos a comentar que es FILESTREAM, para que vale, los beneficios que nos puede ofrecer, y que componentes lo forman.

    Primero, ¿Qué es FILESTREAM?

    Es una tecnología diseñada para tratar, dentro de una instancia SQL, ficheros sin perjudicar la instancia.

    Esto se realiza con un driver externo, RSFX, que interactuara entre la BD, y un sistema NTFS.

    ¿Cómo se monta?

    Para montar esta tecnología, tendremos 2 pasos a realizar. Primero, activar ese driver, y segundo, permitir a la instancia a comunicarse con este driver.

    Activando el driver:

    clip_image002

    Para habilitar el driver, dentro de SQL Server Configuration Manager, en la pestaña de servicios, elegiremos la instancia donde queremos habilitar esta configuración. Dentro de las propiedades, pestaña FILESTREAM, podemos seleccionar para que queremos tenerlo habilitado, para TSQL, para TSQL y acceso a través de las APIs de Win32, y si queremos que ese driver este activo para recoger conexiones externas. (En un cluster, siempre estará activo para esas conexiones externas)

    Si se habilita el acceso a través de las APIs de Win32, generara un share SMB (que, en este caso, no es un FileShare)

    Si intentamos acceder a este share, tendremos el siguiente error:

    clip_image004

    Esto es así, porque debajo del share SMB no hay un sistema de archivos, sino un EndPoint del driver de RSFX.

    El siguiente paso, es habilitar la instancia para que pueda comunicarse con el driver. Para ello, dentro de SQL Server Manager Studio, lo habilitaremos:

    clip_image006

    Una vez reiniciada la instancia, tendremos FILESTREAM listo para usarse.

    A partir de aquí, podremos crear un directorio FILESTREAM dentro de una BD, crear una tabla que utilice esta tecnología, y trabajar con ella, utilizando para ello, o TSQL, o llamadas a la API de Win32 para acceder a ella.

    Los permisos necesarios son,

    Dentro del sistema NTFS, que el usuario que arranca la instancia, tenga acceso al directorio, y permisos de modificación. Se recomienda que nadie más tenga acceso a este directorio, ya que una modificación externa, implicara que se detecte una corrupción de datos.

    Para poder modificar estos ficheros, solo necesitaremos que el usuario que lanza esta modificación / creación / borrado, tenga los permisos DML necesarios (update, select, insert o delete)

    Tendréis más información en los siguientes links:

    WhitePaper de FILESTREAM:

    http://msdn.microsoft.com/en-us/library/cc949109.aspx

    Como crear una BD con FILESTREAM:

    http://msdn.microsoft.com/en-us/library/cc645585.aspx

    Como crear una Tabla con FILESTREAM:

    http://msdn.microsoft.com/en-us/library/cc645583.aspx

    Uso de FILESTREAM a través de TSQL:

    http://msdn.microsoft.com/en-us/library/cc645962.aspx

    Uso de FILESTREAM a través de APIs Win32:

    http://msdn.microsoft.com/en-us/library/cc645940.aspx

     

    Moisés Romero Senosiain – Microsoft Customer Support Services

  • Esecuele Sin Fronteras

    Reporting Error in CRM - The report cannot be displayed. (rsProcessingAborted)

    • 0 Comments

     

    Los síntomas de recibir el siguiente error al intentar ejecutar informes creados en CRM con el asistente ó utilizando MSCRM_FetchDataSource:

    En el servidor de SSRS:
    Your browser does not support scripts or has been configured not to allow scripts.

    En las máquinas clientes:

    An error occurred during client rendering.
    An error has occurred during report processing.
    Cannot create a connection to data source ‘CRM’.
    For more information about this error navigate to the report server on the local
    server machine, or enable remote errors.

    En CRM:


    Reporting Error
    The report cannot be displayed. (rsProcessingAborted
    )

     

    Pueden tener las siguientes causas:

    1. SPN's no configurados
    2. Falta de permisos de la cuenta de servicio de SSRS Service en el AD y en SQL
    3. Site de CRM ApplicationHost.config no configurado para utilizar las credenciales de Windows Authentication del AppPoolCredentials

     

    Prueba las alguna de las siguientes alternativas para solucionarlo:

     

    1.       Configurar los SPNs en el servidor especificado:

     

    Servidor CRM
    SETSPN -a HTTP/CrmServerName Domain\CRMAppPool
    SETSPN -a HTTP/CrmServerName.Domain.com Domain\CRMAppPool

    Servidor SSRS/SQL Server:

    SETSPN -a HTTP/SsrsServerName Domain\SRSAppPool
    SETSPN -a HTTP/SsrsServerName.Domain.com Domain\SRSAppPool

    Para SQL:
    SETSPN -a MSSQLSvc/SqlServerName:1433 Domain\SQLServerSvcAcct
    SETSPN -a MSSQLSvc/SqlServerName.Domain.com:1433 Domain\SQLServerSvcAcct

     

    2.       Configurar los permisos de la cuenta de servicio de SSRS Service en el AD y modificar los permisos del grupo SQLAccessGroup en SQL como dice el artículo:


    Error message when you run a report in Microsoft Dynamics CRM 4.0: "Reporting Error.
    The report cannot be displayed"

    http://support.microsoft.com/kb/946585 

    Sección: Complete the following steps if SQL Reporting Services is running as a domain user account

     

    3.       Añadir/cambiar la siguiente sección el fichero ApplicationHost.config del site de CRM Site en un editor de texto . (%windir%\system32\inetsrv\config\. Para todos los ficheros bajo la ruta del Default Web Site)

    Poner el elemento WindowsAuthentication del atributo useAppPoolCredentials a true.

     

    Por ejemplo:

     

    <system.webServer>

       <security>

          <authentication>

            <windowsAuthentication enabled="true" useAppPoolCredentials="true" />

          </authentication>

       </security>

    </system.webServer>   

     

    Si el servidor de CRM 2012 esta configurado en SQL Cluster habrá que añadir un par de SPNs más como explica el siguiente artículo:

    Microsoft Dynamics CRM 2011: Reports created by report wizard may fail when executing

    http://support.microsoft.com/kb/2590774 

     

    Maria Esteban

    Ingeniero de Soporte de Reporting Services

  • Esecuele Sin Fronteras

    Usuario huérfanos en LogShipping

    • 0 Comments

    Supongamos que nos encontramos en la siguiente situación:

    En nuestro entorno tenemos montado Log shipping. Estamos utilizando logins de SQL Server, por lo que podemos tener usuarios huérfanos.

    Queremos corregir este problema, pero no queremos romper el log shipping. ¿Qué podemos hacer?

    Primero, vamos a entender que es un usuario de base de datos, que es un login, como se genera un usuario huérfano, y cuál es la relación entre un usuario de BD y un login:

    ¿Qué es un login?

    Un login es una entidad que accede a la instancia de SQL Server. Esto puede ser un usuario de dominio, un usuario local del sistema, o un usuario de SQL Server. Con esto, podremos entrar a nivel de instancia, aunque no necesariamente a ninguna BD que este dentro de esta instancia.

    Para acceder a una BD, tendremos que ser parte de alguno de los roles que otorgan acceso a las BD, o bien tener un usuario de BD mapeado a nuestro login.

    ¿Qué es un usuario de BD?

    Un usuario de BD es la entidad que trabajara a nivel de BD, que puede tener ciertos privilegios a ciertos objetos dentro de esta misma BD.

    ¿Cuál es la posible relación entre un usuario de BD y un login?

    Cuando se relacionan login y usuario, lo hacen a través de un identificador único, el SID. Este SID puede ser generado a nivel de dominio, a nivel de máquina, o bien a nivel de instancia.

    Podréis encontrar más información en la siguiente URL:

    http://msdn.microsoft.com/es-es/library/ms181127.aspx

    Una vez visto esto, vamos a comprobar si tenemos usuarios huérfanos entre las instancias que trabajan en el logshipping:

    Primero, comprobamos que en la instancia principal, nuestro Login “Orphaned”, tiene mapeado el usuario de BD:

    clip_image002

    Y comprobamos que el SID que une al usuario de BD “test”, y al login “Orphaned” es el mismo:

    clip_image004

    Por lo que, debido a cómo funciona “Log Shipping”, en la BD secundaria, tendremos este mismo SID asociado a nuestro usuario de BD, pero, nuestro login tendrá un diferente SID:

    clip_image006

    Es decir, que si rompemos el logshipping, veremos que nuestro login “Orphaned” no tendrá acceso a la BD “LogShipping_test”.

    clip_image008

    Si intento entrar con el login “Orphaned” dentro de la BD recién recuperada, recibiré el siguiente error:

    clip_image010

    Posibles soluciones para no romper el LogShipping:

    Crear el login en la instancia secundaria con el mismo SID que la primaria, añadiendo la cláusula “SID”, y poniendo el mismo SID que el login de la instancia primaria:

    clip_image012

    En esta pantalla podemos observar que el login “Orphaned” ya tiene acceso a nuestra BD.

    Esto es muy útil si, por cualquier razón, no podemos modificar el usuario de BD. Aunque, al borrar el login, cuando lo creemos, tendremos que darle todos los permisos que tuviese antes a nivel de instancia, y corregir cualquier otro mapeo de usuario que tenga.

    Si pudiéramos modificar la BD, podremos lanzar el siguiente comando:

    USE <database_name>;

    GO

    sp_change_users_login @Action='update_one', @UserNamePattern='<database_user>',

    @LoginName='<login_name>';

    GO

    Un poco mas de información:

    http://msdn.microsoft.com/es-es/library/ms175475(v=sql.105).aspx

     

    Moisés Romero Senosiain – Microsoft Customer Support Services

  • Esecuele Sin Fronteras

    Versionando Procedimientos Almacenados en SQL SERVER

    • 0 Comments

    INTRODUCCION.- 

    Muchas veces nos hemos encontrado cuando desarrollamos procedimientos almacenados la necesidad de poder crear una versión de uno previamente creado.

    A la hora de versionar un procedimiento almacenado nos encontramos con diferentes problemas, como por ejemplo:

    • Cómo normalizar y/o escribir el nombre de estas versiones V1, V2, fecha y un largo etcetera, por ejemplo, spCargaCliente, spCargaClienteV1.
    •  
    • También existen otros problemas añadidos a la hora de hacer los scripts de creación de la base de datos donde tenemos, para mantener un seguimiento de los mismos, que seleccionar uno a uno todos los procedimientos almacenados con sus diferentes versiones.

     

    PEQUEÑO TRUCO.-

    Vamos a establecer un ejemplo, de cómo, SQL SERVER puede ayudarnos a organizarlos y cómo puede a la hora de crear el script sólo ver un procedimiento almacenado con sus diferentes versiones.

    Lo primero vamos a crear un procedimiento almacenado simple:

    CREATE PROCEDURE [dbo].[spVersion]
    AS
    BEGIN

        SELECT 'v1'
    END

    Si ejecutamos el procedimiento almacenado, por ejemplo, EXEC spVersion obtenemos el valor v1.

    Ahora crearemos otro procedimiento con el mismo nombre pero esta vez vamos a añadir al nombre el signo ; y un número de versión, por ejemplo, el  (Inicialmente la versión 1 ya está reservada para la primera definición del procedimiento ).

     

    CREATE PROCEDURE [dbo].[spVersion];2
    AS
    BEGIN
        SET NOCOUNT ON;

       SELECT 'v2'
    END

    Si ejecutamos el procedimiento EXEC spVersion volvemos a obtener el valor ‘v1’ y si ejecutamos el procedimiento EXEC spVersion;2 obtenemos el valor ‘v2’.

    Si abrimos el SQL SERVER Management Studio y expandimos los procedimientos almacenados, vemos que sólo existe un procedimiento almacenado.

     

    clip_image001

    Y si accedemos a su definición veremos el siguiente resultado.

    clip_image002

    Lo cual nos permite ir realizando los diferentes versionados del procedimiento almacenado.

     

    José Manuel Jurado Diaz.

    Ingeniero de Soporte de SQL SERVER

Page 2 of 8 (78 items) 12345»