© Charles Bell 2020
C. BellBeginning Sensor Networks with XBee, Raspberry Pi, and Arduinohttps://doi.org/10.1007/978-1-4842-5796-8_8

8. Turning Your Raspberry Pi into a Database Server

Charles Bell1 
(1)
Warsaw, VA, USA
 

Now that you know what sensor networks are and even how to build sensor nodes using an Arduino and a Raspberry Pi, it’s time to do something really cool with your Raspberry Pi. The last chapter discussed the various ways you can store data from your sensors. One of the most reliable and the most versatile is storing your sensor data in a database. This chapter explores using a Raspberry Pi as a database server.

While this has always been an option with the Raspberry Pi starting from the older version 2B boards, it is even more an option now that the Raspberry Pi 4B boards are out. They have more than enough processing power and (now, thankfully) more memory for heavier database work. Cool!

You begin with a short introduction to MySQL and then jump into getting MySQL up and running on a Raspberry Pi.1 If you have experience with installing and using MySQL, you may want to skip ahead to the “Building a Raspberry Pi MySQL Server” section.

What Is MySQL?

MySQL is the world’s most popular open source database system for many excellent reasons. First and foremost, it is open source, which means anyone can use it for a wide variety of tasks for free.2 Best of all, MySQL is included in many platform repositories, making it easy to get and install. If your platform doesn’t include MySQL in the repository (such as aptitude), you can download it from the MySQL website (http://dev.mysql.com).

Oracle Corporation owns MySQL. Oracle obtained MySQL through an acquisition of Sun Microsystems, which acquired MySQL from its original owners, MySQL AB. Despite fears to the contrary, Oracle has shown excellent stewardship of MySQL by continuing to invest in the evolution and development of new features as well as faithfully maintaining its open source heritage. Although Oracle also offers commercial licenses of MySQL—just as its prior owners did in the past—MySQL is still open source and available to everyone.

What is Open Source? Is it Really Free?

Open source software grew from a conscious resistance to the corporate-property mindset. While working for MIT, Richard Stallman, the father of the free software movement, resisted the trend of making software private (closed) and left MIT to start the GNU (GNU’s Not Unix) project and the Free Software Foundation (FSF).

Stallman’s goal was to reestablish a cooperating community of developers. He had the foresight, however, to realize that the system needed a copyright license that guaranteed certain freedoms. (Some have called Stallman’s take on copyright “copyleft,” because it guarantees freedom rather than restricts it.) To solve this, Stallman created the GNU Public License (GPL). The GPL, a clever work of legal permissions that permits the code to be copied and modified without restriction, states that derivative works (the modified copies) must be distributed under the same license as the original version without any additional restrictions.

There was one problem with the free software movement. The term free was intended to guarantee freedom to use, modify, and distribute; it was not intended to mean “no cost” or “free to a good home.” To counter this misconception, the Open Source Initiative (OSI) formed and later adopted and promoted the phrase open source to describe the freedoms guaranteed by the GPL license. For more information about open source software, visit www.opensource.org.

MySQL runs as a background process (or as a foreground process if you launch it from the command line3) on your system. Like most database systems, MySQL supports Structured Query Language (SQL). You can use SQL to create databases and objects (using data definition language [DDL]), write or change data (using data manipulation language [DML]), and execute various commands for managing the server.

To issue these commands, you must first connect to the database server. MySQL provides a client application that enables you to connect to and run commands on the server. The application is named MySQL Shell (mysqlsh) and has many improvements over the older client including a better interface as well as SQL, Python, and JavaScript modes. If you have used MySQL in the past, you may be familiar with the older MySQL client (mysql), which you can also use, but MySQL Shell is much easier to use. Please see the online reference manual for MySQL Shell (https://dev.mysql.com/doc/mysql-shell/8.0/en/) to learn more about how to use it, but those who’ve used the older client or who are following along in the tutorial will take to it quickly.

Tip

It is best to use the older mysql client when working on the Raspberry Pi because it requires one less compilation and installation step, but you can build and install the MySQL Shell on Raspberry Pi.

If you don’t already have the MySQL Shell installed, visit https://dev.mysql.com/downloads/shell/ and download it and then install it on your system. For macOS and Linux, follow the platform-specific installation procedures that you use for any other software. For Windows, you can download a separate MySQL Shell installation (.msi) or you can download the Windows Installer, which contains all MySQL applications, tools, and drivers. In that case, you simply select the components you want at the start of the installation.

Of course, you will also need to have access to a MySQL server running some place. The good news is you can install it on your PC! Just download the correct installer from the website (community edition) https://dev.mysql.com/downloads/mysql/, and install it on your system. It is very easy to install, but if you want a step-by-step instruction, see the online reference manual for help (https://dev.mysql.com/doc/refman/8.0/en/).

Once MySQL Shell is installed on your system, you can launch it as shown in Listing 8-1, which shows examples of each type of command discussed earlier in action. Note that these commands will work the same way in the older client.
$ mysqlsh --uri root@localhost:33060
Please provide the password for 'root@localhost:33060':
Save password for 'root@localhost:33060'? [Y]es/[N]o/Ne[v]er (default No): y
MySQL Shell 8.0.18
Copyright (c) 2016, 2019, 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 '?' for help; 'quit' to exit.
Creating a session to 'root@localhost:33060'
Fetching schema names for autocompletion... Press ^C to stop.
Your MySQL connection id is 8 (X protocol)
Server version: 8.0.18 MySQL Community Server – GPL
> CREATE DATABASE testme;
Query OK, 1 row affected (0.0012 sec)
> CREATE TABLE testme.table1 (sensor_node char(30), sensor_value int, sensor_event timestamp);
Query OK, 0 rows affected (0.0059 sec)
> INSERT INTO testme.table1 VALUES ('living room', 23, NULL);
Query OK, 1 row affected (0.0051 sec)
> SELECT ∗ FROM testme.table1;
+-------------+--------------+--------------+
| sensor_node | sensor_value | sensor_event |
+-------------+--------------+--------------+
| living room |           23 | NULL         |
+-------------+--------------+--------------+
1 row in set (0.0003 sec)
> SET @@global.server_id = 111;
Query OK, 0 rows affected (0.0002 sec)
> q
Bye!
Listing 8-1

Commands Using the MySQL Shell

If you’ve not used the MySQL Shell yet, take a look at how I started the shell. Notice I typed in the user credentials in a different format, which is quite intuitive and a bit easier than separate options. Notice also the shell permits me to save the password for faster subsequent logins. Nice!

In this example, you see DML in the form of the CREATE DATABASE and CREATE TABLE statements, DDL in the form of the INSERT and SELECT statements, and a simple administrative command to set a global server variable. Next, you see the creation of a database and a table to store the data, the addition of a row in the table, and finally retrieval of the data in the table.

A great many commands are available in MySQL. Fortunately, you need master only a few of the more common ones. The following are the commands you will use most often. The portions enclosed in <> indicate user-supplied components of the command, and [...] indicates that additional options are needed:

Tip

You must terminate each command with a semicolon (;) or G.

  • CREATE DATABASE <database_name>: Creates a database

  • USE <database>: Sets the default database

  • CREATE TABLE <table_name> [...]: Creates a table or structure to store data

  • INSERT INTO <table_name> [...]: Adds data to a table

  • UPDATE [...]: Changes one or more values for a specific row

  • DELETE FROM <table_name> [...]: Removes data from a table

  • SELECT [...]: Retrieves data (rows) from the table

Although this list is only a short introduction and nothing like a complete syntax guide, there is an excellent online reference manual that explains each and every command (and much more) in great detail. You should refer to the online reference manual whenever you have a question about anything in MySQL. You can find it at https://dev.mysql.com/doc/refman/8.0/en/.

If you are thinking that there is a lot more to MySQL than a few simple commands, you are absolutely correct. Despite its ease of use and fast startup time, MySQL is a full-fledged relational database management system (RDBMS). There is much more to it than you’ve seen here. For more information about MySQL, including all the advanced features, see the reference manual.

MYSQL—What Does it Mean?

The name MySQL is a combination of a proper name and an acronym. SQL is Structured Query Language. The My part isn’t the possessive form—it is a name. In this case, My is the name of one of the founder’s daughter. As for pronunciation, MySQL experts pronounce it “My-S-Q-L” and not “my sequel.” Indeed, the mark of a savvy MySQL user is in their correct pronunciation of the product.

Getting Started with MySQL

Now that you know what MySQL is and how it is used, you need to know a bit more about RDBMSs and MySQL in particular before you start building your first database server. This section discusses how MySQL stores data (and where it is stored), how it communicates with other systems, and some basic administration tasks required in order to manage your new MySQL server.

Note

I present this information as a tutorial or primer on MySQL. You install MySQL on the Raspberry Pi in a later section.

But first, let’s review what a relational database system is and why it matters.

What’s a Relational Database Management System?

An RDBMS is a data storage-and-retrieval service based on the relational model of data as proposed by E. F. Codd in 1970. These systems are the standard storage mechanism for structured data. A great deal of research is devoted to refining the essential model proposed by Codd, as discussed by C. J. Date in The Database Relational Model: A Retrospective Review and Analysis.4 This evolution of theory and practice is best documented in The Third Manifesto.5

The relational model is an intuitive concept of a storage repository (database) that can be easily queried by using a mechanism called a query language to retrieve, update, and insert data. The relational model has been implemented by many vendors because it has a sound systematic theory, a firm mathematical foundation, and a simple structure. The most commonly used query mechanism is SQL, which resembles natural language. Although SQL is not included in the relational model, it provides an integral part of the practical application of the relational model in RDBMSs.

The data are represented as related pieces of information (attributes or columns) about a certain event or entity. The set of values for the attributes is formed as a tuple (sometimes called a record or row). Tuples are stored in tables that have the same set of attributes. Tables can then be related to other tables through constraints on keys, attributes, and tuples.

Tables can have special mappings of columns called indexes that permit you to read the data in a specific order. Indexes are also very useful for fast retrieval of rows that match the value(s) of the indexed columns.

Now that we know a bit about the theory, let’s see how MySQL works to store our data.

How and Where MySQL Stores Data

The MySQL database system stores data via an interesting mechanism of programmatic isolation called a storage engine that is governed by the handler interface. The handler interface permits the use of interchangeable storage components in the MySQL server so that the parser, the optimizer, and all manner of components can interact in storing data on disk using a common mechanism. This is also referred to as a pluggable storage engine.6 While MySQL supports several storage engines, the default storage engine is called the InnoDB, which is a transactional storage engine.

What does this mean to you? It means you have the choice of different mechanisms for storing data, but for most application, you won’t need to change the storage engine. If you do want to change the storage engine, you can specify the storage engine in the CREATE TABLE statement shown in the following code sample. Notice the last line in the command: this is how a storage engine is specified. Leaving off this clause results in MySQL using the default storage engine (InnoDB).
CREATE TABLE `books` (
  `ISBN` varchar(15) DEFAULT NULL,
  `Title` varchar(125) DEFAULT NULL,
  `Authors` varchar(100) DEFAULT NULL,
  `Quantity` int(11) DEFAULT NULL,
  `Slot` int(11) DEFAULT NULL,
  `Thumbnail` varchar(100) DEFAULT NULL,
  `Description` text
) ENGINE=MyISAM;
Great! Now, what storage engines exist on MySQL? You can discover which storage engines are supported by issuing the following command. As you see, there are a lot to choose from. I cover a few that may be pertinent to planning sensor networks.
> SELECT engine, support, transactions FROM information_schema.engines;
+--------------------+---------+--------------+
| engine             | support | transactions |
+--------------------+---------+--------------+
| ARCHIVE            | YES     | NO           |
| BLACKHOLE          | YES     | NO           |
| MRG_MYISAM         | YES     | NO           |
| FEDERATED          | NO      | NULL         |
| MyISAM             | YES     | NO           |
| PERFORMANCE_SCHEMA | YES     | NO           |
| InnoDB             | DEFAULT | YES          |
| MEMORY             | YES     | NO           |
| CSV                | YES     | NO           |
+--------------------+---------+--------------+
9 rows in set (0.0005 sec)

Common Storage Engines

As of version 5.6, MySQL uses the InnoDB storage engine by default. Previous versions used MyISAM as the default. InnoDB is a fully transactional, ACID7 storage engine. A transaction is a batch of statements that must all succeed before any changes are written to disk. The classic example is a bank transfer. If you consider a system that requires deducting an amount from one account and then crediting that amount to another account to complete the act of moving funds, you would not want the first to succeed and the second to fail or vice versa!

Wrapping the statements in a transaction ensures that no data is written to disk until and unless all statements are completed without errors. Transactions in this case are designated with a BEGIN statement and concluded with either a COMMIT to save the changes or a ROLLBACK to undo the changes. InnoDB stores its data in a single file (with some additional files for managing indexes and transactions).

The MyISAM storage engine is optimized for reads. MyISAM has been the default for some time and was one of the first storage engines available. In fact, a large portion of the server is dedicated to supporting MyISAM. It differs from InnoDB in that it does not support transactions and stores its data in an indexed sequential access method format. This means it supports fast indexing. You would choose MyISAM over InnoDB if you did not need transactions and you wanted to be able to move or back up individual tables.

Another storage engine that you may want to consider, especially for sensor networks, is Archive. This engine does not support deletes (but you can drop entire tables) and is optimized for minimal storage on disk. Clearly, if you are running MySQL on a small system like a Raspberry Pi, minimizing disk usage may be a goal. The inability to delete data may limit more advanced applications, but most sensor networks merely store data and rarely delete it. In this case, you can consider using the Archive storage engine.

There is also the CSV storage engine (where CSV stands for comma-separated values). This storage engine creates text files to store the data in plain text that can be read by other applications such as a spreadsheet application. If you use your sensor data for statistical analysis, the CSV storage engine may make the process of ingesting the data easier.

Where Is My Data Stored?

So where is all this data? If you query the MySQL server and issue the command SHOW VARIABLES LIKE 'datadir';, you see the path to the location on disk that all storage engines use to store data. In the case of InnoDB, this is a single file on disk located in the data directory. InnoDB also creates a few administrative files, but the data is stored in the single file. For most other storage engines except NDB and MEMORY, the data for the tables is stored in a folder with the name of the database under the data directory. Listing 8-2 shows an example. The database folders are shown in bold. Some files omitted for brevity.

Tip

When you use sudo for the first time, you are required to enter the password for the root user.

> SHOW VARIABLES LIKE 'datadir';
+---------------+------------------------+
| Variable_name | Value                  |
+---------------+------------------------+
| datadir       | /usr/local/mysql/data/ |
+---------------+------------------------+
1 row in set (0.0037 sec)
> q
Bye!
$ sudo ls -lsa /usr/local/mysql/data
total 336248
    0 drwxr-x---   12 _mysql  _mysql       384 Nov  4 16:28 #innodb_temp
    0 drwxr-x---   30 _mysql  _mysql       960 Nov  4 17:05 .
    0 drwxr-xr-x   17 root    wheel        544 Nov  4 16:28 ..
    8 -rw-r-----    1 _mysql  _mysql        56 Nov  4 16:28 auto.cnf
    8 -rw-r-----    1 _mysql  _mysql       665 Nov  4 16:28 binlog.000001
  264 -rw-r-----    1 _mysql  _mysql     84608 Nov  4 17:05 binlog.000002
    8 -rw-r-----    1 _mysql  _mysql        32 Nov  4 16:28 binlog.index
    0 drwxr-x---    8 _mysql  _mysql       256 Nov  4 17:05 bvm
    8 -rw-r-----    1 _mysql  _mysql      3513 Nov  4 16:28 ib_buffer_pool
98304 -rw-r-----    1 _mysql  _mysql  50331648 Nov  4 17:05 ib_logfile0
98304 -rw-r-----    1 _mysql  _mysql  50331648 Nov  4 16:28 ib_logfile1
24576 -rw-r-----    1 _mysql  _mysql  12582912 Nov  4 17:05 ibdata1
24576 -rw-r-----    1 _mysql  _mysql  12582912 Nov  4 16:28 ibtmp1
    0 drwxr-x---    8 _mysql  _mysql       256 Nov  4 16:28 mysql
49152 -rw-r-----    1 _mysql  _mysql  25165824 Nov  4 17:05 mysql.ibd
    8 -rw-r-----    1 _mysql  _mysql       739 Nov  4 16:28 mysqld.local.err
    8 -rw-r-----    1 _mysql  _mysql         5 Nov  4 16:28 mysqld.local.pid
    0 drwxr-x---  105 _mysql  _mysql      3360 Nov  4 16:28 performance_schema
    0 drwxr-x---    3 _mysql  _mysql        96 Nov  4 16:28 sys
    0 drwxr-x---    3 _mysql  _mysql        96 Nov  4 16:36 testme
20480 -rw-r-----    1 _mysql  _mysql  10485760 Nov  4 17:05 undo_001
20480 -rw-r-----    1 _mysql  _mysql  10485760 Nov  4 17:05 undo_002
$ sudo ls -lsa /usr/local/mysql/data/bvm
total 64
 0 drwxr-x---   8 _mysql  _mysql   256 Nov  4 17:05 .
 0 drwxr-x---  30 _mysql  _mysql   960 Nov  4 17:05 ..
16 -rw-r-----   1 _mysql  _mysql  5324 Nov  4 17:05 books.MYD
 8 -rw-r-----   1 _mysql  _mysql  1024 Nov  4 17:05 books.MYI
16 -rw-r-----   1 _mysql  _mysql  8012 Nov  4 17:05 books_354.sdi
 8 -rw-r-----   1 _mysql  _mysql   281 Nov  4 17:05 settings.MYD
 8 -rw-r-----   1 _mysql  _mysql  1024 Nov  4 17:05 settings.MYI
 8 -rw-r-----   1 _mysql  _mysql  2250 Nov  4 17:05 settings_355.sdi
Listing 8-2

Finding Where Your Data Is Located

This example first queries the database server for the location of the data directory (it is in a protected folder on this machine). If you issue a listing command, you can see the InnoDB files identified by the ib and ibd prefixes. You also see a number of directories, all of which are the databases on this server. Below that is a listing of one of the database folders. Notice the files with the extension .MY?: these are MyISAM files (data and index).

For more information about storage engines and the choices and features of each, please see the online MySQL reference manual section “Storage Engines” (https://dev.mysql.com/doc/refman/8.0/en/storage-engines.html).

The MySQL Configuration File

The MySQL server can be configured using a configuration file, similar to the way you configure the Raspberry Pi. On the Raspberry Pi, the MySQL configuration file is located in the /etc/mysql folder and is named my.cnf. This file contains several sections, one of which is labeled [mysqld]. The items in this list are key-value pairs: the name on the left of the equal sign is the option and its value on the right. The following is a typical configuration file (with many lines suppressed for brevity):
[mysqld]
port = 3306
basedir = /usr/local/mysql
datadir = /usr/local/mysql/data
server_id = 5
general_log

As you can see, this is a simple way to configure a system. This example sets the TCP port, base directory (the root of the MySQL installation including the data as well as binary and auxiliary files), data directory, and server ID (used for replication, as discussed shortly) and turns on the general log (when the Boolean switch is included, it turns on the log). There are many such variables you can set for MySQL. See the online MySQL reference manual for details concerning using the configuration file. You will change this file when you set up MySQL on the Raspberry Pi.

How to Start, Stop, and Restart MySQL

While working with your databases and configuring MySQL on your Raspberry Pi, you may need to control the startup and shutdown of the MySQL server. The default mode for installing MySQL is to automatically start on boot and stop on shutdown, but you may want to change that, or you may need to stop and start the server after changing a parameter. In addition, when you change the configuration file, you need to restart the server to see the effect of your changes.

You can start, stop, and restart the MySQL server with the script located in /etc/init.d/mysql. Here is a list of its options:
$ /etc/init.d/mysql --help
Usage: mysql.server  {start|stop|restart|reload|force-reload|status}  [ MySQL server options ]
The script can start, stop, and restart the server as well as get its status. You can also pass configuration (such as startup) options to the server. This can be useful for turning on a feature for temporary use as an alternative to modifying the configuration file. For example, if you want to turn on the general log for a period of time, you can use these commands:
/etc/init.d/mysql restart --general-log
/etc/init.d/mysql restart

The first restart restarts the server with the general logon, and the second restarts the server without the log enabled (assuming it isn’t in the configuration file). It’s probably a good idea to make sure no one is using the server when you restart it.

However, the better way to start and stop MySQL on the latest release of Raspbian is with the systemctl command as follows. You can use either method.
  • Start: sudo systemctl start mysqld

  • Stop: sudo systemctl stop mysqld

  • Restart: sudo systemctl restart mysqld

  • Status: sudo systemctl status mysqld

Shutting Down Correctly

You may be tempted to just power down your Raspberry Pi database server like you do your Arduino sensor nodes, but you should avoid that temptation. The Raspberry Pi is a real computer with active file systems that require a synchronized shutdown. You should always execute a controlled shutdown before powering down.

To shut down the Raspberry Pi, recall that you issue the sudo shutdown –h now command. To reboot, you can use the sudo shutdown –r now command.

Creating Users and Granting Access

You need to know about two additional administrative operations before working with MySQL: creating user accounts and granting access to databases. MySQL can perform both of these with the CREATE USER and one or more GRANT statements. For example, the following shows the creation of a user named sensor1 and grants the user access to the database room_temp:
CREATE USER 'sensor1'@'%' IDENTIFIED BY 'secret';
GRANT SELECT, INSERT, UPDATE ON room_temp.∗ TO 'sensor1'@'%';

The first command creates the user named sensor1, but the name also has an @ followed by another string. This second string is the hostname of the machine with which the user is associated. That is, each user in MySQL has both a user and a hostname, in the form user@host, to uniquely identify them. That means the user and host [email protected] and the user and host [email protected] are not the same. However, the % symbol can be used as a wildcard to associate the user with any host. The IDENTIFIED BY clause sets the password for the user.

A Note About Security
It is always a good idea to create a user for your application that does not have full access to the MySQL system. This is so you can minimize any accidental changes and also to prevent exploitation. For sensor networks, it is recommended that you create a user with access only to those databases where you store (or retrieve) data. You can change MySQL user passwords with the following command:
ALTER USER sensor1@"%" IDENTIFIED BY 'super_secret';

Also be careful about using the wildcard % for the host. Although it makes it easier to create a single user and let the user access the database server from any host, it also makes it much easier for someone bent on malice to access your server (once they discover the password).

Another consideration is connectivity. As with the Raspberry Pi, if you connect a database to your network and the network is in turn connected to the Internet, it may be possible for other users on your network or the Internet to gain access to the database. Don’t make it easy for them—change your root user password, and create users for your applications.

The second command allows access to databases. There are many privileges that you can give a user. The example shows the most likely set that you would want to give a user of a sensor network database: read (SELECT),8 add data (INSERT), and change data (UPDATE). See the online reference manual for more information about security and account access privileges.

The command also specifies a database and objects to which to grant the privilege. Thus, it is possible to give a user read (SELECT) privileges to some tables and write (INSERT, UPDATE) privileges to other tables. This example gives the user access to all objects (tables, views, and so on) in the room_temp database.

Now that you’ve had a short (and perhaps a bit terse) introduction to MySQL, let’s get started on your MySQL Raspberry Pi database server.

Building a Raspberry Pi MySQL Server

It is time to get your hands dirty and work some magic on your unsuspecting Raspberry Pi! Let’s begin by adding a USB drive to it. A flash drive with fast read/write speeds can work especially well considering it does not need as much power as a traditional external hard drive. Depending on the size of your data, you may want to seriously consider doing this.

If your data will be small (never more than a few megabytes), you may be fine using MySQL from your boot image SD card. However, if you want to ensure that you do not run out of space and keep your data separate from your boot image, you should mount a USB drive that automatically connects on boot. This section explains how to do this in detail.

If you plan to use an external hard drive, be sure you use a good-quality powered USB hub to host your external drive. This is especially important if you are using a traditional spindle drive, because it consumes a lot more power. Connecting your external drive directly to the Raspberry Pi may rob it of power and cause untold frustration. Symptoms include random reboot (always a pleasant surprise), failed commands, data loss, and so on. Always be sure you have plenty of power for your peripherals as well as your Raspberry Pi.

The choice of what disk to use is up to you. You can use a USB flash drive, which should work fine if it has plenty of space and is of sufficient speed (most newer models are fast). You can also use a solid-state drive (SSD) if you have an extra one or want to keep power usage and heat to a minimum. On the other hand, you may have an extra hard drive lying around that can be pressed into service. This section’s example uses a surplus 250GB laptop hard drive mounted in a typical USB hard drive enclosure.

Tip

Using an external hard drive—either an SSD or traditional spindle drive—is much faster than accessing data on a flash drive. It is also typically cheaper per unit (gigabyte) or, as I mentioned, can be easily obtained from surplus.

Partitioning and Formatting the Drive

Before you can use a new or an existing drive with a file system incompatible with the Raspberry Pi, you must partition and format the drive. Because the surplus drive in this example had an old Windows partition on it, I had to follow these steps. Your Raspberry OS may be able to read the format of your old drive, but you should use the ext4 file system for optimal performance. This section shows you how to partition and format your drive.

Begin by connecting the drive to the Raspberry Pi. Then determine what drives are attached by using the fdisk command as shown:
$ sudo fdisk -l
...
Disk /dev/sda: 59.2 GiB, 63518539776 bytes, 124059648 sectors
Disk model: Cruzer Fit
Units: sectors of 1 ∗ 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xde217a25
Device     Boot   Start      End  Sectors  Size  Id  Type
/dev/sda1  ∗         64  6691199  6691136  3.2G  17  Hidden HPFS/NTFS
/dev/sda2       6691200  6692671     1472  736K   1  FAT12

What you see here are all the devices attached to the Raspberry Pi. If you are new to Linux or partitioning drives, this may look like a lot of nonsense. I’ve highlighted the interesting rows in bold. Notice that the output identifies a 64GB drive located on a device designated as /dev/sda. All the interesting data about the drive is shown as well.

As I mentioned, there is already a partition on this drive, indicated by the row with the name of the device plus the number of the partition. Thus, /dev/sda1 is the one and only partition on this drive. Let’s delete that partition and create a new one. You execute both operations using the fdisk application as shown in Listing 8-3.

Caution

If you have a partition on your drive that has data you want to keep, abort now and copy the data to another drive first. The following steps erase all data on the drive!

$ sudo fdisk /dev/sda
Welcome to fdisk (util-linux 2.33.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
Command (m for help): p
Disk /dev/sda: 59.2 GiB, 63518539776 bytes, 124059648 sectors
Disk model: Cruzer Fit
Units: sectors of 1 ∗ 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xde217a25
Device     Boot   Start     End Sectors  Size Id Type
/dev/sda1  ∗         64 6691199 6691136  3.2G 17 Hidden HPFS/NTFS
/dev/sda2       6691200 6692671    1472  736K  1 FAT12
Command (m for help): d
Partition number (1,2, default 2):
Partition 2 has been deleted .
Command (m for help): d
Selected partition 1
Partition 1 has been deleted.
Command (m for help): n
Partition type
   p   primary (0 primary, 0 extended, 4 free)
   e   extended (container for logical partitions)
Select (default p): p
Partition number (1-4, default 1):
First sector (2048-124059647, default 2048):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-124059647, default 124059647):
Created a new partition 1 of type 'Linux' and of size 59.2 GiB.
Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.
Listing 8-3

Partitioning the USB Drive

The first command, d, deletes a partition. In this case, there was only one partition, so you select it by entering 1. You then create a new partition using the command n and accept the defaults to use all the free space. To check your work, you can use the p command to print the device partition table and metadata. It shows (and confirms) the new partition.

If you are worried that you may have made a mistake, do not panic! The great thing about fdisk is that it doesn’t write or change the disk until you tell it to with the w or write command. In the example, you issue the w command to write the partition table. To see a full list of the commands available, you can use the h command or run man fdisk.

Tip

For all Linux commands, you can view the manual file by using the command man <application>.

The next step is to format the drive with the ext4 file system. This is easy and requires only one command: mkfs (make file system). You pass it the device name. If you recall, this is /dev/sda1. Even though you created a new partition, it is still the first partition because there is only one on the drive. If you are attempting to use a different partition, be sure to use the correct number! The command may take a few minutes to run, depending on the size of your drive. Listing 8-4 shows the command in action.
$ sudo mkfs.ext4 /dev/sda
mke2fs 1.44.5 (15-Dec-2018)
/dev/sda contains an iso9660 file system labelled 'Backup'
Proceed anyway? (y,N) y
Creating filesystem with 15507456 4k blocks and 3883008 inodes
Filesystem UUID: d370c755-18be-4c7f-bf66-4dd666ade676
Superblock backups stored on blocks:
    32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 4096000, 7962624, 11239424
Allocating group tables: done
Writing inode tables: done
Creating journal (65536 blocks): done
Writing superblocks and filesystem accounting information: done
Listing 8-4

Formatting the Drive

Now you have a new partition, and it has been properly formatted. The next step is associating the drive with a mount point on the boot image and then connecting that drive on boot, so you don’t have to do anything to use the drive each time you start your Raspberry Pi.

Setting Up Automatic Drive Mounting

External drives in Linux are connected (mounted) with mount and disconnected (unmounted) with umount . Unlike with some operating systems, it is generally a bad idea to unplug your USB drive without unmounting it first. Likewise, you must mount the drive before you can use it. This section shows the steps needed to mount the drive and to make the drive mount automatically on each boot.

I begin with a discussion of the preliminary steps to get the drive mounted and ready for automatic mounting. These include creating a folder under the /media folder to mount the drive (called a mount point), changing permissions to the folder to allow access, and executing some optional steps to tune the drive:
$ sudo mkdir /media/mysql
$ sudo chmod 755 /media/mysql
$ sudo tune2fs -m 0 /dev/sda
tune2fs 1.44.5 (15-Dec-2018)
Setting reserved blocks percentage to 0% (0 blocks)
$ sudo tune2fs -L MySQL /dev/sda
tune2fs 1.44.5 (15-Dec-2018)
$ sudo mount /dev/sda /media/mysql
$ sudo ls -lsa /media/mysql
total 24
 4 drwxr-xr-x 3 root root  4096 Nov 27 13:44 .
 4 drwxr-xr-x 4 root root  4096 Nov 27 13:55 ..
16 drwx------ 2 root root 16384 Nov 27 13:44 lost+found

These commands are easy to discern and are basic file and folder commands. However, the tuning steps using tune2fs (tune file system) are used to first reset the number of blocks used for privileged access (which saves a bit of space) and then label the drive as MYSQL. Again, these are optional, and you may skip them if you like.

Tip

You can unmount the drive with sudo umount /dev/sda1.

At this point, the drive is accessible and ready to be used. You can change to the /media/HDD folder and create files or do whatever you’d like. Now let’s complete the task of setting up the drive for automatic mounting.

The best way to do this is to refer to the drive by its universally unique identifier (UUID). This is assigned to this drive and only this drive. You can tell the operating system to mount the drive with a specific UUID to a specific mount point (/media/HDD).

Remember the /dev/sda device name from earlier? If you plugged your drive into another hub port—or, better still, if there are other drives connected to your device and you unmount and then mount them—the device name may not be the same the next time you boot! The UUID helps you determine which drive is your data drive, frees you from having to keep the drive plugged in to a specific port, and allows you to use other drives without fear of breaking your MySQL installation if the drive is given a different device name.

To get the UUID, use the blkid (block ID) application:
$ sudo blkid
...
/dev/sda: LABEL="MySQL" UUID="d370c755-18be-4c7f-bf66-4dd666ade676" TYPE="ext4"
...

Notice the line in bold. Wow! That’s a big string. A UUID is a 128-byte (character) string. Copy it for the next step.

To set up automatic drive mapping, you use a feature called static information about the file system (fstab). This consists of a file located in the /etc folder on your system. You can edit the file however you like. If you are from the old school of Linux or Unix, you may choose to use vi.9 The resulting file is as follows:
$ sudo nano /etc/fstab
proc    /proc   proc    defaults  0       0
/dev/mmcblk0p1  /boot   vfat    defaults  0       0
/dev/mmcblk0p2  /       ext4    defaults,noatime  0       0
UUID= d370c755-18be-4c7f-bf66-4dd666ade676 /media/mysql   ext4   defaults,noatime   0   0
The line you add is shown in bold. Here you simply add the UUID, mount point, file system, and options. That’s it! You can reboot your Raspberry Pi using the following command and watch the screen as the messages scroll. Eventually, you see that the drive is mounted. If there is ever an error, you can see it in the boot-up sequence:
$ sudo shutdown –r now

Now you are ready to build a MySQL database server! The following section details the steps needed to do this using your Raspberry Pi.

Project: Installing MySQL Server on a Raspberry Pi

Turning a Raspberry Pi into a MySQL database server is easy. Well, almost. The latest version of MySQL (8.0) is not available for the Raspberry Pi.10 However, since MySQL is open source, we can build (compile and link) MySQL from source on our Raspberry Pi. How cool is that? This section shows you how to acquire the source code for MySQL, build, and install it. We then learn how to move its default data directory from your boot image to the new external drive you connected in the previous section.

What about Other MYSQL Variants?

Savvy readers may already be aware of variants of MySQL that are available from other vendors. While most claim to be 100% compatible with Oracle’s MySQL (owners of the source code), there are some differences that can make development more difficult. For example, the MySQL database connector for Arduino (called Connector/Arduino) is known to have issues with some versions of some variants. Thus, it is this author’s opinion you should always use Oracle’s release of MySQL rather than variants.

In this section, we will use Raspberry Pi computers rather than more expensive mainstream server hardware. If you would like to follow along and use more traditional server hardware, you can do, but remember that some of the commands used on the Raspberry Pi are very similar to those you would use on typical Linux-based platforms. You may need to substitute platform-specific versions to use the following on your PC.

Recall, since MySQL is open source, we can download the source code, compile, and install it ourselves. In fact, we will do just that in this walk-through. The following lists the steps necessary to prepare a Raspberry Pi computer for use with MySQL:
  • Build MySQL.

  • Install MySQL manually.

  • Configure MySQL.

This list is like the process you would use to set up MySQL on commodity hardware, but the build and configuration steps are required to make MySQL work on Raspbian (because there are no installation packages). It is important to note that these extra steps are not unique to Raspbian.

In fact, building, installing, and configuring MySQL from source is a viable alternative to using installation packages. You can find instructions on building MySQL for various platforms in the section entitled “Installing MySQL from Source” in the online reference manual (https://dev.mysql.com/doc/refman/8.0/en/source-installation.html).

The process is straightforward involving a few minor system configuration items to prepare our system and two commands: cmake and make. This section will walk you through all those steps with ample examples and every step documented.

The task of building MySQL from source code may seem daunting to those who have never programmed or for those who haven’t written a program in a while, but do not despair. The hardest part of compiling MySQL on the Raspberry Pi is waiting for the process to complete. That is, it may take an hour or so to compile everything. But that is a small price to pay for being able to use Raspberry Pi computers to experiment with MySQL!

Let’s dive into compiling MySQL on Raspbian starting with the prerequisites.

Prerequisites

There are several things you need to install to prepare your Raspberry Pi to compile MySQL including hardware and software prerequisites MySQL.

The hardware requirement is that the latest version of MySQL (8.0.18 at the time of this writing) requires using a Raspberry Pi 4B with 2GB or 4GB (4GB is faster) board. Thus, you should consider whether you want to build MySQL on the same Raspberry Pi 4B where you want to install it. Why is this important? It is important because you may want to run MySQL on older Raspberry Pi boards. More specifically, while it is best to compile MySQL on the 4B, you can install and run it on the 3B+ without any issues. We will see how to do this later. The reason we need to use the 4B is largely due to memory. MySQL simply requires more than the 1GB of RAM found on the 3B boards.11

Beyond requiring a Raspberry Pi 4B, the software prerequisites include the following software:
  • You need to install Curses 5 (libncurses5-dev).

  • You need to install Bison.

  • You need to install OpenSSL (libssl-dev).

  • You need to install CMake.

To install all these libraries at one time, use the following command in a terminal window. This will download the necessary files and install them. Notice we must use elevated privileges to install the library.
$ sudo apt-get install libncurses5-dev bison libssl-dev cmake
The only other prerequisite is we must download the MySQL Server source code. Go to https://dev.mysql.com/downloads/mysql/, select Source Code in the Select Operating System drop-down box, Generic Linux from the Select OS Version drop-down box, and then click the Generic Linux (Architecture Independent), Compressed TAR Archive Includes Boost Headers download link at the bottom of the list as shown in Figure 8-1. This file contains another library that we need (boost) as well as the server source code. It is the easiest of the downloads to start from to build. Once you’ve downloaded the file, copy it to your Raspberry Pi.
../images/313992_2_En_8_Chapter/313992_2_En_8_Fig1_HTML.jpg
Figure 8-1

Download MySQL server source code

OK, now we’re ready to build the MySQL server.

Building MySQL Server

There are just three steps to building MySQL on Raspberry Pi. We first run the preprocessor called CMake, then build the code with make, and finally build an installation package with the make package command. We can use this package to install MySQL on another Raspberry Pi. Let’s see the details of each of these steps beginning with CMake.

CMake (cmake.org) is another open source product used to build, test, and package software. Recall, we installed CMake in the previous section. There are many variations of options you can use to build software and many that apply to MySQL. In fact, you can spend a lot of time customizing the CMake command options to build for almost any platform. Since we downloaded the MySQL source code for generic Linux with the Boost libraries, we’ve got everything we need.

Thus, the command options we need to use with CMake are minimal and include the following. Each of these will be explained in a bit more detail here:
  • You should set -DWITH_UNIT_TESTS=OFF to save compile time (not needed).

  • You should set the PREFIX to set the installation path to make it easy to install.

  • We need to turn off the “gold” linker.

  • We must build with the release code (debug requires too much memory for the Raspberry Pi).

  • We must add additional compiling and build flags to ensure the code builds properly on ARM32.

Running CMake (Preparing to Compile)
The first thing we’re going to do is extract the TAR file that we downloaded. You can do so with the following commands. This will create a folder named mysql-8.0.18. It is recommended that you unpack this file in a folder in the root user’s home folder, for example, /home/pi/source. The unpack process will take a few minutes as it contains a lot of code.
$ cd /home/pi
$ mkdir source
$ cd source
$ cp ~/Downloads/mysql-boost-8.0.18.tar.gz .
$ tar -xvf mysql-boost-8.0.18.tar.gz
Next, we will make a directory to store all the compiled code using the following commands. This helps prevent accidents when compiling and preserves the source code.
$ cd mysql-8.0.18
$ mkdir build
$ cd build
Now we can run the CMake command. Listing 8-5 shows the complete command you need to use from within the build folder. Notice the command has many options specified including (in order of appearance) using Unix makefiles, setting the build to release code (rather than debug), ignoring AIO checking, setting the boost folder (included in the TAR file we downloaded), turning off the unit tests, and setting some arcane settings for compiling on ARM32.
$ cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=release -DBUILD_CONFIG=mysql_release -DDEBUG_EXTNAME=OFF -DIGNORE_AIO_CHECK=1 -DWITH_UNIT_TESTS=OFF -DCMAKE_C_LINK_FLAGS="-Wl,--no-keep-memory,-latomic" -DCMAKE_CXX_LINK_FLAGS="-Wl,--no-keep-memory,-latomic" -DCMAKE_C_FLAGS_RELEASE="-fPIC" -DCMAKE_CXX_FLAGS_RELEASE="-fPIC" -DCMAKE_INSTALL_PREFIX="/usr/local/mysql" -DUSE_LD_GOLD=OFF -DWITH_BOOST="../boost" ..
-- Running cmake version 3.13.4
-- Found Git: /usr/bin/git (found version "2.20.1")
-- MySQL 8.0.18
-- Source directory /media/pi/source/mysql-8.0.18
-- Binary directory /media/pi/source/mysql-8.0.18/build
-- CMAKE_GENERATOR: Unix Makefiles
...
-- CMAKE_C_FLAGS: -fno-omit-frame-pointer  -Wall -Wextra -Wformat-security -Wvla -Wundef -Wwrite-strings -Wjump-misses-init
-- CMAKE_CXX_FLAGS: -std=c++14 -fno-omit-frame-pointer  -Wall -Wextra -Wformat-security -Wvla -Wundef -Woverloaded-virtual -Wcast-qual -Wimplicit-fallthrough=2 -Wlogical-op
-- CMAKE_CXX_FLAGS_DEBUG: -DSAFE_MUTEX -DENABLED_DEBUG_SYNC -g
-- CMAKE_CXX_FLAGS_RELWITHDEBINFO: -DDBUG_OFF -ffunction-sections -fdata-sections -O2 -g -DNDEBUG
-- CMAKE_CXX_FLAGS_RELEASE: -DDBUG_OFF -ffunction-sections -fdata-sections -fPIC
-- CMAKE_CXX_FLAGS_MINSIZEREL: -DDBUG_OFF -ffunction-sections -fdata-sections -Os -DNDEBUG
-- CMAKE_C_LINK_FLAGS: -Wl,--no-keep-memory,-latomic
-- CMAKE_CXX_LINK_FLAGS: -Wl,--no-keep-memory,-latomic
-- CMAKE_EXE_LINKER_FLAGS
-- CMAKE_MODULE_LINKER_FLAGS
-- CMAKE_SHARED_LINKER_FLAGS
-- Configuring done
-- Generating done
Listing 8-5

Running the CMake Command (ARM32)

Don’t worry if this command looks strange, and it is not necessary to understand all the special settings we’ve used for the compile and link phases. However, if you do want to learn more about these options, you can see the documentation on the GNU compiler (http://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html) and linker (https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html) options.

The command could take a few minutes to run. Be sure there are no errors and that the last lines indicate the build files have been written to the build folder. Pay special attention to the LINK_FLAGS messages at the end. The options in the CMake command do not include spaces. If you accidentally added spaces, the comma-separated list would show them in the CMake output. Be sure there are no spaces. If there are spaces, you may get an error stating --icf=safe (or other) options are invalid. If that happens, run the command again without the spaces.

If you’ve gotten this far without errors, you can almost relax. The next step, compiling the code is easy, but it can take a while to run on a Raspberry Pi 4B (at least 1–2 hours).

Running Make (Compiling)

The next step is to compile the code. This is done simply with the make command. This command allows us to specify how many parallel threads we want to use. For the Raspberry Pi 4B and a total of four CPU cores, it is safe to use three cores for compiling. If you have a watcher for CPU usage running, you will see those three and possibly at times all four cores running at 100%. If your Raspberry Pi is mounted in a case, make sure you have adequate ventilation or a fan blowing over the board.

Listing 8-6 shows the compilation step of the MySQL server code using the command make -j3. The listing is an excerpt of the messages you will likely see (there will be thousands of lines), but the important ones to note are the last several. These ensure the code has compiled without errors.

Tip

You may see minor warnings flow past when the code is compiling, which you can ignore. However, you should not see any compilation errors. If you do, go back and check your CMake command and rerun it if necessary. If all else fails, delete the build directory and start over.

$ make -j3
[  0%] Built target INFO_SRC
[  0%] Built target INFO_BIN
[  0%] Building C object extra/zlib/CMakeFiles/zlib_objlib.dir/gzread.o
[  0%] Building C object extra/zstd/CMakeFiles/zstd_objlib.dir/lib/common/threading.c.o
[  0%] Building C object extra/zstd/CMakeFiles/zstd_objlib.dir/lib/common/xxhash.c.o
[  0%] Building C object extra/zlib/CMakeFiles/zlib_objlib.dir/gzwrite.o
...
[100%] Building CXX object storage/innobase/CMakeFiles/innobase.dir/os/os0thread.cc.o
[100%] Building CXX object storage/innobase/CMakeFiles/innobase.dir/page/zipdecompress.cc.o
[100%] Building CXX object storage/innobase/CMakeFiles/innobase.dir/rem/rec.cc.o
[100%] Building CXX object storage/innobase/CMakeFiles/innobase.dir/ut/crc32.cc.o
[100%] Building CXX object storage/innobase/CMakeFiles/innobase.dir/ut/ut.cc.o
[100%] Linking CXX static library libinnobase.a
[100%] Built target innobase
Scanning dependencies of target mysqld
[100%] Building CXX object sql/CMakeFiles/mysqld.dir/main.cc.o
[100%] Linking CXX executable ../runtime_output_directory/mysqld
[100%] Built target mysqld
Listing 8-6

Compiling MySQL Server

Once the compilation is complete, the next step is to build a package (TAR file) we can use to install MySQL on our server.

Making the Package
The last thing we need to do is build the installation package. In this case, we will build a compress TAR file that we will be able to copy to our initial server and install. We do this with the make package command as shown in Listing 8-7.
$ make package
[  0%] Built target abi_check
[  0%] Built target INFO_SRC
[  0%] Built target INFO_BIN
[  1%] Built target zlib_objlib
[  1%] Built target zlib
[  2%] Built target zstd_objlib
[  2%] Built target zstd
[  3%] Built target edit
[  4%] Built target event_core
...
[100%] Built target routing
[100%] Built target rest_routing
[100%] Built target mysqlrouter
[100%] Built target mysqlrouter_keyring
Run CPack packaging tool...
CPack: Create package using TGZ
CPack: Install projects
CPack: - Run preinstall target for: MySQL
CPack: - Install project: MySQL
CPack: Create package
CPack: - package: /home/pi/source/mysql-8.0.18/build/mysql-8.0.18-linux-armv7l.tar.gz generated.
Listing 8-7

Building the TAR Package

That’s it! We’ve built MySQL on the Raspberry Pi! That wasn’t so bad, was it? Now, let’s see how to install and test MySQL on our server.

Installing MySQL Server

If we built MySQL on a different Raspberry Pi, we need to copy the TAR file to a removable drive to copy the file to the target Raspberry Pi.

Once the server is booted, log in and change to the /usr/local directory and create a new folder named mysql. Then, change to the new folder and copy the TAR file to that folder. Finally, unpack the file using the following commands. There are a lot of files, so it could take a few minutes to unpack.
$ cd /usr/local/
$ mkdir mysql
$ cd mysql
$ sudo cp ~/source/mysql-8.0.11/build/mysql-8.0.18-linux-armv7l.tar.gz .
$ sudo tar -xvf mysql-8.0.11-linux-armv7l.tar.gz --strip-components=1

Notice the last command uses an option to strip one component (the first folder—mysql-8.0.18-linux-armv71) from the extracted file directories. This ensures the MySQL files get copied to /usr/local/mysql.

However, there is one more command we need to run. Since we are space conscience, we do not need the MySQL test files, so we can delete them with the following command. Once we’re done with the TAR file, we can delete that too as shown here:
$ sudo rm -rf mysql-test
$ sudo rm mysql-8.0.18-linux-armv71.tar.gz

Installing from the TAR file requires more steps than installing from a typical platform-specific package. This is because installation packages typically take care of several required configuration steps, all of which are detailed in the online reference manual section entitled “Installing MySQL on Unix/Linux Using Generic Binaries” (https://dev.mysql.com/doc/refman/8.0/en/binary-installation.html).

Configuring MySQL Server

Now that we have the files copied, we can finish the setup. The process is not tedious but does involve several commands run from a terminal, so some patience is needed to ensure all the commands are entered correctly.

We begin by creating a new group named mysql, then add a user named mysql, then create a folder for MySQL to use, and grant access to the folder to the mysql user. The following code shows the commands needed. Run these from a terminal (there will be no output from any of the commands).
$ sudo groupadd mysql
$ sudo useradd -r -g mysql -s /bin/false mysql
$ cd /usr/local/mysql
$ sudo mkdir mysql-files
$ sudo chown mysql:mysql mysql-files
$ sudo chmod 750 mysql-files
We can initialize the data directory easily with the --initialize option as shown in the following code. Notice we run the command with elevated privileges and specify the user to use (mysql). The following code shows an example of the output with the successful messages highlighted. If you see errors, consult the online reference manual to resolve the errors. Notice the output contains the initial root user password. You will need that for the next step. Note that this step can take a few moments to run.
$ sudo ./bin/mysqld --initialize --user=mysql
2019-11-17T02:02:41.118355Z 0 [System] [MY-013169] [Server] /usr/local/mysql/bin/mysqld (mysqld 8.0.18) initializing of server in progress as process 7704
2019-11-17T02:05:04.757386Z 5 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: VPw&eFjU-0z#
Next, we create a configuration file using our favorite editor as shown here:
$ sudo vi /etc/my /etc/my.cnf
Add the following lines to the configuration file and save it (press Esc then :, then w, and then q). We will use this configuration file to start the server in the next step.
[mysqld]
basedir=/usr/local/mysql/
datadir=/usr/local/mysql/data
OK, we are now ready to start MySQL for the first time. Use the mysqld command to start MySQL from the command line. We use this command instead of the /etc/init.d/mysql start command so we can check the output for errors. If there are no errors, you should see output like those shown here:
$ sudo bin/mysqld --defaults-file=/etc/my.cnf --user=mysql &
[1] 8745
$ 2019-11-17T02:09:41.429418Z 0 [Warning] [MY-011037] [Server] The CYCLE timer is not available. WAIT events in the performance_schema will not be timed.
2019-11-17T02:09:42.191155Z 0 [System] [MY-010116] [Server] /usr/local/mysql/bin/mysqld (mysqld 8.0.18) starting as process 8750
2019-11-17T02:09:58.600980Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed.
2019-11-17T02:09:59.167758Z 0 [System] [MY-010931] [Server] /usr/local/mysql/bin/mysqld: ready for connections. Version: '8.0.18'  socket: '/tmp/mysql.sock'  port: 3306  Source distribution.
2019-11-17T02:09:59.378833Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Socket: '/tmp/mysqlx.sock' bind-address: '::' port: 33060
Now we can test our MySQL server with the mysql client using the following command. Be sure to use the password displayed when you initialized the data directory. Listing 8-8 shows an example of using the mysql client to connect to the server for the first time. We will first display the version and then change the root user password. Notice we also shut down the server with the shutdown SQL command.
$ bin/mysql -uroot -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or g.
Your MySQL connection id is 8
Server version: 8.0.18
Copyright (c) 2000, 2019, 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> SELECT @@version;
+-----------+
| @@version |
+-----------+
| 8.0.18    |
+-----------+
1 row in set (0.00 sec)
mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'secret';
Query OK, 0 rows affected (0.11 sec)
mysql> shutdown;
Query OK, 0 rows affected (0.00 sec)
mysql> q
Listing 8-8

Connecting to MySQL for the First Time

Next, we must add the path to the MySQL binaries. We can do this easily by editing our Bash resource file using the command nano ~/.bashrc. When the file opens, add the following line to the bottom of the file. The next time you open a terminal, you can execute the MySQL applications and tools without specifying the path.
export PATH=${PATH}:/usr/local/mysql/bin
There is one final step needed—we must copy the startup and shutdown script (service) to allow us to automatically start MySQL at boot. To do so, copy the mysql.server file from the support-files folder from the build to the /etc/init.d/mysql file as shown in Listing 8-9. We will also test the server connection again and then shut it down with the sudo systemctl daemon-reload command to refresh the list of daemons and the sudo systemctl start or sudo systemctl stop commands to start or stop MySQL. You can also use sudo systemctl status command to see the status of MySQL. This can be helpful if you encounter errors or want to check to see if MySQL is running. Note that you may be prompted for a password when using the command. Also, you want to copy the mysql.server file from the build directory, not the root of the source code directory.
$ sudo cp ./support-files/mysql.server /etc/init.d/mysql
$ sudo chmod 0755 /etc/init.d/mysql
$ sudo systemctl daemon-reload
$ sudo systemctl start mysql
$ sudo systemctl status mysql
● mysql.service - LSB: start and stop MySQL
   Loaded: loaded (/etc/init.d/mysql; generated)
   Active: active (running) since Sat 2019-11-16 21:22:44 EST; 6s ago
     Docs: man:systemd-sysv-generator(8)
  Process: 11023 ExecStart=/etc/init.d/mysql start (code=exited, status=0/SUCCESS)
    Tasks: 40 (limit: 2200)
   Memory: 350.5M
   CGroup: /system.slice/mysql.service
           ├─11037 /bin/sh /usr/local/mysql//bin/mysqld_safe --datadir=/usr/local/mysql/data --
           └─11148 /usr/local/mysql/bin/mysqld --basedir=/usr/local/mysql/ --datadir=/usr/local
...
Nov 16 21:22:44 raspberrypi systemd[1]: Started LSB: start and stop MySQL.
$ mysql -uroot -p -e "select @@version"
Enter password:
+-----------+
| @@version |
+-----------+
| 8.0.18    |
+-----------+
$ sudo systemctl stop mysql
$ sudo systemctl status mysql
...
Nov 16 21:23:02 raspberrypi systemd[1]: Stopped LSB: start and stop MySQL.
Listing 8-9

Starting MySQL Automatically or Manually with systemctl

That’s it! We’ve installed MySQL server and tested that is works. It would also be a good idea to install the MySQL Shell on each server. In the next section, you tell MySQL to use the external drive instead for storing your databases and data.

Moving the Data Directory to the External Drive

Recall that you want to use MySQL to store your sensor data. As such, the sensor data may grow in volume and over time may consume a lot of space. Rather than risk filling up your boot image SD, which is normally only a few gigabytes, you can use an external drive to save the data. This section shows you how to tell MySQL to change its default location for saving data.

The steps involved require stopping the MySQL server, changing its configuration, and then restarting the server. Finally, you test the change to ensure that all new data is being saved in the new location. Begin by stopping the MySQL server:
$ sudo systemctl stop mysql
You must create a folder for the new data directory:
$ sudo mkdir /media/mysql/mysql_data
Now you copy the existing data directory and its contents to the new folder. Notice that you copy only the data and not the entire MySQL installation, which is unnecessary:
$ sudo cp -R /usr/local/mysql/data  /media/mysql/mysql_data
$ chown -R mysql mysql /media/mysql/mysql_data
Note

If you get permission errors, try changing the owner of the /media/mysql folder to mysql:mysql.

Next, you edit the configuration file for MySQL. In this case, you change the datadir line to read datadir = /media/mysql. It is also a good idea to comment out the bind-address line to permit access to MySQL from other systems on the network:
$ sudo vi /etc/mysql/my.cnf
There is one last step. You must change the owner and group to the MySQL user that was created on installation. Here is the correct command:
$ sudo chown -R mysql:mysql /media/mysql/mysql_data
Now you restart MySQL:
$ sudo systemctl start mysql
You can determine whether the changes worked by connecting to MySQL, creating a new database, and then checking to see if the new folder was created on the external drive, as shown in Listing 8-10.
$ ./bin/mysql -uroot -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or g.
Your MySQL connection id is 9
Server version: 8.0.18 Source distribution
Copyright (c) 2000, 2019, 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> SHOW VARIABLES LIKE 'datadir';
+---------------+----------------------------------+
| Variable_name | Value                            |
+---------------+----------------------------------+
| datadir       | /media/pi/mysql/mysql_data/data/ |
+---------------+----------------------------------+
1 row in set (0.08 sec)
mysql> CREATE DATABASE testme;
Query OK, 1 row affected (0.08 sec)
mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| testme             |
+--------------------+
5 rows in set (0.06 sec)
mysql> q
Bye
pi@raspberrypi:/usr/local/mysql $ sudo ls -lsa /media/pi/mysql/mysql_data/data
total 168024
    4 drwxr-x--- 7 mysql mysql     4096 Nov 27 15:09  .
    4 drwxr-xr-x 3 mysql mysql     4096 Nov 27 15:03  ..
    4 -rw-r----- 1 mysql mysql       56 Nov 27 15:03  auto.cnf
    4 -rw-r----- 1 mysql mysql      499 Nov 27 15:03  binlog.000001
    4 -rw-r----- 1 mysql mysql      178 Nov 27 15:03  binlog.000002
    4 -rw-r----- 1 mysql mysql      346 Nov 27 15:09  binlog.000003
    4 -rw-r----- 1 mysql mysql       48 Nov 27 15:06  binlog.index
...
    4 -rw-r----- 1 mysql mysql     3344 Nov 27 15:03  ib_buffer_pool
12288 -rw-r----- 1 mysql mysql 12582912 Nov 27 15:09  ibdata1
49152 -rw-r----- 1 mysql mysql 50331648 Nov 27 15:09  ib_logfile0
49152 -rw-r----- 1 mysql mysql 50331648 Nov 27 15:03  ib_logfile1
12288 -rw-r----- 1 mysql mysql 12582912 Nov 27 15:06  ibtmp1
...
    4 drwxr-x--- 2 mysql mysql     4096 Nov 27 15:09  testme
Listing 8-10

Testing the New Data Directory

In the output, the new database name is represented as the folder testme.

Well, there you have it—a new MySQL database server running on a Raspberry Pi!

If you are curious about what more you can do with your new database server, read on. In the next section, you tackle a very popular feature of MySQL called replication. It permits two or more servers to have copies of databases. For your purposes, it may be handy to use the copies as a backup, so you don’t have to do any manual file copying from your Raspberry Pi.

Advanced Project: Using MySQL Replication to Back Up Your Sensor Data

One of the nicest things about using an external drive to save your MySQL data is that at any point you can shut down your server, disconnect the drive, plug it in to another system, and copy the data. That may sound great if your Raspberry Pi database server is in a location that makes it easy to get to (physically) and if there are periods when it is OK to shut down the server.

However, this may not be the case for some sensor networks. One of the benefits of using a Raspberry Pi for a database server is that the server can reside in close proximity to the sensor nodes. If the sensor network is in an isolated area, you can collect and store data by putting the Raspberry Pi in the same location. But this may mean trudging out to a barn or pond or walking several football field lengths into the bowels of a factory to get to the hardware if there is no network to connect to your database server.

But if your Raspberry Pi is connected to a network, you can use an advanced feature of MySQL called replication to make a live, up-to-the-minute copy of your data. Not only does this mean you can have a backup, but it also means you can query the server that maintains the copy and therefore unburden your Raspberry Pi of complex or long-running queries. The Raspberry Pi is a very cool small-footprint computer, but a data warehouse it is not.

What Is Replication, and How Does It Work?

MySQL replication is an easy-to-use feature and yet a very complex and major component of the MySQL server. This section presents a bird’s-eye view of replication for the purpose of explaining how it works and how to set up a simple replication topology. For more information about replication and its many features and commands, see the online MySQL reference manual (http://dev.mysql.com/doc/refman/5.5/en/replication.html).

Replication requires two or more servers. One server must be designated as the origin or master. The master role means all data changes (writes) to the data are sent to the master and only the master. All other servers in the topology maintain a copy of the master data and are by design and requirement read-only servers. Thus, when your sensors send data for storage, they send it to the master. Applications you write to use the sensor data can read it from the slaves.

The copy mechanism works using a technology called the binary log that stores the changes in a special format, thereby keeping a record of all the changes. These changes are then shipped to the slaves and re-executed there. Thus, once the slave re-executes the changes (called events), the slave has an exact copy of the data.

The master maintains a binary log of the changes, and the slave maintains a copy of that binary log called the relay log. When a slave requests data changes from the master, it reads the events from the master and writes them to its relay log; then another thread in the slave executes those events from the relay log. As you can imagine, there is a slight delay from the time a change is made on the master to the time it is made on the slave. Fortunately, this delay is almost unnoticeable except in topologies with very high traffic (lots of changes). For your purposes, it is likely when you read the data from the slave, it is up to date. You can check the slave’s progress using the command SHOW SLAVE STATUS; among many other things, it shows you how far behind the master the slave is. You see this command in action in a later section.

Now that you have a little knowledge of replication and how it works, let’s see how to set it up. The next section discusses how to set up replication with the Raspberry Pi as the master and a desktop computer as the slave.

How to Set Up Replication

This section demonstrates how to set up replication from a Raspberry Pi (master) to a desktop computer (slave). The steps include preparing the master by enabling binary logging and creating a user account for reading the binary log, preparing the slave by connecting it to the master, and starting the slave processes. You conclude with a test of the replication system.

Preparing the Master
Replication requires the master to have binary logging enabled. It is not turned on by default, so you must edit the configuration file and turn it on. Edit the configuration file with sudo vi /etc/mysql/my.cnf, and turn on binary logging by uncommenting and changing the following lines:
server-id = 1
log_bin = /media/mysql/mysql_data/mysql-bin.log

The first line sets the server ID of the master. In basic replication (what you have for version 5.5), each server must have a unique server ID. In this case, you assign 1 to the master; the slave will have some other value, such as 2. Imaginative, yes?

The next line sets the location and name of the binary log file. You save it to your external drive because, like the data itself, the binary log can grow over time. Fortunately, MySQL is designed to keep the file to a reasonable size and has commands that allow you to truncate it and start a new file (a process called rotating). See the online reference manual (https://dev.mysql.com/doc/refman/8.0/en/replication.html) for more information about managing binary log files. Once the edits are saved, you can restart the MySQL server (or simply stop and then start).

Next, you must create a user to be used by the slave to connect to the master and read the binary log. There is a special privilege for this named REPLICATION SLAVE. The following code shows the correct GRANT statement to create the user and add the privilege. Remember the user and password you use here—you need it for the slave:
mysql> CREATE USER 'rpl'@'%' IDENTIFIED BY 'secret'
Query OK, 0 rows affected (0.01 sec)
mysql> GRANT REPLICATION SLAVE ON ∗.∗ TO 'rpl'@'%';
Query OK, 0 rows affected (0.01 sec)
But one more piece of information is needed for the slave. The slave needs to know the name of the binary log to read and what position in the file to start reading events. You can determine this with the SHOW MASTER STATUS command:
mysql> SHOW MASTER STATUS;
+--------------+----------+-------------+------------------+...
| File          | Position | Binlog_Do_DB | Binlog_Ignore_DB |...
+--------------+----------+-------------+------------------+...
| binlog.000003 |      878 |              |                  |...
+--------------+----------+-------------+------------------+...
1 row in set (0.00 sec)
Now that you have the master’s binary log file name and position as well as the replication user and password, you can visit your slave and connect it to the master. You also need to know the hostname or IP address of the Raspberry Pi as well as the port on which MySQL is running. By default, the port is 3306; but if you changed that, you should note the new value. Jot down all the information in Table 8-1.
Table 8-1

Information Needed from the Master for Replication

Item from Master

Value

IP Address or Hostname

 

Port

 

Binary log file

 

Binary log file position

 

Replication user ID

 

Replication user password

 

The MySQL server you want to use as a slave should be the same version as the server on the Raspberry Pi or at least a server that is compatible. The online reference manual specifies which MySQL versions work well together. Fortunately, the list of versions with issues is very short. In this section, you should have a server installed on your desktop or server computer and ensure that it is configured correctly.

The steps needed to connect a slave to a master include issuing a CHANGE MASTER command to connect to the master and a START SLAVE command to initiate the slave role on the server. Yes, it is that easy! Recall that you need the information from the master to complete these commands. The following commands show a slave being connected to a master running on a Raspberry Pi. Let’s begin with the CHANGE MASTER command as shown here:
mysql> CHANGE MASTER TO MASTER_HOST='10.0.1.17', MASTER_PORT=3306, MASTER_LOG_FILE='mysql-bin.000003', MASTER_LOG_POS=878, MASTER_USER="rpl", MASTER_PASSWORD="secret";
Query OK, 0 rows affected (0.22 sec)

This example uses the IP address of the Raspberry Pi, the port number (3306 is the default), the log file and position from the SHOW MASTER STATUS command, and the user and password for the replication user. If you typed the command correctly, it should return without errors. If there are errors or warnings, use the SHOW WARNINGS command to read the warnings and correct any problems.

The next step is to start the slave processes. This command is simply START SLAVE. It normally does not report any errors; you must use SHOW SLAVE STATUS to see them. Here are both of these commands in action as shown in Listing 8-11.

Tip

For wide results, use the G option to see the columns as rows (called vertical format).

mysql> start slave;
Query OK, 0 rows affected (0.00 sec)
mysql> show slave status G
∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 1. row ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗
       Slave_IO_State: Waiting for master to send event
  Master_Host: 10.0.1.17
  Master_User: rpl
  Master_Port: 3306
Connect_Retry: 60
      Master_Log_File: mysql-bin.000003
  Read_Master_Log_Pos: 107
       Relay_Log_File: clone-relay-bin.000003
Relay_Log_Pos: 4
Relay_Master_Log_File: mysql-bin.000001
     Slave_IO_Running: Yes
    Slave_SQL_Running: Yes
      Replicate_Do_DB:
  Replicate_Ignore_DB:
   Replicate_Do_Table:
       Replicate_Ignore_Table:
      Replicate_Wild_Do_Table:
  Replicate_Wild_Ignore_Table:
   Last_Errno: 0
   Last_Error:
 Skip_Counter: 0
  Exec_Master_Log_Pos: 107
      Relay_Log_Space: 555
      Until_Condition: None
       Until_Log_File:
Until_Log_Pos: 0
   Master_SSL_Allowed: No
   Master_SSL_CA_File:
   Master_SSL_CA_Path:
      Master_SSL_Cert:
    Master_SSL_Cipher:
       Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
       Last_SQL_Errno: 0
       Last_SQL_Error:
  Replicate_Ignore_Server_Ids:
     Master_Server_Id: 1
1 row in set (0.00 sec)
mysql>
Listing 8-11

Starting the Slave

Take a moment to slog through all these rows. There are several key fields you need to pay attention to. These include anything with error in the name and the state columns. For example, the first row (Slave_IO_State) shows the textual message indicating the state of the slave’s I/O thread. The I/O thread is responsible for reading events from the master’s binary log. There is also a SQL thread that is responsible for reading events from the relay log and executing them.

For this example, you just need to ensure that both threads are running (YES) and there are no errors. For detailed explanations of all the fields in the SHOW SLAVE STATUS command , see the online MySQL reference manual (https://dev.mysql.com/doc/refman/8.0/en/replication-configuration.html).

Now that the slave is connected and running, let’s check for that testme database on it:
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
+--------------------+
3 rows in set (0.00 sec)
mysql>

Wait! Where did it go? Wasn’t this example supposed to replicate everything? Well, yes and no. It is true that your slave is connected to the master and will replicate anything that changes on the master from this point on. Recall that you used the SHOW MASTER STATUS command to get the binary log file and position. These values are the coordinates for the location of the next event, not any previous events. Aha: you set up replication after the testme database was created.

How do you fix this? That depends. If you really wanted the testme database replicated, you would have to stop replication, fix the master, and then reconnect the slave. I won’t go into these steps, but I list them here as an outline for you to experiment on your own:
  1. 1.

    Stop the slave.

     
  2. 2.

    Go to the master and drop the database.

     
  3. 3.

    Get the new SHOW MASTER STATUS data.

     
  4. 4.

    Reconnect the slave.

     
  5. 5.

    Start the slave.

     

Got that? Good. If not, it is a good exercise to go back and try these steps on your own.

Once you get the master cleaned and replication restarted, go ahead and try to create a database on the master and observe the result on the slave. The following are the commands. I used a different database name in case you elected to not try the previous challenge as shown here:
mysql> create database testme_again;
Query OK, 1 row affected (0.00 sec)
mysql> show databases;
+--------------------+
| Database          |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| testme             |
| testme_again       |
+--------------------+
4 rows in set (0.01 sec)
mysql>
Returning to the slave, check to see what databases are listed there as shown here:
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| testme_again       |
+--------------------+
4 rows in set (0.00 sec)
mysql>

Success! Now your Raspberry Pi database server is being backed up by your desktop computer.

Component Shopping List

The only new component you need for this chapter is a surplus USB hard drive, which is listed in Table 8-2. Table 8-3 shows a list of the supporting hardware that is included in the shopping list from other chapters.
Table 8-2

Components Needed

Item

Vendors

Est. Cost USD

Qty Needed

Surplus hard drive

Any USB hard drive (surplus or purchased)

Varies

1

Table 8-3

Components Reused from Previous Chapters

Item

Vendors

Est. Cost USD

Qty Needed

Raspberry Pi Model 4B 2GB or 4GB RAM

sparkfun.com, adafruit.com, thepithut.com

$50 and up

1

Mini-HDMI cable

Most online and retail stores

Varies

1

HDMI or DVI monitor

Most online and retail stores

Varies

1

USB keyboard

Most online and retail stores

Varies

1

USB-C power supply

Most online and retail stores

Varies

1

SD card, 32GB or more

Most online and retail stores

Varies

1

Summary

This chapter introduced MySQL and gave you a crash course on how to use it. You also compiled and installed MySQL on a Raspberry Pi and saw how to use more advanced features of MySQL, like replication.

Although it does not have nearly the sophistication of a high-availability, five-nines uptime (99.999%) database server, the low-cost Raspberry Pi with an attached USB hard drive makes for a very small-footprint database server that you can put just about anywhere.

This is great because sensor networks, by nature and often by necessity, need to be small and low cost. Having to build an expensive database server is not usually the level of investment desired.

Furthermore, depending on your choice of host for the sensor, saving data is difficult. If you choose an Arduino as the host, saving the data to a database requires a connection to the Internet and reliance on another service to store your data. This is fine for cases where you can actually connect the sensor nodes to the Internet12 (or the sensor network’s aggregator node); but if you cannot or do not want to connect to the Internet, it is difficult to get data into a database server from the Arduino.

That is, it was until recently. As you will see, there is indeed a way to save sensor data from a sensor node. In the next chapter, you build a sensor node that saves its data in your new database server—directly from an Arduino!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.82.23