paper-TPCTC-PocketData/sections/4-queryc.tex

275 lines
22 KiB
TeX

\begin{figure}
\centering
\input{tables/query_breakdown}
\caption{\textbf{Types and numbers of SQL statements executed during the trace, and query features used in each.}}
\label{fig:breakdownByCatAndFeature}
\end{figure}
In this section we discuss the query complexity we observed during our study and illustrate typical workloads over pocket data.
Figure~\ref{fig:breakdownByCatAndFeature} summarizes all 45 million statements executed by SQLite over the 1 month period. As might be expected, \texttt{SELECT} forms almost three quarters of the workload by volume. \texttt{UPSERT} statements (\textit{i.e.}, \texttt{INSERT OR REPLACE}) form a similarly substantial 16\% of the workload --- more than simple \texttt{INSERT} and \texttt{UPDATE} statements combined. Also of note is a surprising level of complexity in \texttt{DELETE} statements, many of which rely on nested sub-queries when determining which records to delete.
\begin{figure*}
\begin{subfigure}[t]{0.5\textwidth}
\centering
\begin{tabular}{c|c}
\textbf{Client App} & \textbf{Statements Executed} \\ \hline
Google Play services & 14,813,949 \\
Media Storage & 13,592,982 \\
Gmail & 2,259,907 \\
Google+ & 2,040,793 \\
Facebook & 1,272,779 \\
Hangouts & 974,349 \\
Messenger & 676,993 \\
Calendar Storage & 530,535\\
User Dictionary & 252,650 \\
Android System & 237,154\\
\end{tabular}
\caption{}
\label{fig:topBottom10Apps:top}
\end{subfigure}%
\begin{subfigure}[t]{0.5\textwidth}
\centering
\begin{tabular}{c|c}
\textbf{Client App} & \textbf{Statements Executed} \\ \hline
Weather & 12 \\
Speedtest & 11 \\
KakaoStory & 8 \\
MX Player Pro & 4 \\
Quickoffice & 4\\
VLC & 4\\
Barcode Scanner & 2\\
Office Mobile & 2\\
PlayerPro & 2\\
KBS kong & 2 \\
\end{tabular}
\caption{}
\label{fig:topBottom10Apps:bottom}
\end{subfigure}%
\caption{\textbf{Apps that executed the (a) 10 most and (b) 10 fewest SQL statements.}}
\label{fig:topBottom10Apps}
\end{figure*}
Figure~\ref{fig:topBottom10Apps} shows the 10 most frequent and 10 least frequent clients of SQLite over the one month trace. The most active SQLite clients include internal Android services that broker access to data shared between apps such as personal media, calendars and address books, as well as pre-installed and popular social media apps. There is less of a pattern at the low end, although several infrequent SQLite clients are themselves apps that may be used only infrequently, especially on a phone-sized device. We suspect that the distribution of apps would differ significantly for a tablet-sized device.
\subsection{Database Reads}
\begin{figure*}
\centering
\begin{subfigure}[t]{0.5\textwidth}
\includegraphics[width=\textwidth]{graphs/select_breakdown_by_width}
\caption{}
\label{fig:coarseSelectComplexity:byWidth}
\end{subfigure}%
\begin{subfigure}[t]{0.5\textwidth}
\includegraphics[width=\textwidth]{graphs/select_breakdown_by_nesting}
\caption{}
\label{fig:coarseSelectComplexity:byNesting}
\end{subfigure}%
\caption{\textbf{\texttt{SELECT} queries by (a) number of tables accessed and (b) maximum nesting depth.}}
\label{fig:coarseSelectComplexity}
\end{figure*}
Of the 45 million queries analyzed, 33.47 million were read-only \texttt{SELECT} queries.
Figure~\ref{fig:coarseSelectComplexity} shows the distribution of \texttt{SELECT} queries by number of tables accessed by the query, as well as the maximum level of query nesting. Nesting includes from-nesting (\textit{e.g.}, \texttt{SELECT \ldots\ FROM (SELECT \ldots)}), as well as expression-nesting (\textit{e.g.}, \texttt{SELECT \ldots\ WHERE EXISTS (SELECT \ldots)}).
Even at this coarse-grained view of query complexity, the read-only portion of the embedded workload distinguishes itself from existing TPC benchmarks.
Like TPC-C~\cite{tpcc}, the vast majority of the workload involves simple, small requests for data that touch a small number of tables.
29.15 million, or about 87\% of the \texttt{SELECT} queries were simple select-project-join queries. Of those, 28.72 million or about 86\% of all queries were simple single-table scans or look-ups. In these queries, which form the bulk of SQLite's read workload, the query engine exists simply to provide an iterator over the relationally structured data it is being used to store.
Conversely, the workload also has a tail that consists of complex, TPC-H-like~\cite{tpch} queries. Several hundred thousand queries involve at least 2 levels of nesting, and over a hundred thousand queries access 5 or more tables. As an extreme example, our trace includes 10 similar \texttt{SELECT} queries issued by the Google Play Games Service\footnote{\url{https://developers.google.com/games/services/}}, each of which accesses up to 8 distinct tables to combine developer-provided game state, user preferences, device profile meta-data, and historical game-play results from the user.
\subsubsection{Simple \texttt{SELECT} Queries}
\begin{figure}
\centering
\input{tables/spjsort_by_width_and_where}
\caption{\textbf{Number of simple look-up queries subdivided by join width (number of tables) and number of conjunctive terms in the \texttt{WHERE} clause.}}
\label{fig:spjsByWidthAndWhere}
\end{figure}
We next examine more closely a class of \textit{simple look-up} queries, defined as any \texttt{SELECT} query that consists exclusively of selections, projections, joins, limit, and order by clauses, and which does not contain any nested sub-queries or unions.
Figure~\ref{fig:spjsByWidthAndWhere} shows queries of this class, broken down by the number of tables involved in the query (Join Width) and the complexity of the where clause, as measured in number of conjunctive terms (Where Clauses). For example, consider a query of the form:
\texttt{SELECT R.A FROM R, S WHERE R.B = S.B AND S.C = 10}
This query would have a join width of 2 (\texttt{R}, \texttt{S}) and 2 conjunctive terms (\texttt{R.B = S.B} and \texttt{S.C = 10}). For uniformity, \texttt{NATURAL JOIN} and \texttt{JOIN ON} (\textit{e.g.}, \texttt{SELECT R.A from R JOIN S ON B}) expressions appearing in the \texttt{FROM} clause are rewritten into equivalent expressions in the \texttt{WHERE} clause.
The first column of this table indicates queries to a single relation. Just over 1 million queries were full table scans (0 where clauses), and just under 27 million queries involved only a single conjunctive term. This latter class constitutes the bulk of the simple query workload, at just over 87\% of the simple look-up queries. Single-clause queries appear to be the norm. Recall that an N-way equi-join requires N-1 conjunctive terms; Spikes occur in the number of queries with one more term than strictly required to perform a join, suggesting a constraint on at least one relation.
\begin{figure}
\centering
{\footnotesize
\input{tables/sp_trivial_condition_breakdown}
}
\caption{\textbf{The \texttt{WHERE} clause structure for single-tabled simple lookup queries with a single conjunctive term in the \texttt{WHERE} clause.}}
\label{fig:singleClauseExpressions}
\end{figure}
Narrowing further, we examine simple look-up queries referencing only a single source table and a single conjunctive term in the WHERE clause. Figure~\ref{fig:singleClauseExpressions} summarizes the structure of the predicate that appears in each of these queries. In this figure, constant terms (Const) are any primitive value term (\textit{e.g.}, a quoted string, an integer, or a float), or any JDBC-style parameter ($?$). For simple relational comparators, we group together \textit{in}equalities (\textit{i.e.}, $<$, $\leq$, $>$, $\geq$ and $\neq$) under the symbol $\theta$, and explicitly list equalities. Other relational operators such as \texttt{LIKE}, \texttt{BETWEEN}, and \texttt{IN} are also seen with some frequency. However, the majority (85\% of all simple look-ups) are exact match look-ups.
Not surprisingly, this suggests that the most common use-case for SQLite is as a relational key-value store. As we show shortly through a per-app analysis of the data (Section~\ref{sec:select:perapp}), 24 out of the 179 apps that we encountered posed no queries other than exact look-ups and full table scans.
\subsubsection{Other \texttt{SELECT} Queries}
Figure~\ref{fig:allSelectConditionBreakdown} shows a similar breakdown for all 33.5 million \texttt{SELECT} queries seen. As before, the table shows the form of all expressions that appear as one of the conjunctive terms of a \texttt{WHERE} clause, alongside the number of queries where the expression appears. 31.0 million of these queries contain an exact lookup.
1.6 million queries contain at least one multi-attribute equality expression such as an equi-join constraint, lining up nicely with the 1.7 million queries that reference at least two tables.
\begin{figure}
\centering
{\footnotesize
\input{tables/select_condition_breakdown}
}
\caption{\textbf{WHERE clause expression structures, and the number of SELECT queries in which the structure appears as a conjunctive clause.}}
\label{fig:allSelectConditionBreakdown}
\end{figure}
App developers make frequent use of SQLite's dynamic typing: Where clauses include bare column references (\textit{e.g.}, \texttt{WHERE A}, implicitly equivalent to \texttt{WHERE A <> 0}) as well as bare bit-wise AND expressions (\textit{e.g.}, \texttt{A\&0xc4}). This latter predicate appearing in a half-million queries indicates extensive use of bit-arrays packed into integers.
\subsubsection{Functions}
\begin{figure}
\centering
{\footnotesize
\input{tables/select_functions}
}
\caption{\textbf{Functions appearing in SELECT queries by number of times the function is used.}}
\label{fig:selectFunctions}
\end{figure}
Functions extend the basic SQL syntax, providing for both specialized local data transformations, as well as computation of aggregate values. Figure~\ref{fig:selectFunctions} shows all functions appearing in \texttt{SELECT} queries during our trace, organized by the number of times that each function is used.
All functions that we saw are either built-in SQLite functions, or in the case of \texttt{PHONE\_NUMBERS\_EQUAL} are Android-specific extensions; No user-defined functions appeared in the trace.
Overall, the most common class of function was aggregate functions (\textit{e.g.}, \texttt{SUM}, \texttt{MAX}, \texttt{COUNT}), followed by string operations (\textit{e.g.}, \texttt{LENGTH} and \texttt{SUBSTR}).
The most commonly used function was \texttt{GROUP\_CONCAT}, an aggregate operator that constructs a string by concatenating its input rows. This is significant, as it means that the most commonly used aggregate operator is holistic --- its output size is linear in the number of input rows.
\subsubsection{Per-Application Analysis}
\label{sec:select:perapp}
\begin{figure*}
\vspace*{-0.3in}
\begin{subfigure}[t]{0.5\textwidth}
\centering
\includegraphics[width=0.9\textwidth]{graphs/select_count_cdf_by_app}
\caption{}
\label{fig:selectByApp:all}
\end{subfigure}%
\begin{subfigure}[t]{0.5\textwidth}
\centering
\includegraphics[width=0.9\textwidth]{graphs/select_percent_simple_cdf_by_app}
\caption{}
\label{fig:selectByApp:simple}
\end{subfigure}%
\caption{\textbf{Breakdown of \texttt{SELECT} queries by app. (a) Cumulative distribution of applications by the number of \texttt{SELECT} queries issued (note the logarithmic scale). (b) Cumulative distribution of applications by the percent of the app's \texttt{SELECT} queries that are full table scans or exact look-ups.}}
\label{fig:selectByApp}
\end{figure*}
We next break the \texttt{SELECT} workload down by the calling application (app).
Due to limitations of the logging infrastructure, 4.32 million queries (just over 12.9\% of the workload) could not be associated with a specific application, and our app-specific analysis excludes these queries.
Additionally, system services in Android are often implemented as independent apps and counted as such in the numbers presented.
Over the course of the one-month trace we observed 179 distinct apps, varying from built-in Android applications such as \textit{Gmail} or \textit{YouTube}, to video players such as \textit{VLC}, to games such as \textit{3 Kingdoms}. Figure~\ref{fig:selectByApp:all} shows the cumulative distribution of apps sorted by the number of queries that the app performs. The results are extremely skewed, with the top 10\% of apps each posing more than 100 thousand queries over the one month trace. The most query-intensive system service, \textit{Media Storage} was responsible for 13.57 million queries or just shy of 40 queries per minute per phone. The most query-intensive user-facing app was \textit{Google+}, which performed 1.94 million queries over the course of the month or 5 queries per minute.
At the other end of the spectrum, the bottom 10\% of apps posed as few as 30 queries over the entire month.
We noted above that a large proportion of \texttt{SELECT} queries were exact look-ups, suggesting that many applications running on the device might be using SQLite as a simple key-value store. This suggestion was confirmed in our app-level analysis. For example, approximately half of one specific app's query workload consisted of the following two queries:
\begin{verbatim}
INSERT OR REPLACE INTO properties(property_key,property_value) VALUES (?,?);
SELECT property_value FROM properties WHERE property_key=?;
\end{verbatim}
In this query, \texttt{?} is a prepared statement parameter that acts as a place holder for values that are bound when the prepared statement is evaluated.
To broaden the scope of our search for key/value queries, we define a key-value look-up query as a \texttt{SELECT} query over a single relation that either performs a full table scan, or performs an exact look-up on a single attribute.
Figure~\ref{fig:selectByApp:simple} shows the cumulative distribution of apps sorted by the percent of its queries that are key-value lookup queries. For 24 apps (13.4\%), we observed only key-value queries during the entire, month-long trace.
% Adobe Reader, Barcode Scanner, BuzzFeed, Candy Crush Saga, Discover, Evernote, Foursquare, GPS Status, Google Play Newsstand, Google Sky Map, KBS kong, LTE Discovery, MX Player Pro, Muzei, My Tracks, Office Mobile, PayPal, Quickoffice, SignalCheck Lite, Titanium Backup, TuneIn Radio Pro, VLC, Weather, Wifi Analyzer
\subsection{Database Writes}
Write statements, \texttt{INSERT}, \texttt{INSERT OR REPLACE} (here abbreviated as \texttt{UPSERT}), \texttt{UPDATE}, and \texttt{DELETE}, together constitute 11.6 million statements or about 25\% of the trace. As shown in Figure~\ref{fig:breakdownByCatAndFeature}, the most prevalent operation is the \texttt{UPSERT}. \texttt{INSERT} and \texttt{UPSERT} together account for 9.3 million operations, of which 7.4 are \texttt{UPSERT}s. In many of these cases, the use of \texttt{UPSERTS} appears to be defensive programming on the part of wrapper libraries that make use of SQLite (\textit{e.g.}, Object Relational Mappers, or ORMs). \texttt{UPSERTS} are also the canonical form of update in key-value stores, further supporting the argument that a large fragment of SQLite's traffic is based on key-value access patterns.
%\ask{At what frequency do upserts need to replace? Unfortunately, we don't have a number of rows modified to figure this out.}
\subsubsection{\texttt{DELETE} Statements}
\begin{figure}
\centering
{\footnotesize
\input{tables/delete_condition_breakdown}
}
\caption{\textbf{\texttt{WHERE} clause expression structures, and the number of \texttt{DELETE} statements in which the structure appears.}}
\label{fig:allDeleteConditionBreakdown}
\end{figure}
The trace includes 1.25 million \texttt{DELETE} statements. This was by far the most expensive class of statement, with an average \texttt{DELETE} taking just under 4 ms to complete. A significant portion of this cost is attributable to the use of \texttt{DELETE} as a form of bulk erasure. As shown in Figure~\ref{fig:allDeleteConditionBreakdown}, 323 thousand \texttt{DELETE}s have no exact match condition in their WHERE clause, while 528 thousand do include a range predicate.
\texttt{DELETE} predicates can become quite complex; 46,122 \texttt{DELETE}s (just under 3.7\%) use nested \texttt{SELECT} queries, and touch as many as 7 separate tables (in 616 cases).
This suggests extensive use of \texttt{DELETE} as a form of garbage-collection or cache invalidation, where the invalidation policy is expressed through SQL.
%\ask{Suggestion is one thing... how might we validate the claim that DELETE is used for cache invalidation?}
\subsubsection{\texttt{UPDATE} Statements}
Slightly over 1 million statements executed by SQLite over the course of the month were \texttt{UPDATE} statements. Figure~\ref{fig:allUpdateConditionBreakdown} breaks down the predicates used to select rows to be updated. Virtually all \texttt{UPDATE} statements involved an exact look-up. Of the million updates, 28 thousand did not include an exact look-up.
193 of the \texttt{UPDATE} statements relied on a nested \texttt{SELECT} statement as part of their \texttt{WHERE} clause, including 56 that involved 2 levels of nesting. Of the 193 \texttt{UPDATE}s with nested subqueries, 25 also involved aggregation.
Although the \texttt{WHERE} clause of the updates included a variety of expressions, \textit{every single setter} in every \texttt{UPDATE} statement in the trace assigned a constant value;
% , as in the following statement:
% \begin{verbatim}
% UPDATE ScheduledTaskProto SET value=?,key=?,sortingValue=? WHERE key = ?;
% \end{verbatim}
Not a single \texttt{UPDATE} expression attempted to compute new values using SQL, suggesting a strong preference for computing updated values in the application itself. This is not entirely unexpected, as the database lives in the address space of the application. Consequently, it is feasible to first perform a \texttt{SELECT} to read values out of the database and then perform an \texttt{UPDATE} to write out the changes, a tactic used by many ORMs. An unfortunate consequence of this tactic is that ORMs cache database objects at the application layer unnecessarily, suggesting that a stronger coupling between SQL and Java (\textit{e.g.}, through language primitives like LINQ~\cite{box2007linq} or StatusQuo~\cite{cheung2013statusquo}) could be of significant benefit to Android developers.
\begin{figure}
\centering
{\footnotesize
\input{tables/update_condition_breakdown}
}
\caption{\textbf{\texttt{WHERE} clause expression structures, and the number of \texttt{UPDATE} statements in which the structure appears.}}
\label{fig:allUpdateConditionBreakdown}
\end{figure}
% \begin{figure}
% \centering
% \includegraphics[width=\textwidth]{graphs/update_target_cols}
% \caption{Frequency with which \texttt{UPDATE} statements update a given number of rows.}
% \label{fig:colsUpdated}
% \end{figure}
% Figure~\ref{fig:colsUpdated} breaks down \texttt{UPDATE} statements by the number of columns updated by the statement. Under the hypothesis that SQLite is being used as a pure key-value store, we might expect a strong skew in favor of
\subsubsection{Per-Application Analysis}
\begin{figure}
\begin{subfigure}[t]{0.5\textwidth}
\centering
\includegraphics[width=0.9\textwidth]{graphs/data_mod_ops_cdf_by_app}
\caption{}
\label{fig:updateByApp:modOps}
\end{subfigure}%
\begin{subfigure}[t]{0.5\textwidth}
\centering
\includegraphics[width=0.9\textwidth]{graphs/read_write_ratio_cdf_by_app}
\caption{}
\label{fig:updateByApp:writeRatio}
\end{subfigure}%
\caption{\textbf{App-level write behavior. (a) Cumulative distribution of applications by number of data manipulation statements performed (note the logarithmic scale). (b) Cumulative distribution of applications by read/write ratio. }}
\label{fig:updateByApp}
\end{figure}
Figure~\ref{fig:updateByApp:modOps} illustrates app-level write workloads, sorting applications by the number of \texttt{INSERT}, \texttt{UPSERT}, \texttt{UPDATE}, and \texttt{DELETE} operations that could be attributed to each. The CDF is almost perfectly exponential, suggesting that the number of write statements performed by any given app follows a long-tailed distribution, a feature to be considered in the design of a pocket data benchmark.
Figure~\ref{fig:updateByApp:writeRatio} breaks apps down by their read/write ratio. Surprisingly, 25 apps (14\% of the apps seen) did not perform a single write over the course of the entire trace. Manual examination of these apps suggested two possible explanations. Several apps have reason to store state that is updated only infrequently. For example, \textit{JuiceSSH} or \textit{Key Chain} appear to use SQLite as a credential store. A second, far more interesting class of apps includes apps like \textit{Google Play Newsstand}, \textit{Eventbrite}, \textit{Wifi Analyzer}, and \textit{TuneIn Radio Pro}, which all have components that query data stored in the cloud. We suspect that the cloud data is being encapsulated into a pre-constructed SQLite database and being pushed to, or downloaded by the client applications.
This type of behavior might be compared to a bulk ETL process or log shipment in a server-class database workload, except that here, the database has already been constructed. Pre-caching through database encapsulation is a unique feature of embedded databases, and one that is already being used in a substantial number of apps.
% Barcode Scanner, BharatMatrimony, CamCard, CityMaps2Go Pro, Discover, Download Manager, Eventbrite, GPS Status, Google Play Newsstand, JuiceSSH, KBS kong, Key Chain, LTE Discovery, MX Player Pro, My Tracks, PlayerPro, Pushbullet, Quickoffice, SignalCheck Lite, Sound Search for Google Play, Splitwise, TuneIn Radio Pro, VLC, WeChat, Wifi Analyzer
%% LocalWords: SQLite UPSERT App Gmail Facebook Android Speedtest
%% LocalWords: KakaoStory MX Quickoffice VLC Barcode PlayerPro KBS
%% LocalWords: kong Apps apps pre TPC SQLite's equi Const JDBC app
%% LocalWords: SUBSTR CONCAT cdf app's YouTube BuzzFeed ms
%% LocalWords: Evernote LTE Muzei PayPal SignalCheck Lite TuneIn
%% LocalWords: Wifi UPSERTS Mappers ORMs upserts subqueries LINQ
%% LocalWords: StatusQuo Truffle cols ops JuiceSSH Eventbrite ETL
%% LocalWords: BharatMatrimony CamCard CityMaps Pushbullet WeChat
%% LocalWords: Splitwise