Posted
over 4 years
ago
by
Continuent
Zero Downtime SaaS Provider — How to Easily Deploy MySQL Clusters in AWS and Recover from Multi-Zone AWS Outages
This is the second post in a series of blogs in which we cover a number of different Continuent Tungsten customer use cases that center
... [More]
around achieving continuous MySQL operations with commercial-grade high availability (HA), geographically redundant disaster recovery (DR) and global scaling – and how we help our customers achieve this.
This use case looks at a multi-year (since 2012) Continent customer who is a large Florida-based SaaS provider dealing with sensitive (HIPAA Compliant) medical data, which offers electronic health records, practice management, revenue cycle management and data analytics for thousands of doctors.
What is the Challenge?
Lack of high availability in AWS. The challenge they were facing came from using AWS, which allowed them to rapidly provision database and application servers – BUT the instances, underlying storage, and management interface were not highly available.
What is the Solution?
By using Tungsten Clustering, they could and can quickly deploy large numbers of Tungsten MySQL clusters in AWS and recover from multi-zone AWS outages. The solution uses the Composite Active/Passive Tungsten Clustering topology – a Pod Architecture with multiple 3-node Active Clusters (for High Availability) and 3-node Passive Clusters (for Disaster Recovery).
The Pod Architecture provides practically infinite scalability for the SaaS providers as they can just keep adding new Pods when and as their customer base grows. Each Pod includes a 3-node Tungsten Cluster deployed in multi-AZ AWS, and a 3-node DR cluster deployed in another AWS Region with all sensitive traffic encrypted in flight (this covers application traffic as well).
What are the Benefits?
The benefits this SaaS provider customer is able to reap from our solution include continuous operations, high availability, scalability and better data protection.
One very important item, often overlooked, is that beside the excellent MySQL HA/DR/Geo-clustering software we offer, Continuent also helps sustain our customer’s continuous operations with our 24/7/365 support.
Our average response time for urgent support requests has been less than three (3) minutes during the past two years. All requests are handled by highly qualified MySQL experts with decades of operational experience with business-critical deployments.
This use case, like the others we’re covering in this blog series, clearly showcases how our solutions and best practices can help customers who manage business-critical MySQL environments achieve the availability, scalability and safe operations with fast response times they are looking for.
Related Resources
Use Case Blog: Geo-Scale MySQL for Continuous Global Operations & Fast Response Times
Technical Product Blog: Global Read-Scaling for MySQL / MariaDB / Percona Server using Tungsten Clustering
Technical Blog: How To Architect MySQL to be Available No Matter What
Technical Blog: How To Architect MySQL for Zero Downtime Maintenance
About Tungsten Clustering
Tungsten Clustering allows enterprises running business-critical MySQL database applications to cost-effectively achieve continuous operations with commercial-grade high availability (HA), geographically redundant disaster recovery (DR) and global scaling.
To find out more, visit our Tungsten Clustering product page.
[Less]
|
Posted
over 4 years
ago
by
MySQL Performance Blog
MySQL has been ranked as the second most popular database since 2012 according to DB-Engines. Three features help it retain its top position: replication, storage engines, and NoSQL support. During this webinar, we’ll discuss the MySQL architecture
... [More]
surrounding these features and how to utilize their full power when your application hits performance or design limits.
This webinar is geared towards new MySQL users as well as those with other database administration experience. However, it’s also useful for experienced users looking to refresh their knowledge.
Please join Sveta Smirnova on Thurs, Dec 19, 10 to 11 am PST.
Register Now
If you can’t attend, sign up anyways we’ll send you the slides and recording afterward.
[Less]
|
Posted
over 4 years
ago
by
Netflix
Andreas Andreakis, Ioannis Papapanagiotou
Continue reading on Netflix TechBlog »
|
Posted
over 4 years
ago
by
Olivier Dasini
This webinar will cover the advantages and process for migrating from MariaDB/Galera cluster to MySQL InnoDB Cluster.
|
Posted
over 4 years
ago
by
Federico Razzoli
Relational DBMSs allow to grant users permissions on certain tables or columns. Here we'll discuss how to restrict access to a certain set of rows.
|
Posted
over 4 years
ago
by
Percona Community
Database administrators are responsible for maintaining the privacy and integrity of data. When the data contains confidential information, your company has a legal obligation to ensure that privacy is maintained. Even so, being able to access the
... [More]
information contained in that dataset, for example for testing or reporting purposes, has great value so what to do? MySQL Enterprise Edition offers data masking and de-identification, so I decided to contribute similar functionality to Percona Server for MySQL. In this post, I provide some background context and information on how to use these new functions in practice.
Some context
One of the most important assets of any company is data. Having good data allows engineers to build better systems and user experiences.
Even through our most trivial activities, we continuously generate and share great volumes of data. I’m walking down the street and if I take a look at my phone it’s quite straightforward to get recommendations for a place to have lunch. The platform knows that it’s almost lunch time and that I have visited this nearby restaurant, or a similar one, a few times in the past. Sounds cool, right?
But this process could be more manual than we might think at first. Even if the system has implemented things like AI or Machine Learning, a human will have validated the results; they might have taken a peek to ensure that everything is fine; or perhaps they are developing some new cool feature that must be tested… And this means that someone, somewhere has the ability to access my data. Or your data.
Now, that is not so great, is it?
In the last decade or so, governments around the world have taken this challenge quite seriously. They have enforced a series of rules to guarantee that the data is not only safely stored, but also safely used. I’m sure you will have heard terms like PCI, GDPR or HIPAA. They contain mandatory guidelines for how our data can be used, for primary or secondary purposes, and if it can be used at all.
Data masking and de-identification
One of the most basic safeguarding rules is that if the data is to be used for secondary purposes – such as for data analytics – it has to be de-identified in a way that it would make impossible identify the original individual.
Let’s say that the company ACME is storing employee data.
We will use the example database of employees that’s freely available.
Employee number
First name
Last name
Birth date
Gender
Hire date
Gross salary
Salary from date
Salary to date
We can clearly see that all those fields can be classified as private information. Some of these directly identify the original individual, like employee number or first + last name. Others could be used for indirect identification: I could ask my co-workers their birthday and guess the owner of that data using
birth date.
So, here is where de-identification and data-masking come into play. But what are the differences?
De-identification transforms the original data into something different that could look more or less real. For example, I could de-identify
birth date and get a different date.
However, this method would make that information unusable if I want to see the relationship between salary and employee’s age.
On the other hand, data-masking transforms the original data leaving some part untouched. I could mask
birth date replacing the month and day for January first. That way, the year would be retained and that would allow us to identify that salary–employee’s age relationship.
Of course, if the dataset I’m working with is not big enough, certain methods of data-masking would be inappropriate as I could still deduce who the data belonged to.
MySQL data masking
Oracle’s MySQL Enterprise Edition offers a de-identification and data-masking solution for MySQL, using a flexible set of functions that cover most of our needs.
Percona Server for MySQL 8.0.17 introduces that functionality as an open source plugin, and is compatible with Oracle’s implementation. You no longer need to code slow and complicated stored procedures to achieve data masking, and you can migrate the processes that were written for the MySQL Enterprise Edition to Percona Server for MySQL. Go grab a cup of coffee and contribute something cool to the community with all that time you have got back. ☺
In the lab
Put on your thinking cap and let’s see how it works.
First we need an instance of Percona MySQL Server 8.0.17 or newer. I think containers are the most flexible way to test new stuff so I will be using that, but you could use a virtual server or just a traditional setup. Let’s download the latest version of Percona MySQL Server in a ready to run container:
docker pull percona:8.0.17-1
Eventually that command should work but sadly, Percona hadn’t built this version of the docker image when this article was written. Doing it yourself is quite simple, though, and by the time you read this it will likely be already there.
Once in place, Running an instance of Percona MySQL Server has never been so easy:
docker run --name ps -e MYSQL_ROOT_PASSWORD=secret -d percona:8.0.17-8
We’ll logon to the new container:
docker exec -ti ps mysql -u root -p
Now is the time to download the test database employees from GitHub and load it into our Percona Server. You can follow the official instructions in the project page.
Next step is to enable the data de-identification and masking feature. Installing the data masking module in Percona MySQL Server is easier than in Oracle.
mysql> INSTALL PLUGIN data_masking SONAME 'data_masking.so';
Query OK, 0 rows affected (0.06 sec)
This automatically defines a set of global functions in our MySQL instance, so we don’t need to do anything else.
A new concept: Dictionaries
Sometimes we will like to generate new data selecting values from a predefined collection. For example we could want to have
first name values that are really first names and not a random alphanumeric. This will make our masked data looks real, and it’s perfect for creating demo or QA environments.
For this task we have dictionaries. They are nothing more than text files containing a value per line that are loaded into MySQL memory. You need to be aware that the contents of the file are fully loaded into memory and that the dictionary only exists while MySQL is running. So keep this in mind before loading any huge file or after restarting the instance.
For our lab we will load two dictionaries holding first and last names. You can use these files or create different ones: first names and last names
Store the files in a folder of your database server (or container) readable by the
mysqld process.wget https://raw.githubusercontent.com/philipperemy/name-dataset/master/names_dataset/first_names.all.txt
docker cp first_names.all.txt ps:/tmp/
wget https://raw.githubusercontent.com/philipperemy/name-dataset/master/names_dataset/last_names.all.txt
docker cp last_names.all.txt ps:/tmp/
Once the files are in our server we can map them as MySQL dictionaries.
mysql> select gen_dictionary_load('/tmp/first_names.all.txt', 'first_names');
+----------------------------------------------------------------+
| gen_dictionary_load('/tmp/first_names.all.txt', 'first_names') |
+----------------------------------------------------------------+
| Dictionary load success |
+----------------------------------------------------------------+
1 row in set (0.04 sec)
mysql> select gen_dictionary_load('/tmp/last_names.all.txt', 'last_names');
+--------------------------------------------------------------+
| gen_dictionary_load('/tmp/last_names.all.txt', 'last_names') |
+--------------------------------------------------------------+
| Dictionary load success |
+--------------------------------------------------------------+
1 row in set (0.03 sec)
Masking some data
Now let’s take another look at our
employees tablemysql> show columns from employees;
+------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------+------+-----+---------+-------+
| emp_no | int(11) | NO | PRI | NULL | |
| birth_date | date | NO | | NULL | |
| first_name | varchar(14) | NO | | NULL | |
| last_name | varchar(16) | NO | | NULL | |
| gender | enum('M','F') | NO | | NULL | |
| hire_date | date | NO | | NULL | |
+------------+---------------+------+-----+---------+-------+
Ok, it’s very likely we will want to de-identify everything in this table. You can apply different methods to achieve your security requirements, but I will create a view with the following transformations:
emp_no: get a random value from 900.000.000 to 999.999.999
birth_date: set it to January 1st of the original year
first_name: set a random first name from a list of names that we have in a text file
last_name: set a random last name from a list of names that we have in a text file
gender: no transformation
hire_date: set it to January 1st of the original year
CREATE VIEW deidentified_employees
AS
SELECT
gen_range(900000000, 999999999) as emp_no,
makedate(year(birth_date), 1) as birth_date,
gen_dictionary('first_names') as first_name,
gen_dictionary('last_names') as last_name,
gender,
makedate(year(hire_date), 1) as hire_date
FROM employees;
Let’s check how the data looks in our de-identified view.
mysql> SELECT * FROM employees LIMIT 10;
+--------+------------+------------+-----------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date |
+--------+------------+------------+-----------+--------+------------+
| 10001 | 1953-09-02 | Georgi | Facello | M | 1986-06-26 |
| 10002 | 1964-06-02 | Bezalel | Simmel | F | 1985-11-21 |
| 10003 | 1959-12-03 | Parto | Bamford | M | 1986-08-28 |
| 10004 | 1954-05-01 | Chirstian | Koblick | M | 1986-12-01 |
| 10005 | 1955-01-21 | Kyoichi | Maliniak | M | 1989-09-12 |
| 10006 | 1953-04-20 | Anneke | Preusig | F | 1989-06-02 |
| 10007 | 1957-05-23 | Tzvetan | Zielinski | F | 1989-02-10 |
| 10008 | 1958-02-19 | Saniya | Kalloufi | M | 1994-09-15 |
| 10009 | 1952-04-19 | Sumant | Peac | F | 1985-02-18 |
| 10010 | 1963-06-01 | Duangkaew | Piveteau | F | 1989-08-24 |
+--------+------------+------------+-----------+--------+------------+
10 rows in set (0.00 sec)
mysql> SELECT * FROM deidentified_employees LIMIT 10;
+-----------+------------+------------+---------------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date |
+-----------+------------+------------+---------------+--------+------------+
| 930277580 | 1953-01-01 | skaidrīte | molash | M | 1986-01-01 |
| 999241458 | 1964-01-01 | grasen | cessna | F | 1985-01-01 |
| 951699030 | 1959-01-01 | imelda | josephpauline | M | 1986-01-01 |
| 985905688 | 1954-01-01 | dunc | burkhardt | M | 1986-01-01 |
| 923987335 | 1955-01-01 | karel | wanamaker | M | 1989-01-01 |
| 917751275 | 1953-01-01 | mikrut | allee | F | 1989-01-01 |
| 992344830 | 1957-01-01 | troyvon | muma | F | 1989-01-01 |
| 980277046 | 1958-01-01 | aliziah | tiwnkal | M | 1994-01-01 |
| 964622691 | 1952-01-01 | dominiq | legnon | F | 1985-01-01 |
| 948247243 | 1963-01-01 | sedale | tunby | F | 1989-01-01 |
+-----------+------------+------------+---------------+--------+------------+
10 rows in set (0.01 sec)
The data looks quite different, but remains good enough to apply some analytics and get meaningful results.
Let’s de-identify the table
salaries this time.mysql> show columns from salaries;
+-----------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------+------+-----+---------+-------+
| emp_no | int(11) | NO | PRI | NULL | |
| salary | int(11) | NO | | NULL | |
| from_date | date | NO | PRI | NULL | |
| to_date | date | NO | | NULL | |
+-----------+---------+------+-----+---------+-------+
We could use something like this:
CREATE VIEW deidentified_salaries
AS
SELECT
gen_range(900000000, 999999999) as emp_no,
gen_range(40000, 80000) as salary,
mask_inner(date_format(from_date, '%Y-%m-%d'), 4, 0) as from_date,
mask_outer(date_format(to_date, '%Y-%m-%d'), 4, 2, '0') as to_date
FROM salaries;
We are using again the function
gen_range . For the dates this time we are using the very flexible functions mask_inner and mask_outer that replace some characters in the original string. Let’s see how the data looks now.
In a real life exercise we would like to have the same values for emp_no across all the tables to keep referential integrity. This is where I think the original MySQL data-masking plugin falls short, as we don’t have deterministic functions using the original value as seed.
mysql> SELECT * FROM salaries LIMIT 10;
+--------+--------+------------+------------+
| emp_no | salary | from_date | to_date |
+--------+--------+------------+------------+
| 10001 | 60117 | 1986-06-26 | 1987-06-26 |
| 10001 | 62102 | 1987-06-26 | 1988-06-25 |
| 10001 | 66074 | 1988-06-25 | 1989-06-25 |
| 10001 | 66596 | 1989-06-25 | 1990-06-25 |
| 10001 | 66961 | 1990-06-25 | 1991-06-25 |
| 10001 | 71046 | 1991-06-25 | 1992-06-24 |
| 10001 | 74333 | 1992-06-24 | 1993-06-24 |
| 10001 | 75286 | 1993-06-24 | 1994-06-24 |
| 10001 | 75994 | 1994-06-24 | 1995-06-24 |
| 10001 | 76884 | 1995-06-24 | 1996-06-23 |
+--------+--------+------------+------------+
10 rows in set (0.00 sec)
mysql> SELECT * FROM deidentified_salaries LIMIT 10;
+-----------+--------+------------+------------+
| emp_no | salary | from_date | to_date |
+-----------+--------+------------+------------+
| 929824695 | 61543 | 1986XXXXXX | 0000-06-00 |
| 954275265 | 63138 | 1987XXXXXX | 0000-06-00 |
| 948145700 | 53448 | 1988XXXXXX | 0000-06-00 |
| 937927997 | 54704 | 1989XXXXXX | 0000-06-00 |
| 978459605 | 78179 | 1990XXXXXX | 0000-06-00 |
| 993464164 | 75526 | 1991XXXXXX | 0000-06-00 |
| 946692434 | 51788 | 1992XXXXXX | 0000-06-00 |
| 979870243 | 54807 | 1993XXXXXX | 0000-06-00 |
| 958708118 | 70647 | 1994XXXXXX | 0000-06-00 |
| 945701146 | 76056 | 1995XXXXXX | 0000-06-00 |
+-----------+--------+------------+------------+
10 rows in set (0.00 sec)
Clean-up
Remember that when you’re done, you can free up memory by removing the dictionaries. Restarting the instance will also remove the dictionaries.
mysql> SELECT gen_dictionary_drop('first_names');
+------------------------------------+
| gen_dictionary_drop('first_names') |
+------------------------------------+
| Dictionary removed |
+------------------------------------+
1 row in set (0.01 sec)
mysql> SELECT gen_dictionary_drop('last_names');
+------------------------------------+
| gen_dictionary_drop('last_names') |
+------------------------------------+
| Dictionary removed |
+------------------------------------+
1 row in set (0.01 sec)
If you use the MySQL data-masking plugin to define different levels of access to the data, remember that you will need to load the dictionaries each time the instance is restarted. With this usage, for example, you could control the data that someone in support has access to, very much like a bargain-basement virtual private database solution. (I’m not proposing this for production systems!)
Other de-identification and masking functions
Percona Server for MySQL Data-Masking includes more functions that the ones we’ve seen here.
We have specialized functions for Primary Account Numbers (PAN), Social Security Numbers (SSN), phone numbers, e-Mail addresses… And also generic functions that will allow us to de-identify types without a specialized method.
Being an open source plugin it should be quite easy to implement any additional methods and contribute it to the broader community.
Next Steps
Using these functions we can de-identify and mask any existing dataset. But if you are populating a lower level environment using production data you would want to store the transformed data only. To achieve this you could choose between various options.
Small volumes of data: use “de-identified” views to export the data and load into a new database using mysqldump or mysqlpump.
Medium volumes of data: Clone the original database and de-identify locally the data using updates.
Large volumes of data option one: using replication, create a master -> slave chain with STATEMENT
binlog format and define triggers de-identifying the data on the slave. Your master can be a slave to the master (using log_slave_updates), so you don’t need to run your primary master in STATEMENT
mode.
Large volumes of data option two: using multiplexing in ProxySQL, configure ProxySQL to send writes to a clone server where you have defined triggers to de-identify the data.
Future developments
While de-identifying complex schemas we could find that, for example, the name of a person is stored in multiple tables (de-normalized tables). In this case, these functions would generate different names and the resulting data will look broken. You can solve this using a variant of the dictionary functions that will obtain the value based on the original value and passed as parameter:
gen_dictionary_deterministic('Francisco', 'first_names')
This not-yet-available function would always return the same value using that dictionary file, but in such a way that the de-identification cannot be reversed.
Oracle doesn’t currently support this, so we will expand Percona Data-Masking plugin to introduce this as a unique feature. However, that will be in another contribution, so stay tuned for more exciting changes to Percona Server for MySQL Data Masking.
—Image: Photo by Finan Akbar on Unsplash
The content in this blog is provided in good faith by members of the open source community. Percona has not edited or tested the technical content (although in this case, of course, we have tested the data masking feature incorporated into Percona Server for MySQL 8.0/17, just not the examples in this blog). Views expressed are the authors’ own. When using the advice from this or any other online resource test ideas before applying them to your production systems, and always secure a working back up.
The post Percona Server for MySQL 8.0 – New Data Masking Feature appeared first on Percona Community Blog.
[Less]
|
Posted
over 4 years
ago
by
Frederic Descamps
See some update at the end: 23 Dec 2019
MySQL and PHP is a love story that started long time ago. However the love story with MySQL 8.0 was a bit slower to start… but don’t worry it rules now !
The support of MySQL 8.0’s new default
... [More]
authentication method in PHP took some time and was added in PHP 7.2.8 but removed in PHP 7.2.11.
Now it’s fully supported in PHP 7.4 !
If you have installed PHP 7.4, you can see that the new plugin auth_plugin_caching_sha2_passwordis now available:
# php -i | grep "Loaded plugins\|PHP Version " | tail -n2
PHP Warning: Module 'mysql_xdevapi' already loaded in Unknown on line 0
PHP Version => 7.4.0
Loaded plugins => mysqlnd,debug_trace,auth_plugin_mysql_native_password,
auth_plugin_mysql_clear_password,
auth_plugin_caching_sha2_password,
auth_plugin_sha256_password
So no need to create a user with mysql_native_passwordas authentication method in MySQL 8.0 as explained in the following posts:
https://lefred.be/content/mysql-8-0-17-and-drupal-8-7/
https://lefred.be/content/migrating-to-mysql-8-0-for-wordpress-episode-1/
https://lefred.be/content/drupal-and-mysql-8-0-11-are-we-there-yet/
https://lefred.be/content/joomla-and-mysql-8-0-12/
https://lefred.be/content/mysql-8-0-and-magento/
In summary, if you want to use a more secure method to connect to your MySQL 8.0 form your PHP application, make sure you upgrade to PHP 7.4
Update:
The drawback of this new mysqli.so is that if you don’t modify the php.ini and by adding a value to mysqli.default_socket, when you try to connect to MySQL on localhost without specifying the socket path, the connection will fail with the following message:
PHP Warning: mysqli::__construct(): (HY000/2002):
No such file or directory in .php on line 45
An error occurred when trying to establish a connection to the database: Error #2002
You have then 2 solutions:
add in php.ini a default value for mysqli.default_socket
or specify the socket path when you initiate the connection to MySQL using mysqli in your code
This is also the default behavior on Ubuntu when using PHP 7.4 from ppa:ondrej/php repository.
[Less]
|
Posted
over 4 years
ago
by
Frederic Descamps
MySQL and PHP is a love story that started long time ago. However the love story with MySQL 8.0 was a bit slower to start… but don’t worry it rules now !
The support of MySQL 8.0’s new default authentication method in PHP took some time and was
... [More]
added in PHP 7.2.8 but removed in PHP 7.2.11.
Now it’s fully supported in PHP 7.4 !
If you have installed PHP 7.4, you can see that the new plugin auth_plugin_caching_sha2_passwordis now available:
# php -i | grep "Loaded plugins\|PHP Version " | tail -n2
PHP Warning: Module 'mysql_xdevapi' already loaded in Unknown on line 0
PHP Version => 7.4.0
Loaded plugins => mysqlnd,debug_trace,auth_plugin_mysql_native_password,
auth_plugin_mysql_clear_password,
auth_plugin_caching_sha2_password,
auth_plugin_sha256_password
So no need to create a user with mysql_native_passwordas authentication method in MySQL 8.0 as explained in the following posts:
https://lefred.be/content/mysql-8-0-17-and-drupal-8-7/
https://lefred.be/content/migrating-to-mysql-8-0-for-wordpress-episode-1/
https://lefred.be/content/drupal-and-mysql-8-0-11-are-we-there-yet/
https://lefred.be/content/joomla-and-mysql-8-0-12/
https://lefred.be/content/mysql-8-0-and-magento/
In summary, if you want to use a more secure method to connect to your MySQL 8.0 form your PHP application, make sure you upgrade to PHP 7.4
[Less]
|
Posted
over 4 years
ago
by
Frederic Descamps
MySQL and PHP is a love story that started long time ago. However the love story with MySQL 8.0 was a bit slower to start… but don’t worry it rules now !
The support of MySQL 8.0’s new default authentication method in PHP took some time and was
... [More]
added in PHP 7.2.8 but removed in PHP 7.2.11.
Now it’s fully supported in PHP 7.4 !
If you have installed PHP 7.4, you can see that the new plugin auth_plugin_caching_sha2_passwordis now available:
# php -i | grep "Loaded plugins|PHP Version " | tail -n2
PHP Warning: Module 'mysql_xdevapi' already loaded in Unknown on line 0
PHP Version => 7.4.0
Loaded plugins => mysqlnd,debug_trace,auth_plugin_mysql_native_password,
auth_plugin_mysql_clear_password,
auth_plugin_caching_sha2_password,
auth_plugin_sha256_password
So no need to create a user with mysql_native_passwordas authentication method in MySQL 8.0 as explained in the following posts:
https://lefred.be/content/mysql-8-0-17-and-drupal-8-7/
https://lefred.be/content/migrating-to-mysql-8-0-for-wordpress-episode-1/
https://lefred.be/content/drupal-and-mysql-8-0-11-are-we-there-yet/
https://lefred.be/content/joomla-and-mysql-8-0-12/
https://lefred.be/content/mysql-8-0-and-magento/
In summary, if you want to use a more secure method to connect to your MySQL 8.0 form your PHP application, make sure you upgrade to PHP 7.4
[Less]
|
Posted
over 4 years
ago
by
Vlad Mihalcea
Introduction In this article, we are going to see how a CROSS JOIN works, and we will also make use of this SQL join type to build a poker card game. Database table model For our poker card game application, we have created the ranks and suits
... [More]
database tables: The ranks table defines the ranking of cards, as well as the name and symbol used for each card rank: The suits table describes the four possible categories used by the French playing cards: Cartesian product In the set theory, the Cartesian product... Read More
The post A beginner’s guide to SQL CROSS JOIN appeared first on Vlad Mihalcea.
[Less]
|