By Clemens Bleile
Network latency is crucial for applications, especially if lots of “row by row”-processing (some call it “slow by slow”-processing) is used by the application or if lots of data is transported between the database and the application. If there’s lots of data to be transported between the database and the application then increasing the fetchsize may help to improve the performance and throughput. But this is not the aim of this blog. What I wanted to provide is a simple script on how to meassure the network latency (network-round-trip-time) when running SQL against your database.
In today’s environment with the Cloud and Standby-DBs, it may easily happen that the application server suddenly is far away from the database server. This causes network latency to go up considerably. But what is the network latency exactly between my application server and my database server?
There’s a very good blog from Frits Hoogland describing network latency and how you may measure it in detail.
What I wanted to achieve however, was to provide a simpler method to measure the network latency. The objective was to install sqlcl on the application server and run a SQL with around 5000 network round trips and measure the network latency by calculating
(“Elapsed Time” – “DB Time”) / “network round trips” = Network Latency
Using sqlcl provides the advantage that I just need to have Java on the application server as a prerequisite.
So the first step is to install sqlcl by downloading it from here and unzip it on the Application Server:
REMARK: Download a current version of sqlcl, becuase I’m using “set feedback only” (to hide the query output), which is not available in older versions.
$ unzip sqlcl-21.4.0.348.1716.zip $ alias sqlcl="$PWD/sqlcl/bin/sql" $ sqlcl /nolog SQLcl: Release 21.4 Production on Wed Dec 29 18:55:47 2021 Copyright (c) 1982, 2021, Oracle. All rights reserved. SQL> quit $
Here the setup-script for my tests:
$ cat setup_test_netlat_random.sql connect sys/password@clemens-oradb/pdb1 as sysdba set echo on drop user test_netlat cascade; create user test_netlat identified by WElcome_netlat_01; grant dba to test_netlat; grant select on v_$mystat to test_netlat; grant select on v_$statname to test_netlat; grant select on v_$SESS_TIME_MODEL to test_netlat; connect test_netlat/WElcome_netlat_01@clemens-oradb/pdb1 drop table tlat purge; create table tlat (id number, filler varchar2(200)); exec dbms_random.seed(0); insert into tlat select rownum,dbms_random.string('L',dbms_random.value(100,100)) from all_objects where rownum <= 5000; commit; exec dbms_stats.gather_table_stats(user,'TLAT'); create or replace function net_roundtrips return number as nrt number; begin select mystat.value into nrt from v$mystat mystat, v$statname statname where mystat.statistic# = statname.statistic# and statname.display_name = 'SQL*Net roundtrips to/from client'; return nrt; end; / create or replace function my_db_time_microsecs return number as mydbtime number; begin select value into mydbtime from V$SESS_TIME_MODEL where sid=SYS_CONTEXT( 'USERENV', 'SID' ) and stat_name='DB time'; return mydbtime; end; / set echo off
Just run it after adjusting it to your needs:
$ sqlcl /nolog SQLcl: Release 21.4 Production on Wed Dec 29 20:37:13 2021 Copyright (c) 1982, 2021, Oracle. All rights reserved. SQL> @setup_test_netlat_random.sql Connected. SQL> drop user test_netlat cascade; User TEST_NETLAT dropped. SQL> create user test_netlat identified by WElcome_netlat_01; User TEST_NETLAT created. SQL> grant dba to test_netlat; Grant succeeded. SQL> grant select on v_$mystat to test_netlat; Grant succeeded. SQL> grant select on v_$statname to test_netlat; Grant succeeded. SQL> grant select on v_$SESS_TIME_MODEL to test_netlat; Grant succeeded. SQL> SQL> connect test_netlat/WElcome_netlat_01@clemens-oradb/pdb1 Connected. SQL> SQL> -- drop table tlat purge; SQL> create table tlat (id number, filler varchar2(200)); Table TLAT created. SQL> exec dbms_random.seed(0); PL/SQL procedure successfully completed. SQL> insert into tlat select rownum,dbms_random.string('L',dbms_random.value(100,100)) from all_objects where rownum commit; Commit complete. SQL> exec dbms_stats.gather_table_stats(user,'TLAT'); PL/SQL procedure successfully completed. SQL> SQL> create or replace function net_roundtrips return number as 2 nrt number; 3 begin 4 select mystat.value into nrt 5 from v$mystat mystat, v$statname statname 6 where mystat.statistic# = statname.statistic# 7 and statname.display_name = 'SQL*Net roundtrips to/from client'; 8 return nrt; 9 end; 10 / Function NET_ROUNDTRIPS compiled SQL> SQL> create or replace function my_db_time_microsecs return number as 2 mydbtime number; 3 begin 4 select value into mydbtime 5 from V$SESS_TIME_MODEL 6 where sid=SYS_CONTEXT( 'USERENV', 'SID' ) 7 and stat_name='DB time'; 8 return mydbtime; 9 end; 10 / Function MY_DB_TIME_MICROSECS compiled SQL> SQL> set echo off SQL>
I.e. I filled a table with 5000 rows. The filler column contains a 100 Bytes long random string. In addition I created 2 functions which return the current statistics for ‘SQL*Net roundtrips to/from client’ and ‘DB time’ for my session.
Below the script for the network latency test (adjust it to meet your needs as well):
$ cat test_netlat.sql connect test_netlat/WElcome_netlat_01@clemens-oradb/pdb1 set echo on var roundtrips_begin number; var roundtrips_end number; var time_begin number; var time_end number; var db_time_start number; var db_time_end number; set feed only arraysize 15 -- select filler from tlat ; -- select filler from tlat ; set arraysize 1 begin :roundtrips_begin := NET_ROUNDTRIPS; :db_time_start := MY_DB_TIME_MICROSECS; :time_begin := dbms_utility.get_time; end; / select filler from tlat ; begin :roundtrips_end := NET_ROUNDTRIPS; :db_time_end := MY_DB_TIME_MICROSECS; :time_end := dbms_utility.get_time; end; / set serveroutput on feed off exec dbms_output.put_line(to_char(:roundtrips_end - :roundtrips_begin)||' network round trips.'); exec dbms_output.put_line(to_char((:time_end - :time_begin)*10)||' ms elapsed time.'); exec dbms_output.put_line(to_char((:db_time_end - :db_time_start)/1000)||' ms DB time.'); exec dbms_output.put_line(to_char(round((((:time_end - :time_begin)*10)-((:db_time_end - :db_time_start)/1000))/(:roundtrips_end - :roundtrips_begin),3))||' ms latency per round trip.'); exec dbms_output.put_line('--> (Elapsed Time - DB Time) / network round trips'); set echo off
What I’m basically doing is to get the number of “SQL*Net roundtrips to/from client” and the “DB Time” of my session before the run. In addition I take the begin-time with the dbms_utility.get_time function. With “arraysize 1” (1 row per fetch) I run a full-table-scan to get the 5000 rows – one by one over the network. After taking statistcs after the run I do produce the output:
– number of network round trips
– elapsed time of the query in ms
– DB time of the query in ms
– the calculated network latency: (Elapsed Time – DB Time) / network round trips
Here an example output:
$ sqlcl /nolog SQLcl: Release 21.4 Production on Wed Dec 29 20:41:26 2021 Copyright (c) 1982, 2021, Oracle. All rights reserved. SQL> @test_netlat Connected. SQL> var roundtrips_begin number; SQL> var roundtrips_end number; SQL> var time_begin number; SQL> var time_end number; SQL> var db_time_start number; SQL> var db_time_end number; SQL> set feed only arraysize 15 SQL> -- select filler from tlat ; SQL> -- select filler from tlat ; SQL> set arraysize 1 SQL> begin 2 :roundtrips_begin := NET_ROUNDTRIPS; 3 :db_time_start := MY_DB_TIME_MICROSECS; 4 :time_begin := dbms_utility.get_time; 5 end; 6 / PL/SQL procedure successfully completed. SQL> SQL> select filler from tlat ; 5,000 rows selected. SQL> SQL> begin 2 :roundtrips_end := NET_ROUNDTRIPS; 3 :db_time_end := MY_DB_TIME_MICROSECS; 4 :time_end := dbms_utility.get_time; 5 end; 6 / PL/SQL procedure successfully completed. SQL> set serveroutput on feed off SQL> exec dbms_output.put_line(to_char(:roundtrips_end - :roundtrips_begin)||' network round trips.'); 4953 network round trips. SQL> exec dbms_output.put_line(to_char((:time_end - :time_begin)*10)||' ms elapsed time.'); 18870 ms elapsed time. SQL> exec dbms_output.put_line(to_char((:db_time_end - :db_time_start)/1000)||' ms DB time.'); 876.625 ms DB time. SQL> exec dbms_output.put_line(to_char(round((((:time_end - :time_begin)*10)-((:db_time_end - :db_time_start)/1000))/(:roundtrips_end - :roundtrips_begin),3))||' ms latency per round trip.'); 3.633 ms latency per round trip. SQL> exec dbms_output.put_line('--> (Elapsed Time - DB Time) / network round trips'); --> (Elapsed Time - DB Time) / network round trips SQL> set echo off SQL>
I.e. I do have an average network latency of 3.633 ms for my 100 Bytes of data.
With the tc-utility I can add a 5ms delay for packets crossing my network interface wlxe84e063dd27f on my Linux-Server:
% tc qdisc add dev wlxe84e063dd27f root netem delay 5ms
Then repeat the test:
SQL> set arraysize 1 SQL> begin 2 :roundtrips_begin := NET_ROUNDTRIPS; 3 :db_time_start := MY_DB_TIME_MICROSECS; 4 :time_begin := dbms_utility.get_time; 5 end; 6 / PL/SQL procedure successfully completed. SQL> SQL> select filler from tlat ; 5,000 rows selected. SQL> SQL> begin 2 :roundtrips_end := NET_ROUNDTRIPS; 3 :db_time_end := MY_DB_TIME_MICROSECS; 4 :time_end := dbms_utility.get_time; 5 end; 6 / PL/SQL procedure successfully completed. SQL> set serveroutput on feed off SQL> exec dbms_output.put_line(to_char(:roundtrips_end - :roundtrips_begin)||' network round trips.'); 4953 network round trips. SQL> exec dbms_output.put_line(to_char((:time_end - :time_begin)*10)||' ms elapsed time.'); 45050 ms elapsed time. SQL> exec dbms_output.put_line(to_char((:db_time_end - :db_time_start)/1000)||' ms DB time.'); 747.075 ms DB time. SQL> exec dbms_output.put_line(to_char(round((((:time_end - :time_begin)*10)-((:db_time_end - :db_time_start)/1000))/(:roundtrips_end - :roundtrips_begin),3))||' ms latency per round trip.'); 8.945 ms latency per round trip. SQL> exec dbms_output.put_line('--> (Elapsed Time - DB Time) / network round trips'); --> (Elapsed Time - DB Time) / network round trips
The latency went up from 3.633 ms on the first run to 8.945 ms on the second run.
The results have been confirmed with the oratcptest-tool from MOS Note “Assessing and Tuning Network Performance for Data Guard and RMAN (Doc ID 2064368.1)”:
$ java -jar oratcptest.jar clemens-oradb -mode=sync -length=100 -duration=20s -interval=20s -port=1550 [Requesting a test] Message payload = 100 bytes Payload content type = RANDOM Delay between messages = NO Number of connections = 1 Socket send buffer = (system default) Transport mode = SYNC Disk write = NO Statistics interval = 20 seconds Test duration = 20 seconds Test frequency = NO Network Timeout = NO (1 Mbyte = 1024x1024 bytes) (21:00:52) The server is ready. Throughput Latency (21:01:12) 0.027 Mbytes/s 3.901 ms (21:01:12) Test finished. Socket send buffer = 131768 bytes Avg. throughput = 0.027 Mbytes/s Avg. latency = 3.901 ms
With the 5ms additional latency:
$ java -jar oratcptest.jar clemens-oradb -mode=sync -length=100 -duration=20s -interval=20s -port=1550 [Requesting a test] Message payload = 100 bytes Payload content type = RANDOM Delay between messages = NO Number of connections = 1 Socket send buffer = (system default) Transport mode = SYNC Disk write = NO Statistics interval = 20 seconds Test duration = 20 seconds Test frequency = NO Network Timeout = NO (1 Mbyte = 1024x1024 bytes) (21:02:05) The server is ready. Throughput Latency (21:02:25) 0.011 Mbytes/s 9.293 ms (21:02:25) Test finished. Socket send buffer = 131768 bytes Avg. throughput = 0.011 Mbytes/s Avg. latency = 9.293 ms
To cleanup just drop the DB-user test_netlat:
SQL> drop user test_netlat cascade;
and (if set) remove the network delay on the network card:
% tc qdisc del dev wlxe84e063dd27f root
Summary: If you plan to have a longer distance between the application-server and the db-server (e.g. when going to the Cloud) then test the latency carefully. If there is a high number of network round trips between the application and the database then chances are high that the application becomes MUCH slower after moving the servers away from each other. Here the statistics about network round trips in your database since startup:
SQL> select value from v$sysstat sysstat, v$statname statname 2 where sysstat.statistic# = statname.statistic# 3 and statname.display_name = 'SQL*Net roundtrips to/from client';
Cet article Script to calculate the network latency between the application and the Oracle DB-server est apparu en premier sur Blog dbi services.