Performance tests IRIS - PostgreSQL - MySQL using Python
Tags: database, performance, python, testing
Published: 2024-06-16 16:17:52

<p>It seems like yesterday when we did a small project in Java to test the performance of IRIS, PostgreSQL and MySQL (you can review the article we wrote back in June at the end of this article). If you remember, IRIS was superior to PostgreSQL and clearly superior to MySQL in insertions, with no big difference in queries.</p>
<p>Well, shortly after @Dmitry.Maslennikov told me "Why don't you test it from a Python project?" Well, here is the Python version of the tests we previously performed using the JDBC connections.</p>
<p>First of all, let you know that I am not an expert in Python, so if you see anything that could be improved, do not hesitate to contact me.</p>
<p>For this example I have used Jupyter Notebook, which greatly simplifies Python development and allows us to see step by step what we are doing. Associated with this article you have the application so that you can do your own tests.</p>
<h2>Warning for Windows users</h2>
<p>If you clone the GitHub project in Visual Studio Code you may have to change the default end of line configuration from CRLF to LF to be able to correctly deploy the containers:</p>

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/px0sb01l2soqm9rjpf00.png)
<p>If you are going to try to reproduce the tests on your computers, you must take the following into consideration: Docker Desktop will request permissions to access the folders on your computers that it needs to deploy the project. If you have not configured access permission to these folders before launching the Docker containers, the initial creation of the test table in PostgreSQL will fail, so before launching the project you must configure shared access to the project folders in your DockerDesktop.</p>
<p>To do this you must access <strong>Settings -&gt; Resources -&gt; File sharing</strong> and add the folder where you have cloned the project to the list:</p>

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/50z9sud8mne505ko48y3.png)
<p>You are warned!</p>
<h2>&nbsp;</h2>
<h2>Test of performance</h2>
<p>For these tests we will use a fairly simple table with the most basic information possible about a patient. Here you can see the command to create the table in SQL:</p>
<pre class="codeblock-container" idlang="1" lang="SQL" tabsize="4"><code class="language-sql hljs"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> Test.Patient (
    <span class="hljs-keyword">Name</span> <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">225</span>),
    Lastname <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">225</span>),
    Photo <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">5000</span>),
    Phone <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">14</span>),
    Address <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">225</span>)    
)</code></pre>
<p>As you can see, we have defined the patient's photo as a VARCHAR(5000), the reason for this is because here we are going to include (theoretically) the vectorized information of the photo. A few months ago I published an article explaining how using Embedded Python we could implement IRIS, a facial recognition system (<a href="https://community.intersystems.com/post/facial-recognition-embedded-python-and-iris" target="_blank">here</a>) where you can see how images are transformed into vectors for later comparison. Well, the issue of vectorization comes from the fact that said vector format is the norm in many Machine Learning models and it never hurts to test with something similar to reality (just something).</p>
<h3>&nbsp;</h3>
<h3>Jupyter Notebook Setup</h3>
<p>To simplify the development of the project in Python as much as possible, I have used the magnificent Jupyter Notebook tool that allows us to develop each of the functionalities that we will need step by step.</p>
<p>Here's a look at our Jupyter:</p>

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tc333jux9ihcwvws966v.png)
<p>Let's take a look at the most interesting points of it:</p>
<h4>Importing libraries:</h4>
<pre class="codeblock-container" idlang="2" lang="Python" tabsize="4"><code class="language-python hljs"><span class="hljs-keyword">import</span> iris
<span class="hljs-keyword">import</span> names
<span class="hljs-keyword">import</span> numpy <span class="hljs-keyword">as</span> np
<span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime
<span class="hljs-keyword">import</span> psycopg2
<span class="hljs-keyword">import</span> mysql.connector
<span class="hljs-keyword">import</span> matplotlib.pyplot <span class="hljs-keyword">as</span> plt
<span class="hljs-keyword">import</span> random_address
<span class="hljs-keyword">from</span> phone_gen <span class="hljs-keyword">import</span> PhoneNumber</code></pre>
<h4>Connecting to the databases:</h4>
<p>IRIS:</p>
<pre class="codeblock-container" idlang="2" lang="Python" tabsize="4"><code class="language-python hljs">connection_string = <span class="hljs-string">"iris:1972/TEST"</span>
username = <span class="hljs-string">"superuser"</span>
password = <span class="hljs-string">"SYS"</span>
connectionIRIS = iris.connect(connection_string, username, password)
cursorIRIS = connectionIRIS.cursor()
print(<span class="hljs-string">"Connected"</span>)</code></pre>
<p>PostgreSQL:</p>
<pre class="codeblock-container" idlang="2" lang="Python" tabsize="4"><code class="language-python hljs">connectionPostgres = psycopg2.connect(database=<span class="hljs-string">"testuser"</span>,
                        host=<span class="hljs-string">"postgres"</span>,
                        user=<span class="hljs-string">"testuser"</span>,
                        password=<span class="hljs-string">"testpassword"</span>,
                        port=<span class="hljs-string">"5432"</span>)
