terça-feira, 3 de dezembro de 2019

Oracle - Wait event Library cache: mutex X

Olá,

Recentemente eu estava investigando um incidente na qual várias sessões no banco de dados simplesmente paravam em um wait específico e praticamente todo o banco de dados congelava e precisava ser reiniciado com "shutdown abort". Esse sintoma vinha ocorrendo com uma certa frequência em um ambiente específico na qual muitos sistemas compartilhavam os mesmos SQLs através de schemas distintos no banco de dados. Pelos gráficos abaixo identifiquei que os eventos estavam associados a wait class Concurrency, especificamente de waits Latch/mutex.






Para verificar qual objeto estava causando o evento de mutex (Tabela, índice, package, procure, SQL Cursor), identifiquei que sempre estava relacionado ao SQL_ID fnuugzjw1014k conforme demonstrado abaixo:

SQL> select a.cursors, a.sql_id,b.sql_text
  2  from
  3  (
  4    select count(*) as cursors, ssc.sql_id
  5    from v$sql_shared_cursor ssc
  6    group by ssc.sql_id
  7    order by cursors desc
  8  ) a,
  9  (select sa.sql_id, sa.sql_text, sa.parsing_schema_name from v$sqlarea sa) b
 10  where a.sql_id=b.sql_id
 11  and rownum<=5;

             CURSORS SQL_ID        SQL_TEXT
-------------------- ------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------
               88022 fnuugzjw1014k select * from (select D0.ain_code from t1 D0 where D0.ain_id = :1 and D0.ain_astcode = :2) where rownum <= :3
                6961 dsdny6fnhwjj7 select * from (select D0.gei_code from t2 D0, t3 D1 where D0.gei_gehcode = D1.geh_code and D1.geh_gelcode = :1) where rownum <= :3
                5134 5s3p206y7uxjp select count(1) as n from t2 D0, t3 D1 where D0.gei_gehcode = D1.geh_code and D1.geh_gelcode = :1 and D0.gei_pincode = :2
                3455 3am62hr7b3u75 select D0.elg_code from t0 D0 where D0.elg_eobcode = :1 and D0.elg_objpkvalue = :2 order by D0.elg_date
                3412 33f16tj5ukzzt select * from (select rownum sysRowNum, r.* from (select D0.gei_code from t2 D0, t3 D1 where D0.gei_gehcode = D1.geh_code) r) where sysRowNum between :3 and :4

O que me chamou a atenção é que as instruções SQL utilizam bind variables. Se tivessem usando literais e até entenderia o problema. Pelo que entendi, toda vez que o Oracle tenta utilizar o cursor na library cache para analisar o plano, o Oracle cria um lock (latch) no mesmo até terminar a análise da instrução. Como essa mesma instrução estava sendo chamada várias vezes de forma concorrente, criou-se uma fila muito grande. Talvez se o servidor tivesse mais memória (SGA), o problema não ocorreria com tanta frequência, mas é apenas uma suposição.

De qualquer forma estudando o assunto, descobri que a partir da versão Oracle 11g (11.2.0.2) foi criar uma procedure dbms_shared_pool.markhot que consegue marcar o objeto que está sofrendo latch/mutex de forma que o mesmo seja clonado várias vezes com SQL_ID diferentes de forma a diminuir a contenção em um único cursor. Desta forma executei o comando abaixo para achar o FULL_HASH_VALUE do SQL_ID fnuugzjw1014k.


SQL> SELECT hash_value,
  2         full_hash_value,
  3         namespace
  4    FROM v$db_object_cache
  5   WHERE name LIKE '%select * from (select D0.ain_code from t1 D0 where D0.ain_id = :1 and D0.ain_astcode = :2) where rownum <= :3%'
  6     AND rownum<=1;

          HASH_VALUE FULL_HASH_VALUE                  NAMESPACE
-------------------- -------------------------------- ---------
          2014315666 35e89608ac8d712eea6b4ffc78100492 SQL AREA

A partir do FULL_HASH_VALUE será possível marcar este objeto como "hot" através da procedure dbms_shared_pool.markhot de forma que o Oracle possa criar várias cópias desta instrução SQL na library cache com SQL_ID distintos a fim de diminuir essa contenção. Para isso, será necessário apenas executar o comando abaixo. No mais, vale a pena salientar que esse procedimento deverá ser executado toda vez que a instância for reiniciada.

SQL> exec dbms_shared_pool.markhot(hash =>'35e89608ac8d712eea6b4ffc78100492', namespace =>0);

sexta-feira, 1 de novembro de 2019

SQL Server - Como resetar a senha do usuário "sa" no Linux

Olá,

É comum precisarmos resetar a senha do usuário "sa" no SQL Server caso tenhamos esquecido a mesma. No SQL Server do Linux temos um jeito muito fácil de realizar este procedimento.

Supondo eu eu tenha esquecido a senha do usuário "sa":

[root@linux ~]# sqlcmd -S localhost -U sa -Psenha
Sqlcmd: Error: Microsoft ODBC Driver 17 for SQL Server : Login failed for user 'sa'..

Para definir uma nova senha, basta executarmos o procedimento set-sa-password conforme demonstrado a seguir.


[root@linux ~]# /opt/mssql/bin/mssql-conf set-sa-password
An instance of SQL Server is running. Please stop the SQL Server service
using the following command

    sudo systemctl stop mssql-server

Para realizar tal procedimento, a instância do SQL Server deverá sofrer shutdown caso a mesma esteja no ar.

[root@linux ~]# systemctl stop mssql-server

[root@linux ~]# /opt/mssql/bin/mssql-conf set-sa-password
Enter the SQL Server system administrator password: *********
Confirm the SQL Server system administrator password: *********
Configuring SQL Server...

ForceFlush is enabled for this instance.
ForceFlush feature is enabled for log durability.
The system administrator password has been changed.
Please run 'sudo systemctl start mssql-server' to start SQL Server.

Pronto. Após a definição da nova senha, poderemos reiniciar o SQL Server.

[root@linux ~]# systemctl start mssql-server

