Search this Blog

Thursday, September 26, 2019

ORA-01017 on CONNECT / AS SYSDBA after changing OS user/group

From an existing database server the database Instance name and the OS-user and -group running the database had to change, as the database itself went from Development environment to Test environment.
The database was called "STID01", its OS-user "stid01" with the group "dbad01"

Everything was running just fine before the change.
It was intended to change the database name to "STIT01" and its OS-user to "stit01", with the OS-group set to "dbat01".
So a very simple change.

We brought down the database and the UNIX admins changed the OS-username, its OS-group and renamed file system mount points also, as they also contain the Dev environment name.

When I got the machine back, I ran a command to find all files on the system that belonged to the old user and changed their ownership to the new username and group.
After that I relinked the Oracle binaries, using the "$ORACLE_HOME/bin/relink all" command.

Finally I updated the /etc/oratab, renamed and edited the initSTID01.ora file to match the new configuration and I also created a new password file.

Now trying to connect to the database, it generated an ORA-01017 error on connect as "/ as sysdba":

[stit01@vtl1g152 oraInventory]$ sqlplus / as sysdba

SQL*Plus: Release 12.1.0.2.0 Production on Thu Sep 26 14:50:57 2019

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

ERROR:
ORA-01017: invalid username/password; logon denied


Connecting as user SYS with its password, entered in the password file did work.
So I started working on renaming the Instance and datafile locations using a generated control file script:

SQL>  alter database backup controlfile to trace as '/tmp/crctrlfile.sql';

Afer renaming the database using the generated script the database was opened with the RESETLOGS option.

Now I updated /etc/oratab, /etc/oraInst.loc and the .bash_profile file and reconnected.

Connecting with "/ as sysdba" still generated the same  ORA-01017 however

Remembering a similar issue on IBM AIX, I went browsing the Internet.
Looking up things on Google, the main line of thinking in the results was based on the change of the OS-group. The user running the database, or wanting to connect as sysdba without a password should be member of a privileged dba-group set during the installation.

During the installation of this ORACLE_HOME this group was understandably set to "dbad01".
Now that we are part of group "dbat01" this OS authentication fails.

This brought me into looking into the installation and setup of the ORACLE_HOME.
From ORACLE_HOME I ran a "find" command for all files containing the OS-group "dbad01".
Two files came up as suspicious:

./install/chainedInstall/globalcontext.xml
./rdbms/lib/config.c


Especially the first one got my attention as a line in this file looked like:



Changing this file, setting the VALUE to "dbat01", I ran a "relink all" again.
Still no luck, again an ORA-01017 when trying to connect.

So, back to editing the next file.
Editing the file $ORACLE_HOME/rdbms/lib/config.c showed a whole list of variables set to "dbad01". Looking at the location, this file is used for recompiling the library files.

So we "relink all" again....and.... :

 sqlplus / as sysdba

SQL*Plus: Release 12.1.0.2.0 Production on Thu Sep 26 16:08:01 2019

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

Connected to an idle instance.

SQL>


Yes, we fixed it. This confirms that the OS-group is the variable that controls password less authentication at SYSDBA level.


Now this post reads more or less like a "try or die" post, but in fact it wasn't.
All changes before I went looking for the group-definition in the ORACLE_HOME are basic, well understandable changes that have to be done, when the OS-user and OS-group change on an Oracle database server

For the remainder, I had seen and done this before on IBM AIX, but not yet on Linux. 
The Post is therefore written following me searching for the right file.

Friday, May 17, 2019

Calculating Table Sizes Oracle

In this post I will discuss the growth of tables, and the ways to calculate the space used.
For the newbies among us, an Oracle databases exists of tablespaces, the defined space consisting of datafiles on disk, that hold the databases objects, like tables and indexes.

When a table grows, a tablespace can become full. The origin of this document is from one of these occassions where a tablespace is/was full and I was trying to free up space.

But first some theory:

How do tables grow in Oracle?

Well that's an easy question, one would think, but depending on the way a table is used, the table can either grow slowly, grow very fast, or keep approximately the same size.
  • When, in the last example, a table consists of very volatile data, so many inserts and deletes occur during a certain period of time, the database will use the space freed by the deletes for the new rows.
  • When there are more rows added than there are deleted, a table will grow, as new rows are added at the end of the table, behind the so-called high-water-mark of the table.
  • When an application uses a 'direct-insert' method, mostly seen in bulk-loads, the data is inserted after the high water mark anyway, so the database does in that case not look for free space within the existing occupied space.

That about how a table can grow, and of course this covers not all aspects that are involved.
We will address more dependencies, as we discuss the table's actual size.

First we need make a small sidestep and discuss "statistics".

Oracle database statistics contain information about your data. Statistics information consists e.g. of the number of rows in a table, the average length of a row, the spreading of the data in the different columns and more. Via this link, you can find "Oracle Database Statistics for Dummies".

Now that we now, we can find the number of rows in a table on another way than running a "select count(*)..", then we are able to calculate the size of the data within the table.
With that we can make a seperation between the actual physical size and the used size of a table.