cursorPostgres = connectionPostgres.cursor()
print(<span class="hljs-string">"Connected"</span>)</code></pre>
<p>MySQL:</p>
<pre class="codeblock-container" idlang="2" lang="Python" tabsize="4"><code class="language-python hljs">connectionMySQL = mysql.connector.connect(
  host=<span class="hljs-string">"mysql"</span>,
  user=<span class="hljs-string">"testuser"</span>,
  password=<span class="hljs-string">"testpassword"</span>
)
cursorMySQL = connectionMySQL.cursor()
print(<span class="hljs-string">"Connected"</span>)</code></pre>
<h4>Generation of the values to be inserted</h4>
<pre class="codeblock-container" idlang="2" lang="Python" tabsize="4"><code class="language-python hljs">phone_number = PhoneNumber(<span class="hljs-string">"USA"</span>)
resultsIRIS = []
resultsPostgres = []
resultsMySQL = []
parameters =  []
<span class="hljs-keyword">for</span> x <span class="hljs-keyword">in</span> range(<span class="hljs-number">1000</span>):
    rng = np.random.default_rng()
    parameter = []
    parameter.append(names.get_first_name())
    parameter.append(names.get_last_name())
    parameter.append(str(rng.standard_normal(<span class="hljs-number">50</span>)))
    parameter.append(phone_number.get_number())
    parameter.append(random_address.real_random_address_by_state(<span class="hljs-string">'CA'</span>)[<span class="hljs-string">'address1'</span>])
    parameters.append(parameter)

print(<span class="hljs-string">"Parameters built"</span>)</code></pre>
<h4>Insertion into IRIS</h4>
<pre class="codeblock-container" idlang="2" lang="Python" tabsize="4"><code class="language-python hljs">date_before = datetime.now()

cursorIRIS.executemany(<span class="hljs-string">"INSERT INTO Test.Patient (Name, Lastname, Photo, Phone, Address) VALUES (?, ?, ?, ?, ?)"</span>, parameters)
connectionIRIS.commit()
difference = datetime.now() - date_before
print(difference.total_seconds())
resultsIRIS.append(difference.total_seconds())</code></pre>
<h4>Insertion into PostgreSQL</h4>
<pre class="codeblock-container" idlang="2" lang="Python" tabsize="4"><code class="language-python hljs">date_before = datetime.now()
    
cursorPostgres.executemany(<span class="hljs-string">"INSERT INTO test.patient (name, lastname, photo, phone, address) VALUES (%s,%s,%s,%s,%s)"</span>, parameters)
connectionPostgres.commit()
difference = datetime.now() - date_before
print(difference.total_seconds())
resultsPostgres.append(difference.total_seconds())</code></pre>
<h4>Insertion into MySQL</h4>
<pre class="codeblock-container" idlang="2" lang="Python" tabsize="4"><code class="language-python hljs">date_before = datetime.now()
    
cursorMySQL.executemany(<span class="hljs-string">"INSERT INTO test.patient (name, lastname, photo, phone, address) VALUES (%s,%s,%s,%s,%s)"</span>, parameters)
connectionMySQL.commit()
difference = datetime.now() - date_before
print(difference.total_seconds())
resultsMySQL.append(difference.total_seconds())</code></pre>
<p>For our test I have decided to insert the following set of values for each database:</p>
<ul>
<li>1 insertion with 1000 patients.</li>
<li>1 insertion with 5000 patients.</li>
<li>1 insertion with 20,000 patients.</li>
<li>1 insertion with 50,000 patients.</li>
</ul>
<p>Keep in mind when performing the tests that the longest time spent is creating the values to be inserted by Python. To bring it closer to reality, I have launched several tests in advance so that the databases already have a significant set of records (around 200,000 records).</p>
<h2>Test results</h2>
<h3>Insertion of 1000 patients:</h3>
<ul>
<li>InterSystems IRIS: 0.037949 seconds.</li>
<li>PostgreSQL: 0.106508 seconds.</li>
<li>MySQL: 0.053338 seconds.</li>
</ul>
<h3>Insertion of 5,000 patients:</h3>
<ul>
<li>InterSystems IRIS: 0.162791 seconds.</li>
<li>PostgreSQL: 0.432642 seconds.</li>
<li>MySQL: 0.18925 seconds.</li>
</ul>
<h3>Insertion of 20,000 patients:</h3>
<ul>
<li>InterSystems IRIS: 0.601944 seconds.</li>
<li>PostgreSQL: 1.803113 seconds.</li>
<li>MySQL: 0.594396 seconds.</li>
</ul>
<h3>Insertion of 50,000 patients:</h3>
<ul>
<li>InterSystems IRIS: 1.482824 seconds.</li>
<li>PostgreSQL: 4.581251 seconds.</li>
<li>MySQL: 2.162996 seconds.</li>
</ul>

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/43uhn71id7m8ba9202xi.png)
<p>Although this is a fairly simple test, it is very significant since it allows us to see the trend of each database regarding insertion performance.</p>
<h2>Conclusions</h2>
<p>If we compare the performance in the tests carried out with the Java project and the current one in Python we will see that on this occasion the behavior of PostgreSQL is clearly inferior to the Python project, being 4 times slower than InterSystems IRIS,&nbsp;&nbsp;while MySQL has improved compared to to the Java version.</p>
<p>Unquestionably InterSystems IRIS remains the best of the three, with more linear behavior and better insertion performance, regardless of the technology used.</p>
<h2>Technical characteristics of the laptop used for the tests:</h2>
<ul>
<li>Operative System: Microsoft Windows 11 Pro.</li>
<li>Processor: 13th Gen Intel(R) Core(TM) i9-13900H, 2600 Mhz.</li>
<li>RAM: 64 GB.</li>
</ul>