[root@linux ~]# systemctl status mssql-server
() mssql-server.service - Microsoft SQL Server Database Engine
   Loaded: loaded (/usr/lib/systemd/system/mssql-server.service; enabled; vendor preset: disabled)
   Active: active (running) since Tue 2019-11-05 12:44:49 -03; 11s ago
     Docs: https://docs.microsoft.com/en-us/sql/linux
 Main PID: 3523 (sqlservr)
   CGroup: /system.slice/mssql-server.service
           |--3523 /opt/mssql/bin/sqlservr
           |--3526 /opt/mssql/bin/sqlservr

Nov 5 12:44:59 linux.local.net sqlservr[3523]: 2019-11-05 12:44:59.70 Server      InitializeExternalUserGroupSid failed. Implied authentication will be disabled.
Nov 5 12:44:59 linux.local.net sqlservr[3523]: 2019-11-05 12:44:59.70 Server      Implied authentication manager initialization failed. Implied authentication will be disabled.
Nov 5 12:44:59 linux.local.net sqlservr[3523]: 2019-11-05 12:44:59.75 Server      Successfully initialized the TLS configuration. Allowed TLS protocol versions are ['1.0 1.1 1.2']. Allowed TLS ciphers are ['ECDHE-ECDS...CDHE-ECDSA-AES12
Nov 5 12:44:59 linux.local.net sqlservr[3523]: 2019-11-05 12:44:59.87 Server      The maximum number of dedicated administrator connections for this instance is '1'
Nov 5 12:44:59 linux.local.net sqlservr[3523]: 2019-11-05 12:44:59.87 Server      Node configuration: node 0: CPU mask: 0x0000000000000003:0 Active CPU mask: 0x0000000000000003:0. This message provides a description o...ion is required.
Nov 5 12:44:59 linux.local.net sqlservr[3523]: 2019-11-05 12:44:59.90 Server      Using dynamic lock allocation.  Initial allocation of 2500 Lock blocks and 5000 Lock Owner blocks per node.  This is an informational m...ion is required.
Nov 5 12:44:59 linux.local.net sqlservr[3523]: 2019-11-05 12:44:59.94 Server      In-Memory OLTP initialized on lowend machine.
Nov 5 12:45:00 linux.local.net sqlservr[3523]: 2019-11-05 12:45:00.30 Server      Database Instant File Initialization: enabled. For security and performance considerations see the topic 'Database Instant File Initial...ion is required.
Nov 5 12:45:00 linux.local.net sqlservr[3523]: ForceFlush is enabled for this instance.
Nov 5 12:45:00 linux.local.net sqlservr[3523]: 2019-11-05 12:45:00.33 Server      Query Store settings initialized with enabled = 1,
Hint: Some lines were ellipsized, use -l to show in full.

Com a instância no ar, podemos testar novamente a conexão com a nova senha definida.

[root@linux ~]# sqlcmd -S localhost -U sa -Psenha
1> select @@version
2> GO

--------------------------------------------------------------------
Microsoft SQL Server 2017 (RTM-CU17) (KB4515579) - 14.0.3238.1 (X64)
        Sep 13 2019 15:49:57
        Copyright (C) 2017 Microsoft Corporation
        Enterprise Edition (64-bit) on Linux (CentOS Linux 7 (Core))

(1 rows affected)


sexta-feira, 4 de outubro de 2019

MySQL - Extraindo uma única tabela do dump de um banco de dados

Olá,

Geralmente utilizo uma técnica muito simples quando preciso extrair e importar apenas uma tabela que está dentro de um dump que foi gerado pelo mysqldump. A técnica consiste em gerar um arquivo SQL específico contendo apenas a tabela que eu quero. Como demonstrarei a seguir, utilizarei o comando grep do Linux para extrair uma tabela específica contida no arquivo bd01.sql que contém um dump de todas as tabelas e dados do banco de dados bd01.

[mysql@]# grep -n "Table structure" bd01.sql
24:-- Table structure for table `tb_usuarios`
52:-- Table structure for table `tb_historico`
77:-- Table structure for table `tb_adesao`
110:-- Table structure for table `tb_veiculos`
208:-- Table structure for table `tb_atendimento`
255:-- Table structure for table `tb_atendimento_itens`
310:-- Table structure for table `tb_bairros`
334:-- Table structure for table `tb_boletos`
369:-- Table structure for table `tb_carencia`
400:-- Table structure for table `tb_cliente_pj`
427:-- Table structure for table `tb_clientes`
512:-- Table structure for table `tb_parametros`
536:-- Table structure for table `tb_consultores`
582:-- Table structure for table `tb_corretores`
638:-- Table structure for table `tb_credenciados`
710:-- Table structure for table `tb_decl`
737:-- Table structure for table `tb_desbloqueio`
761:-- Table structure for table `tb_desbloqueio_cli`
786:-- Table structure for table `tb_pecas`
815:-- Table structure for table `tb_manutencao_controle`
847:-- Table structure for table `tb_excecao`
872:-- Table structure for table `tb_forma_pgto`
896:-- Table structure for table `tb_tarefas`
935:-- Table structure for table `tb_gastos`
996:-- Table structure for table `tb_liberacao`
1031:-- Table structure for table `tb_lote`
1056:-- Table structure for table `tb_plano_tabela`
1080:-- Table structure for table `tb_mensalidades`
1147:-- Table structure for table `tb_nfse`
1188:-- Table structure for table `tb_obs_cliente`
1222:-- Table structure for table `tb_pagamentos`
1307:-- Table structure for table `tb_parceiros`
1374:-- Table structure for table `tb_veiculos_controle`
1407:-- Table structure for table `tb_orcamentos`
1435:-- Table structure for table `tb_uf`
1464:-- Table structure for table `tb_carroceria_controle`
1497:-- Table structure for table `tb_plano_veiculo`
1534:-- Table structure for table `tb_plano_procedimento`
1564:-- Table structure for table `tb_planos`
1592:-- Table structure for table `tb_portes`
1621:-- Table structure for table `tb_precos`
1647:-- Table structure for table `tb_procedimentos`
1687:-- Table structure for table `tb_procedimentos_diario`
1714:-- Table structure for table `tb_procedimentos_mensal`
1741:-- Table structure for table `tb_profissional`
1770:-- Table structure for table `tb_oficinas`
1794:-- Table structure for table `tb_reajuste_mensalidade`
1826:-- Table structure for table `tb_reajuste_procedimentos`
1859:-- Table structure for table `tb_remessa`

Como demonstrado acima, o resultado do comando grep exibe exatamente o número que dará início a importação de cada tabela dentro do arquivo bd01.sql. Para eu importar apenas a tabela tb_planos, verifico que a mesma se inicia na linha 1564 e que termina uma linha antes de iniciar a próxima tabela que está na linha 1592. Portanto, irei gerar um novo arquivo contendo apenas a estrutura e dados da tabela da linha 1564 até 1591 conforme a seguir.

[mysql@]# sed -n '1564,1591 p' bd01.sql > tb_planos.sql

Pronto. Podemos ver abaixo que o arquivo tb_planos.sql contém os comandos necessários para importar a tabela e seus dados.

[mysql@]# cat tb_planos.sql
-- Table structure for table `tb_planos`
--

DROP TABLE IF EXISTS `tb_planos`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `tb_planos` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `nome_plano` varchar(50) COLLATE latin1_general_ci DEFAULT NULL,
  `valor` decimal(5,2) NOT NULL,
  `usuario` int(11) DEFAULT NULL,
  `data` date DEFAULT NULL,
  `flag_unico` char(1) COLLATE latin1_general_ci DEFAULT 'N',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `tb_planos`
--

LOCK TABLES `tb_planos` WRITE;
/*!40000 ALTER TABLE `tb_planos` DISABLE KEYS */;
INSERT INTO `tb_planos` VALUES
(1,'BASIC',10.15,NULL,NULL,'N'),
(2,'MEDIUM',15.20,NULL,NULL,'N'),
(3,'GOLD',25.99,NULL,NULL,'N'),
(4,'PLATINUM',35.45,NULL,NULL,'N'),
(5,'SOFT',22.10,NULL,NULL,'N'),
(6,'STANDARD',40.00,NULL,NULL,'N'),
(7,'SILVER',99.20,NULL,NULL,'N'),
(8,'MASTER',113.40,NULL,NULL,'N'),
(9,'BLUE',5.15,NULL,NULL,'N'),
(10,'ESSENTIAL',20.15,NULL,NULL,'N'),
(11,'MAX',50.00,NULL,NULL,'N'),
(100,'DIAMOND',60.00,NULL,'2019-11-01','N');
/*!40000 ALTER TABLE `tb_planos` ENABLE KEYS */;
UNLOCK TABLES;

--

Para realizar a importação da tabela para o meu banco de dados, irei realizar a operação a seguir.

[mysql@]$ mysql -uroot -psenha -D bd01 < tb_planos.sql

Pronto. Após a importação podemos ver abaixo que a tabela foi carregada para dentro do banco de dados bd01.

[mysql@]$ mysql -uroot -psenha

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 4911066
Server version: 5.6.13 MySQL Community Server (GPL)

Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> use bd01

Database changed

mysql> select * from tb_planos;
+-----+------------+--------+---------+------------+------------+
| id  | nome_plano | valor  | usuario | data       | flag_unico |
+-----+------------+--------+---------+------------+------------+
|   1 | BASIC      |  10.15 |    NULL | NULL       | N          |
|   2 | MEDIUM     |  15.20 |    NULL | NULL       | N          |
|   3 | GOLD       |  25.99 |    NULL | NULL       | N          |
|   4 | PLATINUM   |  35.45 |    NULL | NULL       | N          |
|   5 | SOFT       |  22.10 |    NULL | NULL       | N          |
|   6 | STANDARD   |  40.00 |    NULL | NULL       | N          |
|   7 | SILVER     |  99.20 |    NULL | NULL       | N          |
|   8 | MASTER     | 113.40 |    NULL | NULL       | N          |
|   9 | BLUE       |   5.15 |    NULL | NULL       | N          |
|  10 | ESSENTIAL  |  20.15 |    NULL | NULL       | N          |
|  11 | MAX        |  50.00 |    NULL | NULL       | N          |
| 100 | DIAMOND    |  60.00 |    NULL | 2019-11-01 | N          |
+-----+------------+--------+---------+------------+------------+
12 rows in set (0.00 sec)

sexta-feira, 6 de setembro de 2019

SQL Server - Restore de backup trará os dados como eles estavam no início ou no término do backup?

Olá,
No SQL Server, suponha que um backup FULL do banco de dados XYZ se inicia às 00:00 e termina às 04:00. Vale a pena salientar que o banco de dados está aberto e recebendo transações normalmente durante a execução do backup. Agora vem a pergunta: Se precisarmos realizar uma restauração desse backup, os dados do banco de dados XYZ serão restaurados como eles estavam às 00:00 ou como eles estavam às 04:00?
 
Fazendo uma comparação com outros bancos de dados teremos o seguinte:
  • Oracle RMAN: O backup feito devidamente em conjunto com os archivelogs restaurará o banco de dados como ele estava ás 04:00.
  • Oracle Datapummp: Se for utilizado a cláusula flashback_time=systimestamp do comando expdp, o dump será consistente a partir do início do backup, ou seja, o dump restaurado trará os dados como eles estavam ás 00:00.
  • MySQL: Se for utilizado a cláusula --single-transaction do comando mysqldump, os bancos dedados não serão lockados durante o backup e o dump será consistente a partir do início do backup, ou seja, o dump restaurado trará os dados como eles estavam ás 00:00.
  • MongoDB: Se for utilizado a cláusula --oplog do comando mongodump, ao final do backup, será realizado um backup também da collection oplog que poderá ser utilizada para aplicar as transações realizadas durante o backup e restaurar os dados como eles estavam ás 04:00. Caso seja utilizado a tecnologia de snapshot como por exemplo no disco EBS da AWS, o snapshot restaurado trará os dados como eles estavam ás 00:00. Vale a pena salientar que o MongoDB precisaria ter sido inicializado com a opção --journal.
E no caso do SQL Server, a restauração do backup trará os dados como eles estavam ás 04:00. Algumas evidências como esta e essa, comprovam esta afirmação.      
               
"A full database backup provides a complete copy of the database and provides a single point-in-time to which the database can be restored. Even though it may take many hours for the backup process to run, you can still only restore the backup to a single point (effectively at the end of the backup, but I'll discuss exactly what that point is later in this article). A full backup does not allow recovery to any point in time while the backup was running. Another misconception about full backups is that they only contain data. Both full backups and differential backups also contain some transaction log records so that the restored component (database, file, or filegroup) can be made transactionally consistent."
               
"Consider an example transaction that inserts a record into a table with a single nonclustered index. The pages of the table and index are spread through the data file. The transaction is split into two parts internally: a record insertion into a data page in the table and then the insertion of the required record in an index page in the nonclustered index. If the backup process just happens to read the nonclustered index page before the record insertion, but reads the table data page after the record insertion, then the database represented by just the data in the backup is transactionally inconsistent. This is where the transaction log comes into play. By also including some transaction log records in the backup, recovery can be run on the restored copy of the database, making it transactionally consistent. For this example transaction, depending on when it commits, the recovery part of restore may roll it forward (meaning it will appear as committed in the restored copy of the database) or roll it back (meaning it will not appear at all in the restored copy of the database). In either case, transactional consistency is maintained, which is crucial."
 
 

quinta-feira, 1 de agosto de 2019

MongoDB - Redimensionando o tamanho do oplog

Olá,

No MongoDB a partir da versão 3.6 em um ambiente de replicasets, temos a opção de redimensionar o tamanho do oplog de forma online através da função replSetResizeOplog. No exemplo abaixo poderemos executar o comando para aumentar o oplog para 32 GB nos membros SECONDARY primeiro e por último no membro PRIMARY.

PrdSet:SECONDARY> db.adminCommand({replSetResizeOplog: 1, size: 32768})

Nas versões do MongoDB anteriores a versão 3.6 precisamos fazer de forma offline, ou seja, os membros da replicaset precisam sofrer shutdown e subir de forma standalone para poder redimensionar o oplog. O ideal é verificar se existe retenção suficiente no oplog do membro primário quando o membro subir e voltar novamente como membro da replicaset de forma que a sincronização tenha tempo suficiente para ser realizada. No mais, segue abaixo os procedimentos necessários realizados para redimensionar o tamanho do oplog em um ambiente de replicaset que utilizam o MongoDB 3.2. O propósito será aumentar o tamanho do oplog de 1GB para 32GB de forma que a sincronização entre os membros tenham uma margem de tempo razoável.

Abaixo, irei checar o status da replicaset de todos os membros envolvidos.

[mongodb@linux2 ~]$ mongo --port 27017
MongoDB shell version: 3.2.17
connecting to: 127.0.0.1:27017/test

PrdSet:SECONDARY> rs.status()
{
        "set" : "PrdSet",
        "myState" : 1,
        "term" : NumberLong(37),
        "heartbeatIntervalMillis" : NumberLong(2000),
        "members" : [
                {
                        "_id" : 0,
                        "name" : "linux.local.net:27017",
                        "health" : 1,
                        "state" : 1,
                        "stateStr" : "PRIMARY",
                        "uptime" : 201,
                        "configVersion" : 4,
                        "self" : true
                },
                {
                        "_id" : 1,
                        "name" : "linux2.local.net:27017",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 194,
                        "pingMs" : NumberLong(0),
                        "syncingTo" : "linux.local.net:27017",
                        "configVersion" : 4
                },
                {
                        "_id" : 2,
                        "name" : "linux3.local.net:27017",
                        "health" : 1,
                        "state" : 7,
                        "stateStr" : "ARBITER",
                        "uptime" : 194,
                        "pingMs" : NumberLong(0),
                        "configVersion" : 4
                }
        ],
        "ok" : 1
}

A estratégia é realizar o procedimento primeiro nos membros SECONDARY e por último no membro PRIMARY. irei realizar o shutdown do MongoDB no nó linux2.local.net:27017 e subi-lo standalone utilizando a porta 37018 conforme a seguir.

[mongodb@linux2 ~]$ mongod --dbpath /mongodb/data --shutdown
killing process with pid: 1786

[mongodb@linux ~]$ mongod --fork --port 37018 --dbpath /mongodb/data/ --logpath /mongodb/log/mongo_PRD.log
about to fork child process, waiting until server is ready for connections.
forked process: 1954
child process started successfully, parent exiting

Após subir a instância do MongoDB como standalone na porta 37018, irei conectar nele e realizar os procedimentos para redimensionamento do oplogsize de 1GB para 32GB. Observe abaixo que ao executar o comando db.printReplicationInfo(), temos a informação do tamanho atual do oplog além da informação de que a retenção do oplog atual do membro PRIMARY está em 4.5 horas, ou seja, um membro SECONDARY conseguiria ficar em média até 4.5 horas fora da replicaset e mesmo assim conseguir se sincronizar com o PRIMARY até esse tempo.

[mongodb@linux2 ~]$ mongo --port 37018
MongoDB shell version: 3.2.17

> //Recreate the Oplog with a New Size and a Seed entry
> use local
switched to db local
> db = db.getSiblingDB('local')
local

> //get size of the oplog
> db.printReplicationInfo()
configured oplog size:   1024MB
log length start to end: 16200secs (4.5hrs)

> //Ensure that the temp temporary collection is empty by dropping the collection
> db.temp.drop()
false

> //Use the db.collection.save() method and a sort on reverse natural order to find the last entry and save it to a temporary collection
> db.temp.save(db.oplog.rs.find( { }, { ts: 1, h: 1 } ).sort( {$natural : -1} ).limit(1).next())
WriteResult({ "nInserted" : 1 })

> //Remove the Existing Oplog Collection
> db.oplog.rs.drop()
true

> //Create a New Oplog
> db.runCommand({ create: "oplog.rs", capped: true, size: (32 * 1024 * 1024 * 1024) })
{ "ok" : 1 }

> //Insert the Last Entry of the Old Oplog into the New Oplog
> db.oplog.rs.save(db.temp.findOne())
WriteResult({
        "nMatched" : 0,
        "nUpserted" : 1,
        "nModified" : 0,
        "_id" : ObjectId("5d1fb2f43037c7e5b94410cf")
})

> exit
bye

Pronto. Agora podemos subir o MongoDB novamente como membro da replicaset na sua porta usual.

[mongodb@linux2 ~]$ mongod --dbpath /mongodb/data --shutdown
killing process with pid: 1954

[mongodb@linux2 ~]$ mongod --fork --journal --replSet PrdSet --port 27017 --dbpath /mongodb/data/ --logpath /mongodb/log/mongo_PRD.log
about to fork child process, waiting until server is ready for connections.
forked process: 3348
child process started successfully, parent exiting

Podemos ver abaixo que o oplog agora está com tamanho de 32GB.

[mongodb@linux2 ~]$ mongo --port 27017
MongoDB shell version: 3.2.17
connecting to: 127.0.0.1:27017/test

PrdSet:SECONDARY> db.printReplicationInfo()
configured oplog size:   32768MB
log length start to end: 16200secs (4.5hrs)

Feito esse procedimento em todos os membros SECONDARY, o último passo será realizá-lo no membro PRIMARY. será necessário apenas repetir o mesmo procedimento. Para tanto, de forma a não gerar indisponibilidade no cluster do MongoDB, será necessário eleger um novo membro PRIMARY antes de realizar essa operação. Basta executar o comando rs.stepDown() no membro PRIMARY de forma que algum membro SECONDARY assuma como PRIMARY. Após a realização do stepDown, o antigo PRIMARY se tornará SECONDARY e o procedimento poderá ser realizado normalmente.

quarta-feira, 10 de julho de 2019

GUOB TECH DAY 2019

Olá,
Com grande sucesso, o GUOB prepara mais uma edição de seu evento nacional no dia 10/08/2019 em São Paulo na Universidade Nove de Julho, UNINOVE, o qual proporcionará um grande encontro de usuários de tecnologia Oracle do Brasil com a participação de palestrantes internacionais e nacionais.


Estarão presentes palestrantes como Anil Nair, Sandesh Rao, Mike Dietrich, Nirmala Sundarappa, Alex Zaballa , entre outros.


Como resultado esperado contaremos com a qualidade das palestras, as quais garantirão o investimento dos participantes em dedicar um dia para estar presente em nosso evento. Além do netwoking proporcionado aos associados do GUOB e aos profissionais usuários de tecnologia Oracle.


Participe da 10o. edição do GUOB TECH DAY / Groundbreakers Tour LATAM 2019. Faça sua inscrição ainda hoje.


GUOB - Grupo de Usuários de Tecnologia Oracle do Brasil.

terça-feira, 2 de julho de 2019

Oracle - ALTER SYSTEM CANCEL SQL no Oracle 12 R2 (18c)

Olá,

A partir do Oracle 12c R2 (18c) temos a opção de abortar a execução de uma instrução SQL em andamento de uma sessão. Nas versões anteriores tínhamos apenas a opção de de matar a sessão (ALTER SYSTEM KILL SESSION). Segue abaixo um exemplo.

A sessão mais abaixo teve sua instrução em andamento interrompida após a execução do comando a seguir. Pode-se observar que a sessão continua conectada mesmo após a execução do comando ALTER SYSTEM CANCEL SQL.

C:\>sqlplus system/manager@linuxserver:1521/ORCLPDB1

SQL> ALTER SYSTEM CANCEL SQL '52, 43281';

Sistema alterado.

C:\>sqlplus scott/tiger@linuxserver/ORCLPDB1

Conectado a:
Oracle Database 18c Enterprise Edition Release 18.0.0.0.0 - Production

SQL> select sid,serial# from v$session where sid in (select distinct sid from v$mystat);

                 SID              SERIAL#
-------------------- --------------------
                  52                43281

SQL> select count(*) from t1;
select count(*) from t1
                     *
ERRO na linha 1:
ORA-01013: o usuário solicitou o cancelamento da operação atual

SQL> select sysdate from dual;

SYSDATE
----------
02/07/2019

Caso queiramos realmente matar a sessão devemos proceder com o comando a seguir.


C:\>sqlplus system/manager@linuxserver:1521/ORCLPDB1

SQL> ALTER SYSTEM KILL SESSION '52, 43281';

Sistema alterado.

C:\>sqlplus scott/tiger@linuxserver/ORCLPDB1

Conectado a:
Oracle Database 18c Enterprise Edition Release 18.0.0.0.0 - Production

SQL> select sid,serial# from v$session where sid in (select distinct sid from v$mystat);

                 SID              SERIAL#
-------------------- --------------------
                  52                43281

SQL> select count(*) from t1;
select count(*) from t1
*
ERRO na linha 1:
ORA-00028: a sessão foi cancelada

O comando ALTER SYSTEM CANCEL SQL pode receber outros parâmetros. Segue um resumo.

-- Instrução SQL corrente da sessão
ALTER SYSTEM CANCEL SQL '52, 43281';

-- Instrução SQL corrente da sessão em um ambiente RAC no node 1
ALTER SYSTEM CANCEL SQL '52, 43281, @1';

-- SQL_ID específico de uma sessão
ALTER SYSTEM CANCEL SQL '52, 43281, 12czs5bwerdfg';

-- SQL_ID específico de uma sessão em um ambiente RAC no node 1
ALTER SYSTEM CANCEL SQL '52, 43281, @1, 12czs5bwerdfg';

quinta-feira, 6 de junho de 2019

MySQL - Procedimento de Diagnóstico utilizando o PERFORMANCE_SCHEMA

Olá,

No MySQL a partir da versão 5.7 tem uma procedimento de diagnóstico da instância que pode ser útil em casos que precise de um relatório geral sobre o ambiente. Pode-se dizer que é um tipo de AWR do Oracle disponível para o MySQL só que para o workload atual. O relatório é extenso mas segue abaixo algumas imagens sobre saída do mesmo gerado em HTML. Seguindo a documentação, eu pedi no exemplo abaixo para fazer uma iteração a cada 30 segundos durante 60 segundos.

mysql -uroot -p -H -e"CALL sys.diagnostics(60, 30, 'current');" > /tmp/mysql_report.html









quinta-feira, 2 de maio de 2019

Oracle - Acessando a view V$SESSION em um ambiente Multitenant

Olá,

Nos ambientes de banco de dados Multitenant a partir do Oracle 12c podemos ter um ou mais bancos de dados plugáveis em uma instância CDB$ROOT. Utilizando o Oracle 18c a seguir, irei mostrar que a view dinâmica de desempenho V$SESSION retorna as sessões de todos os bancos de dados quando executada a partir do banco de dados CDB$ROOT e, quando executada a partir de um banco de dados PDB, irá ser retornado apenas as sessões conectadas apenas neste PDB.

Abaixo é possível perceber que ao conectar no banco de dados CDB$ROOT podemos visualizar todos os datafiles de todos os bancos de dados CDB e PDBs através da view CDB_DATA_FILES. Ao acessar a view V$SESSION também é possível visualizar todas as sessões de todos os bancos de dados.

C:\>sqlplus sys/manager@bdserver:1521/ORCLCDB as sysdba

SQL*Plus: Release 12.2.0.1.0 Production on Qui Mai 2 9:35:50 2019

Copyright (c) 1982, 2016, Oracle.  All rights reserved.

Conectado a:
Oracle Database 18c Enterprise Edition Release 18.0.0.0.0 - Production


SQL> select sys_context('USERENV','CON_NAME') from dual;

SYS_CONTEXT('USERENV','CON_NAME')
---------------------------------
CDB$ROOT

SQL> select file_name,file_id,tablespace_name,bytes,status from CDB_DATA_FILES;

FILE_NAME                                                FILE_ID TABLESPACE_NAME            BYTES STATUS
----------------------------------------------------- ---------- ------------------- ------------ -----------
/opt/oracle/oradata/ORCLCDB/users01.dbf                        7 USERS                    5242880 AVAILABLE
/opt/oracle/oradata/ORCLCDB/undotbs01.dbf                      4 UNDOTBS1               319815680 AVAILABLE
/opt/oracle/oradata/ORCLCDB/system01.dbf                       1 SYSTEM                 891289600 AVAILABLE
/opt/oracle/oradata/ORCLCDB/sysaux01.dbf                       3 SYSAUX                 555745280 AVAILABLE
/opt/oracle/oradata/ORCLCDB/ORCLPDB1/system01.dbf              9 SYSTEM                 283115520 AVAILABLE
/opt/oracle/oradata/ORCLCDB/ORCLPDB1/sysaux01.dbf             10 SYSAUX                 387973120 AVAILABLE
/opt/oracle/oradata/ORCLCDB/ORCLPDB1/undotbs01.dbf            11 UNDOTBS1               104857600 AVAILABLE
/opt/oracle/oradata/ORCLCDB/ORCLPDB1/users01.dbf              12 USERS                    5242880 AVAILABLE

SQL> select sid,serial#,service_name,username,status,osuser,terminal,program
  2    from v$session
  3   where osuser='eduardo.legatti';

   SID      SERIAL# SERVICE_NAME     USERNAME      STATUS     OSUSER               TERMINAL          PROGRAM
------ ------------ ---------------- ------------- ---------- -------------------- ----------------- ---------------
      38         9924 orclpdb1       SCOTT         INACTIVE   eduardo.legatti      SRVNOT001         sqlplus.exe
     262        16107 ORCLCDB        SYS           INACTIVE   eduardo.legatti      SRVNOT001         sqlplus.exe
     265        28378 orclpdb1       ADAM          INACTIVE   eduardo.legatti      SRVNOT001         sqlplus.exe
     273         7253 orclpdb1       SYS           INACTIVE   eduardo.legatti      SRVNOT001         sqlplus.exe
     275        59715 ORCLCDB        SYS           ACTIVE     eduardo.legatti      SRVNOT001         sqlplus.exe


Ao conectar em um banco de dados PDB específico as views irão mostrar apenas informações do banco de dados PDB específico. 

C:\>sqlplus sys/manager@bdserver:1521/ORCLPDB1 as sysdba

SQL*Plus: Release 12.2.0.1.0 Production on Qui Mai 2 9:48:54 2019

Copyright (c) 1982, 2016, Oracle.  All rights reserved.

Conectado a:
Oracle Database 18c Enterprise Edition Release 18.0.0.0.0 - Production

SQL> select sys_context('USERENV','CON_NAME') from dual;

SYS_CONTEXT('USERENV','CON_NAME')
---------------------------------
ORCLPDB1

SQL> select file_name,file_id,tablespace_name,bytes,status from CDB_DATA_FILES;

FILE_NAME                                             FILE_ID TABLESPACE_NAME           BYTES STATUS
--------------------------------------------------- --------- ------------------ ------------ -----------
/opt/oracle/oradata/ORCLCDB/ORCLPDB1/system01.dbf           9 SYSTEM                283115520 AVAILABLE
/opt/oracle/oradata/ORCLCDB/ORCLPDB1/sysaux01.dbf          10 SYSAUX                387973120 AVAILABLE
/opt/oracle/oradata/ORCLCDB/ORCLPDB1/undotbs01.dbf         11 UNDOTBS1              104857600 AVAILABLE
/opt/oracle/oradata/ORCLCDB/ORCLPDB1/users01.dbf           12 USERS                   5242880 AVAILABLE


SQL> select sid,serial#,service_name,username,status,osuser,terminal,program
  2    from v$session
  3   where osuser='eduardo.legatti';

   SID     SERIAL# SERVICE_NAME    USERNAME     STATUS     OSUSER             TERMINAL      PROGRAM
------ ----------- --------------- ------------ ---------- ------------------ ------------- --------------
    38       9924 orclpdb1         SCOTT        INACTIVE   eduardo.legatti    SRVNOT001     sqlplus.exe
   265      28378 orclpdb1         ADAM         INACTIVE   eduardo.legatti    SRVNOT001     sqlplus.exe
   273       7253 orclpdb1         SYS          ACTIVE     eduardo.legatti    SRVNOT001     sqlplus.exe

sexta-feira, 26 de abril de 2019

Oracle 19c disponível para download nas plataformas Linux x86-64

Olá,
A Oracle disponibilizou para download a versão do Oracle 19c para as plataformas Linux x86-64.

terça-feira, 2 de abril de 2019

MySQL - Gerando arquivos CSV incluindo cabeçalho de colunas da tabela

Olá,

No artigo de Janeiro/2019 eu demonstrei como carregar um arquivo CSV para uma tabela no MySQL. Agora irei demonstrar como gerar um arquivo CSV a partir de uma tabela, incluindo também as colunas da tabela como cabeçalho.

mysql> select * from customer;
+------+------+
| id   | name |
+------+------+
|    1 | Joe  |
|    2 | Jack |
|    3 | Zack |
+------+------+
3 rows in set (0.00 sec)

Utilizarei o template abaixo para geração de um arquivo CSV.

SET @table_name = 'nome_da_tabela';
SET @table_schema = 'nome_do_banco_de_dados';
SET SESSION group_concat_max_len = 1000000;
SET @col_names = (
  SELECT GROUP_CONCAT(QUOTE(`column_name`)) AS columns
  FROM information_schema.columns
  WHERE table_schema = @table_schema
  AND table_name = @table_name);
SET @cols = CONCAT('(SELECT ', @col_names, ')');
SET @query = CONCAT('(SELECT * FROM ', @table_schema, '.', @table_name,
  ' INTO OUTFILE \'/tmp/nome_do_arquivo.csv\'
  FIELDS TERMINATED BY \',\' ENCLOSED BY \'"\')');
SET @sql = CONCAT(@cols, ' UNION ALL ', @query);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;


Segue exemplo abaixo na qual irei gerar um arquivo CSV da tabela customer.

mysql> SET @table_name = 'customer';
Query OK, 0 rows affected (0.00 sec)

mysql> SET @table_schema = 'bd01';
Query OK, 0 rows affected (0.00 sec)

mysql> SET SESSION group_concat_max_len = 1000000;
Query OK, 0 rows affected (0.00 sec)

mysql> SET @col_names = (
    ->   SELECT GROUP_CONCAT(QUOTE(`column_name`)) AS columns
    ->   FROM information_schema.columns
    ->   WHERE table_schema = @table_schema
    ->   AND table_name = @table_name);
Query OK, 0 rows affected (0.00 sec)

mysql> SET @cols = CONCAT('(SELECT ', @col_names, ')');
Query OK, 0 rows affected (0.00 sec)

mysql> SET @query = CONCAT('(SELECT * FROM ', @table_schema, '.', @table_name,
    ->   ' INTO OUTFILE \'/tmp/customer.csv\'
    '>   FIELDS TERMINATED BY \',\' ENCLOSED BY \'"\')');
Query OK, 0 rows affected (0.00 sec)

mysql> SET @sql = CONCAT(@cols, ' UNION ALL ', @query);
Query OK, 0 rows affected (0.00 sec)

mysql> PREPARE stmt FROM @sql;
Query OK, 0 rows affected (0.00 sec)
Statement prepared

mysql> EXECUTE stmt;
Query OK, 4 rows affected (0.00 sec)

mysql> DEALLOCATE PREPARE stmt;
Query OK, 0 rows affected (0.00 sec)

mysql> system cat /tmp/customer.csv;
"id","name"
"1","Joe"
"2","Jack"
"3","Zack"

quarta-feira, 6 de março de 2019

Assistente de instalação para o Oracle Instant Client 18c (x64)

Olá,
Disponibilizei para download o assitente de instalação do Oracle Instant Client 18c (x64). Para quem tiver interesse, será necessário baixar o arquivo instantclient18c.zip (48M) e, após descompactá-lo, bastará apenas clicar no arquivo instantclient18c.exe para iniciar a instalação. No mais, segue os links para quem tiver interesse em baixar a versão do Oracle Instant Client 11g e do Oracle Instant Client 10g.



sexta-feira, 15 de fevereiro de 2019

Obtendo o tamanho dos bancos de dados e das collections no MongoDB

Olá,

Neste artigo irei demonstrar de forma prática como obter o tamanho dos bancos de dados e um mostrar um script que obtém o tamanho das collections de cada banco de dados existente na instância, ordenados de forma descendente pelo tamanho.

O script abaixo apresenta o tamanho que cada banco de dados está ocupando no disco.

PRIMARY> var databases = db.adminCommand("listDatabases").databases;
var totalSize = 0;
databases.forEach(function(d) {
PRIMARY> var totalSize = 0;
PRIMARY> databases.forEach(function(d) {
...     print(d.name + " - " + "Total size on disk: " + d.sizeOnDisk + " bytes (" + (d.sizeOnDisk / 1024 ) / 1024 + "MB)");
... });

bd01 - Total size on disk: 3481600 bytes (3.3203125MB)
bd02 - Total size on disk: 66854875136 bytes (63757.77734375MB)
admin - Total size on disk: 131072 bytes (0.125MB)
local - Total size on disk: 325246976 bytes (310.1796875MB)

O mesmo comando pode ser usado como demonstrado a seguir.

PRIMARY> db.adminCommand({listDatabases: 1})
{
        "databases" : [
                {
                        "name" : "bd01",
                        "sizeOnDisk" : 3481600,
                        "empty" : false
                },
                {
                        "name" : "bd02",
                        "sizeOnDisk" : 66860290048,
                        "empty" : false
                },
                {
                        "name" : "admin",
                        "sizeOnDisk" : 131072,
                        "empty" : false
                },
                {
                        "name" : "local",
                        "sizeOnDisk" : 325246976,
                        "empty" : false
                }
        ],
        "totalSize" : 67189149696,
        "ok" : 1
}

Vale a pena salientar que por padrão a engine WireTiger no MongoDB utiliza a compressão de blocos chamada Snappy mas, que também permite a compressão zlib (similar ao gzip). Portanto, podemos ver abaixo que o resultado storageSize (compactado) é significativamente menor do que a soma de dataSize e indexSize (não compactados).


PRIMARY> var sum = 0; db.getMongo().getDBs()["databases"].forEach(function(x) { sum += db.getMongo().getDB(x.name).stats().dataSize }); print("dataSize: " + sum);
dataSize: 323528024366 (301.30 GB)

PRIMARY> var sum = 0; db.getMongo().getDBs()["databases"].forEach(function(x) { sum += db.getMongo().getDB(x.name).stats().indexSize }); print("indexSize: " + sum);
indexSize: 1709084672 (1.59 GB)

PRIMARY> var sum = 0; db.getMongo().getDBs()["databases"].forEach(function(x) { sum += db.getMongo().getDB(x.name).stats().storageSize }); print("storageSize: " + sum);
storageSize: 65474957312 (60.97)

O gráfico abaixo mostra uma comparação sobre cada compressão.


Para finalizar, segue abaixo um script que retorna o tamanho de cada collection por banco de dados, ordenado por tamanho da collection de forma descendente pelo tamanho (do maior para o menor).

PRIMARY> var mgo = db.getMongo()
... function getReadableFileSizeString(fileSizeInBytes) {
...
...     var i = -1;
...     var byteUnits = [' kB', ' MB', ' GB', ' TB', 'PB', 'EB', 'ZB', 'YB'];
...     do {
...         fileSizeInBytes = fileSizeInBytes / 1024;
...         i++;
...     } while (fileSizeInBytes > 1024);
...
...     return Math.max(fileSizeInBytes, 0.1).toFixed(1) + byteUnits[i];
... };
... function getStatsFor(db){
...     var collectionNames = db.getCollectionNames(), stats = [];
...     collectionNames.forEach(function (n) { stats.push(db.getCollection(n).stats()); });
...     stats = stats.sort(function(a, b) { return b['size'] - a['size']; });
...     for (var c in stats) { print(stats[c]['ns'] + ": " + getReadableFileSizeString(stats[c]['size']) + " (" + getReadableFileSizeString(stats[c]['storageSize']) + ")"); }
... }
... function getAllStats(){
...
... // Especifico
... // var dbName = 'nomedobanco';
... // var db = mgo.getDB(dbName); print('\n    '+db+'\n'); getStatsFor(db)
...
... // Geral
... mgo.getDBNames().forEach(function(name){ var db = mgo.getDB(name); print('\n    '+db+'\n'); getStatsFor(db) })
...
... }
... getAllStats()


    bd01

bd01.collection01: 3.7 MB (1.5 MB)
bd01.collection02: 257.9 kB (112.0 kB)
bd01.collection03: 181.6 kB (84.0 kB)
bd01.collection04: 31.8 kB (44.0 kB)
.
.
.

    bd02

bd02.collection01: 87.4 GB (13.0 GB)
bd02.collection02: 67.5 GB (10.3 GB)
bd02.collection03: 18.7 GB (4.0 GB)
bd02.collection04: 15.2 GB (4.6 GB)
bd02.collection05: 13.0 GB (4.5 GB)
bd02.collection06: 11.9 GB (2.9 GB)
bd02.collection07: 10.8 GB (2.8 GB)
bd02.collection08: 7.4 GB (2.2 GB)
bd02.collection09: 4.6 GB (1.2 GB)
bd02.collection10: 4.5 GB (1.4 GB)
bd02.collection11: 3.3 GB (999.5 MB)
bd02.collection12: 3.2 GB (526.8 MB)
bd02.collection13: 3.0 GB (464.3 MB)
bd02.collection14: 2.8 GB (831.5 MB)
bd02.collection15: 372.6 kB (68.0 kB)
bd02.collection16: 348.8 kB (116.0 kB)
bd02.collection17: 348.1 kB (120.0 kB)
.
.
.
    admin

admin.system.users: 1.5 kB (32.0 kB)
admin.system.version: 0.1 kB (16.0 kB)

    local

local.oplog.rs: 1.0 GB (309.9 MB)
local.startup_log: 21.0 kB (36.0 kB)
local.system.replset: 0.7 kB (36.0 kB)
local.replset.minvalid: 0.1 kB (36.0 kB)
local.replset.election: 0.1 kB (36.0 kB)
local.me: 0.1 kB (32.0 kB)

quarta-feira, 9 de janeiro de 2019

MySQL - LOAD DATA LOCAL INFILE (Carregando arquivos CSV para uma tabela)

Olá,

Neste artigo irei demonstrar de forma prática como importar para uma tabela de um banco de dados MySQL um arquivo CSV através do comando "LOAD DATA LOCAL INFILE". O arquivo que utilizarei para carregar a tabela será o arquivo a seguir.

$ cat customer.csv
"1","a"
"2","b"
"3","c"
"4","v"
"5","e"
"6","f"
"7","g"
"8","h"
"9","i"
"10","j"

Agora irei conectar na instância do MySQL, especificamente no banco de dados "teste" e executar o comando LOAD para carregar o arquivo customer.csv na tabela customer.

mysql> use teste
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed

mysql> show tables;
+-----------------+
| Tables_in_teste |
+-----------------+
| customer        |
+-----------------+
1 row in set (0.00 sec)

mysql> describe customer;
+--------+--------------+------+-----+---------+-------+
| Field  | Type         | Null | Key | Default | Extra |
+--------+--------------+------+-----+---------+-------+
| codigo | int(11)      | YES  |     | NULL    |       |
| nome   | varchar(100) | YES  |     | NULL    |       |
+--------+--------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

mysql> load data local infile '/tmp/customer.csv' into table customer
    -> fields terminated by ','
    -> enclosed by '"'
    -> lines terminated by '\n'
    -> (codigo,nome);
Query OK, 10 rows affected (0.02 sec)
Records: 10  Deleted: 0  Skipped: 0  Warnings: 0

Pronto. A tabela foi carregada conforme demonstrado pelo resultado do SQL abaixo.

mysql> select * from customer;
+--------+------+
| codigo | nome |
+--------+------+
|      1 | a    |
|      2 | b    |
|      3 | c    |
|      4 | v    |
|      5 | e    |
|      6 | f    |
|      7 | g    |
|      8 | h    |
|      9 | i    |
|     10 | j    |
+--------+------+
10 rows in set (0.00 sec)