mysql2: a Ruby binding that deliberately refuses to be a one to one mapping
A modern, simple and very fast Mysql library for Ruby - binding to libmysql
At a glance
- What is it?
- The mysql2 gem wraps the MySQL C client behind three classes and an enumerable result, forces the connection charset to UTF-8 or binary, and spends most of its documentation on finding the client library, matching OpenSSL to your Ruby and building against sanitizers. The interesting engineering is in the install path rather than the query path, which is where a native extension always ends up being about your operating system.
- Who is it for?
- mysql2 is the right choice if you want the fastest reasonable path from Ruby to MySQL and you are willing to own a native build, because the small API is genuinely smaller than the C library it wraps and the iterable result is what makes it pleasant to use.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 20 days ago.
- What is it written in?
- Mainly Ruby, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 28, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Three classes, an enumerable result, and two ways to run a query
The design statement is the second paragraph of the readme and it is the most important thing in the repository. The gem is meant to serve the extremely common case of connecting, querying and iterating over results. Some database libraries serve as a direct one to one mapping of the already complex C interface. This one does not. That is a deliberate refusal, and the API is the three classes that follow from it. A client is the connection. A result comes back from a query on the connection and it includes Enumerable, which is the single decision that does the most work, because iterating a result becomes ordinary Ruby collection code rather than a loop over a cursor. A statement comes back from preparing on the connection, and executing it gives you a result. So there are exactly two execution paths, a text query that returns a result directly, and a prepared statement you execute, which is the path to use when a value has to stay out of the query text. Anything else the C library offers is not surfaced, and the readme is upfront that this is a wrapper for the common case rather than a complete binding. For a reader deciding between this and the older pure-Ruby driver, that is the whole comparison: a smaller surface, an iterable result, a native build step, and no coverage of the corners of the C API. The gem also forces the connection charset to UTF-8 or binary and uses encoding-aware calls to the server where it can. The hedge in that last sentence is honest, since not every call has an encoding-aware variant, and the forcing is the part to think about before you install.
A forced charset, and a query path with two execution models
The charset decision is the kind that saves you from a class of bug and simultaneously removes an option you might want. The gem sets the connection to UTF-8 or to binary and does not offer a third choice, so a column declared in a legacy single-byte encoding is read as UTF-8 bytes, and a conversion at that boundary is on you rather than on the server. Whether that is the right default depends entirely on what you are connecting to: for an application database that is almost always right, and for a warehouse holding eight-bit data in a single-byte charset it is a decision you will be living with. The readme says encoding-aware calls are used where possible rather than everywhere, which is the honest version of the claim, because the C interface has encoding-aware variants for some operations and not for others. The second thing in this section is the execution model, which the example files answer rather than the prose. There are two examples, one using threads and one using an event-driven reactor framework. That pairing tells you the library is expected to be usable from both models, which is what you would expect from a binding to a synchronous C interface where the call blocks until the server replies. Neither example changes the API, so the choice is yours and it affects your application architecture rather than your queries. The practical consequence for a threaded application is that a connection is not thread safe, since a client is a socket, so the design question is whether you hold one client per thread or one client behind a queue, and the readme does not prescribe either. That is a decision the examples are there to inform, so they are worth reading before you write the pool.
A four-step search for the client library, and two mutually exclusive overrides
Most of this readme is about the build, and the first part of the build is finding the C library. The gem will look in a defined order: an explicit directory option if you gave one, then an explicit path to the configuration binary if you gave one, then several typical locations for that configuration binary, which the readme notes is the default for the majority of users, and finally a fixed directory under the local prefix. The two overrides are documented with more care than most gems give their build options, and the detail that matters is that they are mutually exclusive. Passing a directory tells the gem to look for the library and the headers in the library and include subdirectories of that directory and not to ask the configuration binary anything at all. Passing a path to the configuration binary does the opposite, asking that binary for the compiler and linker arguments. Supplying both is contradictory and the readme says so. The third option is the one that matters in production rather than on your laptop, an option to override the runtime search path baked into the built extension. The stated reason is deploying to a system where the libraries sit somewhere different than they did on the build machine, and the important word is overrides, because it replaces whatever the build computed rather than adding to it. That is the difference between a gem that works on one host and a gem that works on a fleet, and it is a two-word flag that most people never find. Everything in this section exists because the gem is a native extension, and that is the honest reason to read an install guide this carefully for a database driver.
Sanitizer builds in a released gem, and getting line numbers out of them
There is a build option here that you will not find in most released extensions, and it is worth a section of its own. An option takes a list of sanitizers for the two major compilers and enables them: address, control flow integrity, integer, memory, thread and undefined behaviour. The behaviour on the two ways you can use it is strict in a way that shows the author expects this to be used deliberately rather than casually. With no argument, it tries to enable all of them and fails if none are available. With a comma-separated list, the configure step fails unless every one you named is available, rather than silently enabling the subset that happens to exist. And the readme warns in the same breath that some sanitizers carry a performance penalty and that the address sanitizer may require a runtime library present on the machine. So the feature is for finding memory errors in your own application's use of the extension, not for production. The second half of the feature is the part that makes it usable, because a sanitizer report without file and line numbers is close to useless. The readme gives you the environment variables to set, a path to a symbolizer binary and an option to turn symbolization on, with a note to adjust the path for your system. If you are chasing a crash in a native database driver, building with the address and undefined behaviour sanitizers and following those two lines is a better first hour than reading the extension's C source.
One OpenSSL per process, and a command that works the argument out for you
macOS is where this gem is hardest to install, and the reason is a single process-level constraint. Later versions of the operating system no longer ship a linkable OpenSSL library, so you install one yourself, and then the catch: the Ruby runtime and the MySQL client libraries must be compiled against the same family of OpenSSL, and the readme names the 3.x series, because only one can be loaded into a process at runtime. Get that wrong and the failure is a load error rather than anything that points at the mismatch, which is why the readme says it plainly. The commands are short, and the first one installs the specific OpenSSL series along with a compression library whose purpose the readme does not explain, which is a small loose end worth noting if you are wondering what it is for.
brew install openssl@3 zstd
gem install mysql2 -- --with-openssl-dir=$(brew --prefix openssl@3)There is a better way if you use Bundler, which most Ruby projects do, because you can put the build arguments in the bundler configuration rather than installing a global gem, and the readme's second example does that with a local scope so the setting does not leak into unrelated projects on the machine. Then there is the third example, and it is the best piece of advice in the whole readme. Rather than telling you to work out the path yourself, it reads your own Ruby's build configuration, extracts the argument that says where that Ruby was compiled against, and passes exactly that to the gem's build. One command, no guessing, and it cannot disagree with the interpreter you are running. One detail applies to all three and trips people up constantly: the doubled separator. The readme explains that it separates the arguments the package manager itself interprets from the additional arguments passed through to the gem's build step, and the same applies to the bundler form. If you have ever passed a build option without it and wondered why the gem ignored it, that is the answer.
Windows ships MariaDB's connector, and copies the library into the gem
The Windows story is the one place where the gem makes a decision for you, and it is a consequential one. The precompiled gem for Windows vendors MariaDB's Connector/C, built from the same package that a native Windows build would install through the platform's own package manager, and the readme points at the gemspec to show which package. So on Windows you are connecting through MariaDB's implementation of the C interface rather than Oracle's, whether or not the server on the other end is MySQL or MariaDB. For most applications that is the right trade, because it is the connector that is actually built and signed for that platform, and the readme treats it as the default rather than as a compromise. If you would rather use a local copy of the Oracle connector, there is a flag to point the build at a directory on disk, and the readme notes in passing that the path in that flag may use forward slashes on Windows, which is the kind of detail that saves an afternoon. The other default is a file operation rather than a build choice: the MariaDB shared library is copied into the gem's own directory by default. That keeps the extension self contained and means you are not relying on a system-wide library, which is the right behaviour for a precompiled binary, and it also means the file that has to be present for the gem to load lives somewhere inside your bundle. Requirements for Windows are otherwise the usual ones, Ruby plus a compiler toolchain, with the Ruby installer distribution recommended, so the precompiled path is the one to prefer unless you have a reason not to.
The most common Linux failure is a library without a header
The Linux section is short and contains the single most useful troubleshooting line in the readme. The most common issue the maintainers see is a user who has the client shared library on the machine and is missing the header file, and the fix is to install the development package rather than the runtime one. The distribution packages are named, including the MariaDB one, the Oracle one and the generic default one, with a pointer to the distribution's own guide for the particular name. That failure mode is not specific to this gem, it is what happens to every native extension that compiles against a C library, and it is worth internalising as a category: if a build fails looking for a header, you have installed the wrong package, and no amount of build flag juggling fixes it. The rest of the repository tells you what kind of project this is. There is a linter configuration and, next to it, a file recording the linter's outstanding offences, which is the same approach used elsewhere in this sample set of projects, namely formalise the existing debt so that new code is held to a clean standard. There is a security policy, which for a database driver is the document to read before you adopt it. There is a directory of benchmarks, which is notable given that the project describes itself as very fast, and notable in what it does not contain: the readme publishes no benchmark numbers at all, so the speed claim in the description is a claim rather than a measurement you can check. The licence is MIT, the build is a gemspec plus a Rakefile, and the release record is a 0.5 line with three recent versions, the newest from September 2025 and the last push on 2026-09-09, so the source is being touched more often than a version is cut.
Editorial conclusion
mysql2 is the right choice if you want the fastest reasonable path from Ruby to MySQL and you are willing to own a native build, because the small API is genuinely smaller than the C library it wraps and the iterable result is what makes it pleasant to use. Do not choose it if your connection needs a charset other than UTF-8 or binary, since the gem forces the choice rather than offering it, and if you need a different encoding the conversion has to happen at the column level. On a development machine, plan for the build to be the hard part rather than the code. On Linux that means the development headers rather than just the shared library. On macOS it means installing a specific major version of OpenSSL and pointing the build at it, and preferably letting the command in the readme work the argument out from your own Ruby so the two cannot disagree. On Windows it means accepting MariaDB's connector unless you supply your own. And when something does go wrong at runtime, build against the sanitizers before you debug by hand, because a released gem offering that option is unusual and it will find the memory error that a backtrace alone will not.
Frequently asked questions
What does the mysql2 API consist of?
Three classes. A client is the connection, a result comes back from a query and includes Enumerable so it iterates as a Ruby collection, and a statement comes back from preparing on the connection and produces a result when executed. The gem explicitly does not expose the full C interface one to one.
Which character set does the connection use?
The gem forces UTF-8 or binary for the connection and uses encoding-aware calls to the server where it can, so a single-byte encoded column is not converted for you and the choice is not configurable at the connection level.
How do I install it on macOS when the system has no linkable OpenSSL?
Install the 3.x series of OpenSSL, install the gem with the build option pointing at it, and make sure the Ruby runtime and the client libraries are compiled against the same family, since only one can be loaded at runtime. The readme also gives a command that extracts that path from your own Ruby's build configuration and passes it to the gem automatically.
Which client library does the precompiled Windows gem use?
MariaDB's Connector/C, built from the same package a native Windows build installs with the platform package manager. If you want a local copy of the Oracle connector instead, pass a directory to the build so it looks in that directory's library and include subdirectories rather than asking the configuration binary.
What is the most common install failure on Linux?
Having the client shared library present but the header file missing, which means the development package is not installed. The readme names the relevant packages for several distributions and points at the distribution's own package guide for the exact name.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/brianmario-mysql2)