Imagine this bowl being a table. When we have calculated statistics on this bowl, we know that there are -approx.- 100 rows (peas) in this table, with an average row-size of 1 pea.

The physical size of the bowl can be compared with the physical size of the table. This physical size consists of a 100 rows (peas) and a lot of free space, in this case air.

If we wouldn't have table statistics, we would not know the occupied size of the table, but only the physical size. In that case we would not be able to tell anything of the tables usage.

However when we do have statistics, we can tell more about the table's actual size. Oracle has options to shrink a table back to its used size, in order to free up space in the database but also to increase performance of the table, especially with inserts. If a table has a lot of free space, most of the time very fragmented, then the database possibly needs to spend lots of time to find a suitable empty spot in the table, where a row that is inserted would fit.
If we shrink the table to its used size, the database does not need to spent much time on looking for a free place and put the row at the end of the table. This is of course valid for so long, as there will be done new deletes again, which free up usable space again.

Now imagine this situation of a huge table, having a lot of free space in it. The below query shows us that the table INVOICES only has 49% of actual used space:

select ds.owner 
     , ds.segment_name tabname
     , ds.bytes
     , dt.num_rows
     , dt.avg_row_len
     , dt.num_rows * dt.avg_row_len usedsize_bytes
     , round(dt.num_rows * dt.avg_row_len / ds.bytes * 100,1) "%used"
from   dba_segments ds
     , dba_tables dt
where      dt.tablespace_name = 'DATA'
       and ds.tablespace_name = dt.tablespace_name
       and dt.owner = ds.owner
       and dt.table_name = ds.segment_name;

Result:
OWNER   TABNAME         BYTES   NUM_ROWS AVG_ROW_LEN USEDSIZE_BYTES      %used
------- -------- ------------ ---------- ----------- -------------- ----------
FINANCE INVOICES   9768534016    1204707        3976     4789915032         49


Now we can use the 'alter table shrink space' command to reduce the physical size of the table. 

SQL> alter table FINANCE.INVOICES enable row movement;
SQL> alter table FINANCE.INVOICES shrink space; 

This will take some time, depending on the size of a table, but it will shrink the table to its smallest possible size. That is, when everything goes as expected. In our case it didn't. When running the above query again, the numbers now showed:

OWNER   TABNAME         BYTES   NUM_ROWS AVG_ROW_LEN USEDSIZE_BYTES      %used
------- -------- ------------ ---------- ----------- -------------- ----------
FINANCE INVOICES   9753395200    1204707        3976     4789915032         49

The table's physical size decreased just a little, but not as much as expected.
So what causes this behavior, what extra factors can/must be taken into account and why can this table not be brought down to its actual data size ?


The table's definition here is causing the problem itself. The table is setup as following:

CREATE TABLE FINANCE.INVOICES
(
  INVOICE_ID    NUMBER(10)            NOT NULL,
  CUSTNO        NUMBER(8)             NOT NULL,
  INVOICE_DESC  VARCHAR2(4000 CHAR)   NOT NULL,
  INV_AMOUNT    NUMBER(10)
)
TABLESPACE DATA
PCTUSED    0
PCTFREE    10
INITRANS   1
;

With a database blocksize of 8192, it can easily be determined that only one row of the table fits in a block. The actual available blocksize for row-storage is 8192 - 10% ( pctfree ) = 7372 bytes
The average row length of the table is 3972, the maximum row_length is approx. 4028 bytes (not taking into account UTF encoding or whatever)

So, it is easily calculated that every block of this table only holds one row, and no more. This is confirmed by looking at the number of rows in the table compared to the number of blocks the table occupies:

SQL> select sum(blocks) from user_Extents where segment_name = 'INVOICES'
2>   union all
3>   select num_rows from user_Tables where table_name = 'INVOICES';

       CNT
----------
   1190600
   1206840

So this table holds approximately 8192 - 3972 = 4220 bytes of free space in each block it allocates.
That is 3400 bytes of free row space +  10% PCTFREE space = 820 bytes !

We can double check if this is correct, by doing the following math:

physical table size / ( db block size * average_row_length) = 4729063200

This value (almost) matches the value of the USEDSIZE in the above table being 4789915032.



Can we optimize these tables in any way at all??

Well, in our case for every row the database needs to read from disk, a whole block needs to read.
Put it the other way around, we only get one row when we read one block from disk.

If we would create a seperate tablespace for this table with 16kB blocksize, then it would make a difference. With still a PCTFREE of 10% we then would be able to store 3 rows in a single block, a 33% decrease of occupied space!!

A small test showed us that with the current setting (8k blocksize), a test table filled with 69000 rows occupies 68608 blocks  ( approx. 536Mb )

If we recreate the same table in a 16k blocksize tablespace, then the same table only occupies 23040 blocks ( approx. 360Mb )
This saved 33% of physical diskspace as expected.


Conclusion:

Table growth is not only about rows in a table. Tables that are known for frequent data changes, ( inserts/deletes and updates) can benefit from a shrink every now and then.
However, if this does not bring the expected result as seen above, then the DBA or Data Architects can look at other solutions like different blocksize or maybe a change in the structure of a table.