@liyuj
2016-10-06T20:54:31.000000Z
字数 11919
阅读 3918
Apache-Ignite-1.7.0-中文开发手册
Ignite目前包括了一个ODBC驱动,可以通过标准SQL查询和原生ODBC API从缓存中获取分布式数据。
要了解ODBC的细节,可以参照ODBC开发者参考。
Apache Ignite的ODBC驱动实现了ODBC API的3.0版。
Ignite的ODBC驱动官方在如下环境中进行了测试:
OS | Windows(XP及以上,32位和64位版本) Windows Server(2008及以上,32位和64位版本) Ubuntu(14.x和15.x,64位) |
---|---|
C++编译器 | MS Visual C++ (10.0及以上), g++ (4.4.0及以上) |
Visual Studio | 2010及以上 |
Ignite的ODBC驱动作为Ignite发行版的一部分,以源代码的形式提供,因此在使用之前需要进行构建。关于如何获取和设置Ignite本身,可以参照1.基本概念章节。
因为ODBC驱动是用C++编写的,因此它是作为Ignite C++的一部分提供的,并且依赖于一些C++库,更具体的就是它依赖于utils
和binary
Ignite库,这就意味着,在构建ODBC驱动本身之前,需要先构建它们。
在Windows上构建
如果要在Windows上构建ODBC驱动,需要MS Visual Studio 2010及以后的版本,一旦打开了Ignite方案%IGNITE_HOME%\platforms\cpp\project\vs\ignite.sln
(或者ignite_86.sln
,32位平台),在方案浏览器中左击Ignite项目,然后选择“Build”,Visual Studio会自动地检测并且构建所有必要的依赖。
如果使用VS 2015及以后的版本(MSVC14.0及以后),如果要成功构建odbc项目,需要额外添加
legacy_stdio_definitions.lib
库,这是由于MS的破坏性变更导致的。
一旦构建过程完成,会在%IGNITE_HOME%\platforms\cpp\project\vs\x64\Release
中找到odbc.dll
。
在Linux上构建
在一个基于Linux的操作系统中,如果要构建及使用Ignite ODBC驱动,需要手动安装选择的ODBC驱动管理器,Ignite ODBC驱动已经使用UnixODBC进行了测试。
要构建驱动及其依赖,还需要GCC
,G++
以及Make
。
如果所有必需的都安装好了,可以通过如下方式构建Ignite ODBC驱动:
cd $IGNITE_HOME/platforms/cpp
libtoolize && aclocal && autoheader && automake --add-missing && autoreconf
./configure --enable-odbc --disable-node --disable-core
make
#The following step will most probably require root privileges:
make install
一旦构建过程完成,可以通过如下命令找到ODBC驱动位于何处:
whereis libignite-odbc
路径很可能是:/usr/local/lib/libignite-odbc.so
。
要使用ODBC驱动,首先要在系统中进行注册,因此ODBC驱动管理器必须能找到它。
在Windows上安装
在32位的Windows上需要使用32位版本的驱动,而在64位的Windows上需要使用64位版本的驱动,也可以在64位的Windows上同时安装32位和64位版本的驱动,这样32位和64位的应用都可以使用驱动。
要在Windows上安装驱动,首先要为驱动在文件系统中选择一个目录,选择一个位置后就可以把驱动放在哪并且确保所有的驱动依赖可以被解析,也就是说,他们要么位于%PATH%
,要么和驱动位于同一个目录。
之后,就需要使用%IGNITE_HOME%/platforms/cpp/odbc/install
目录下的安装脚本之一,注意,要执行这些脚本,很可能需要管理员权限。
X86:
install_x86 <absolute_path_to_32_bit_driver>
AMD64:
install_amd64 <absolute_path_to_64_bit_driver> [<absolute_path_to_32_bit_driver>]
在Linux上安装
要在Linux上构建和安装ODBC驱动,首先需要安装ODBC驱动管理器,Ignite ODBC驱动已经使用UnixODBC进行了测试。
如果已经构建完成并且执行了make install
命令,libignite-odbc.so
很可能会位于/usr/local/lib
,要在ODBC驱动管理器中安装ODBC驱动并且可以使用,需要按照如下的步骤进行操作:
ldd
命令像如下这样进行检查(假定ODBC驱动位于/usr/local/lib
):ldd /usr/local/lib/libignite-odbc.so
,如果存在到其他库的无法解析的链接,需要将这些库文件所在的目录添加到LD_LIBRARY_PATH
;$IGNITE_HOME/platforms/cpp/odbc/install/ignite-odbc-install.ini
文件,并且确保Apache Ignite
段的Driver
参数指向libignite-odbc.so
所在的正确位置;odbcinst -i -d -f $IGNITE_HOME/platforms/cpp/odbc/install/ignite-odbc-install.ini
,要执行这条命令,很可能需要root权限。到现在为止,Ignite的ODBC驱动已经安装好了并且可以用了,可以像其它ODBC驱动一样,连接、使用。
Ignite的ODBC驱动支持标准的连接串格式,下面是正常的语法:
connection-string ::= empty-string[;] | attribute[;] | attribute; connection-string
empty-string ::=
attribute ::= attribute-keyword=attribute-value | DRIVER=[{]attribute-value[}]
attribute-keyword ::= identifier
attribute-value ::= character-string
简单来说,连接串就是分号分割的键值条目列表,在下面可以看到连接串的示例。
Ignite的ODBC驱动可以使用如下的连接串/DSN参数:
属性关键字 | 描述 | 默认值 |
---|---|---|
SERVER | 要连接的节点地址 | localhost |
PORT | 节点的OdbcProcessor监听的端口 | 10800 |
CACHE | 缓存名,如果未定义会使用默认的缓存,注意,缓存名是区分大小写的。 |
所有的参数名是不区分大小写的,因此,SERVER
,Server
和server
都是有效的参数名,并且指向同一个参数。
下面的串,可以用于SQLDriverConnect
ODBC调用,来建立与Ignite节点的连接。
指定缓存:
DRIVER={Apache Ignite};SERVER=localhost;PORT=10800;CACHE=MyCache
默认缓存:
DRIVER={Apache Ignite};SERVER=localhost;PORT=10800
像数据库一样访问Ignite。
ODBC驱动内部使用字段查询来获取Ignite缓存中的数据,这意味着通过ODBC只可以访问这些SQL查询可以访问的字段。
下面显示的是可以通过ODBC驱动进行查询的两个类的示例:
Person:
/** All fields of the class will be visible in SQL. */
public class Person {
@QuerySqlField
private long id;
@QuerySqlField
public Long orgId;
@QuerySqlField
private String name;
@QuerySqlField
private double salary;
}
Organization:
/** All fields of the class will be visible in SQL. */
public class Organization {
@QuerySqlField
private Long id;
@QuerySqlField
private String name;
}
预定义字段
除了通过@QuerySqlField
注解标注的所有字段外,每个表都有两个特别的预定义字段:_key
和_val
,他们表示到整个键和值对象的链接。这非常有用,比如,当它们中的一个是基本类型并且希望通过它们的值进行过滤时,要实现这一点,可以执行像SELECT * FROM Person WHERE _key = 100
这样的查询。
现在,可以试着使用ODBC来运行一个小例子来从缓存中查询一些数据,首先,需要稍微改一下上面的类:
Person:
/** All fields of the class will be visible in SQL. */
public class Person {
private static final AtomicLong ID_GEN = new AtomicLong();
@QuerySqlField
private long id;
@QuerySqlField
public Long orgId;
@QuerySqlField
private String name;
@QuerySqlField
private double salary;
public Person(Organization org, String name, double salary) {
id = ID_GEN.incrementAndGet();
orgId = org.id();
this.name = name;
this.salary = salary;
}
}
Organization:
/** All fields of the class will be visible in SQL. */
public class Organization {
private static final AtomicLong ID_GEN = new AtomicLong();
@QuerySqlField
private Long id;
@QuerySqlField
private String name;
public Organization(String name) {
id = ID_GEN.incrementAndGet();
this.name = name;
}
}
下一步,需要正确地创建和初始化要进行查询的缓存:
// Using deafault config.
try (Ignite ignite = Ignition.start("config/default-config.xml")) {
CacheConfiguration<Long, Organization> orgCacheCfg = new CacheConfiguration<>("Organization");
orgCacheCfg.setCacheMode(CacheMode.PARTITIONED); // Default.
ogCacheCfg.setIndexedTypes(Long.class, Organization.class);
CacheConfiguration<AffinityKey<Long>, Person> personCacheCfg = new CacheConfiguration<>("Person");
personCacheCfg.setCacheMode(CacheMode.PARTITIONED); // Default.
personCacheCfg.setIndexedTypes(AffinityKey.class, Person.class);
// Populate cache.
try (
IgniteCache<Long, Organization> orgCache = ignite.getOrCreateCache(orgCacheCfg);
IgniteCache<AffinityKey<Long>, Person> personCache = ignite.getOrCreateCache(personCacheCfg)
) {
orgCache.clear();
// Organizations.
Organization org1 = new Organization("ApacheIgnite");
Organization org2 = new Organization("Other");
orgCache.put(org1.id(), org1);
orgCache.put(org2.id(), org2);
personCache.clear();
// People.
Person p1 = new Person(org1, "John Doe", 2000);
Person p2 = new Person(org1, "Jane Doe", 1000);
Person p3 = new Person(org2, "John Smith", 1000);
Person p4 = new Person(org2, "Jane Smith", 2000);
// Note that in this example we use custom affinity key for Person objects
// to ensure that all persons are collocated with their organizations.
personCache.put(p1.key(), p1);
personCache.put(p2.key(), p2);
personCache.put(p3.key(), p3);
personCache.put(p4.key(), p4);
}
finally {
// Distributed cache could be removed from cluster only by #destroyCache() call.
ignite.destroyCache("Person");
ignite.destroyCache("Organization");
}
}
连接和并置
就像通过IgniteCache
API进行的SQL查询一样,在分区
缓存上进行的关联,只有在关联的对象以并置模式存储时才能正常运行,
跨缓存查询
驱动连接的缓存会被视为默认的模式,要跨越多个缓存进行查询,可以使用跨缓存查询功能。
复制和分区缓存
在复制
缓存上的查询只会在一个节点上执行,而在分区
缓存上的查询会在所有缓存节点上分布式地执行。
最后,就可以使用ODBC的API像使用普通数据库那样在数据网格中执行SQL查询:
C++:
#define BUFFER_SIZE 1024
SQLHENV env;
// Allocate an environment handle
SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
// We want ODBC 3 support
SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, reinterpret_cast<void*>(SQL_OV_ODBC3), 0);
SQLHDBC dbc;
// Allocate a connection handle
SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
// Connection string
SQLCHAR connectStr[] = "DRIVER={Apache Ignite};SERVER=localhost;PORT=11443;CACHE=Person;";
SQLSMALLINT connectStrLen = static_cast<SQLSMALLINT>(sizeof(connectStr));
SQLCHAR outStr[BUFFER_SIZE] = { 0 };
SQLSMALLINT outStrLen = static_cast<SQLSMALLINT>(sizeof(outStr));;
// Connecting to ODBC server.
SQLRETURN ret = SQLDriverConnect(dbc, NULL, connectStr, connectStrLen, outStr, outStrLen, &outStrLen, SQL_DRIVER_COMPLETE);
if (!SQL_SUCCEEDED(ret))
{
SQLCHAR sqlstate[7] = { 0 };
SQLINTEGER nativeCode;
SQLCHAR errMsg[BUFFER_SIZE] = { 0 };
SQLSMALLINT errMsgLen = static_cast<SQLSMALLINT>(sizeof(errMsg));
SQLGetDiagRec(SQL_HANDLE_DBC, dbc, 1, sqlstate, &nativeCode, errMsg, errMsgLen, &errMsgLen);
std::cerr << "Failed to connect to Apache Ignite: "
<< reinterpret_cast<char*>(sqlstate) << ": "
<< reinterpret_cast<char*>(errMsg) << ", "
<< "Native error code: " << nativeCode
<< std::endl;
// Releasing allocated handles.
SQLFreeHandle(SQL_HANDLE_DBC, dbc);
SQLFreeHandle(SQL_HANDLE_ENV, env);
return;
}
SQLHSTMT stmt;
// Allocate a statement handle
SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
SQLCHAR query[] = "SELECT name, salary, Organization.name FROM Person "
"INNER JOIN \"Organization\".Organization ON Person.orgId = Organization.id";
SQLSMALLINT queryLen = static_cast<SQLSMALLINT>(sizeof(queryLen));
ret = SQLExecDirect(stmt, query, queryLen);
if (!SQL_SUCCEEDED(ret))
{
SQLCHAR sqlstate[7] = { 0 };
SQLINTEGER nativeCode;
SQLCHAR errMsg[BUFFER_SIZE] = { 0 };
SQLSMALLINT errMsgLen = static_cast<SQLSMALLINT>(sizeof(errMsg));
SQLGetDiagRec(SQL_HANDLE_DBC, dbc, 1, sqlstate, &nativeCode, errMsg, errMsgLen, &errMsgLen);
std::cerr << "Failed to perfrom SQL query upon Apache Ignite: "
<< reinterpret_cast<char*>(sqlstate) << ": "
<< reinterpret_cast<char*>(errMsg) << ", "
<< "Native error code: " << nativeCode
<< std::endl;
}
else
{
// Printing results.
struct OdbcStringBuffer
{
SQLCHAR buffer[BUFFER_SIZE];
SQLLEN resLen;
};
// Getting number of columns in result set.
SQLSMALLINT columnsCnt = 0;
SQLNumResultCols(stmt, &columnsCnt);
// Allocating buffers for columns.
std::vector<OdbcStringBuffer> columns(columnsCnt);
// Binding colums. For simplicity we are going to use only
// string buffers here.
for (SQLSMALLINT i = 0; i < columnsCnt; ++i)
SQLBindCol(stmt, i + 1, SQL_CHAR, columns[i].buffer, BUFFER_SIZE, &columns[i].resLen);
// Fetching and printing data in a loop.
ret = SQLFetch(stmt);
while (SQL_SUCCEEDED(ret))
{
for (size_t i = 0; i < columns.size(); ++i)
std::cout << std::setw(16) << std::left << columns[i].buffer << " ";
std::cout << std::endl;
ret = SQLFetch(stmt);
}
}
// Releasing statement handle.
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
// Disconneting from the server.
SQLDisconnect(dbc);
// Releasing allocated handles.
SQLFreeHandle(SQL_HANDLE_DBC, dbc);
SQLFreeHandle(SQL_HANDLE_ENV, env);
ODBC接口一致性
ODBC定义了若干接口一致性级别,在本章中可以知道Ignite的ODBC驱动支持哪些特性。
特性 | Ignite是否支持 | 备注 |
---|---|---|
通过调用SQLAllocHandle和SQLFreeHandle来分配和释放所有处理器类型 | 是 | |
使用SQLFreeStmt函数的所有形式 | 是 | |
通过调用SQLBindCol,绑定列结果集 | 是 | |
通过调用SQLBindParameter和SQLNumParams,处理动态参数,包括参数数组,只针对输入方向, | 是 | |
指定绑定偏移量 | 是 | |
使用数据执行对话框,涉及SQLParamData和SQLPutData的调用 | 否 | 主要用于INSERT和UPDATE,Ignite不支持 |
管理游标和游标名 | 部分 | 实现了SQLCloseCursor,Ignite不支持命名游标 |
通过调用SQLColAttribute,SQLDescribeCol,SQLNumResultCols和SQLRowCount,访问结果集的描述(元数据) | 是 | |
通过调用目录函数SQLColumns,SQLGetTypeInfo,SQLStatistics和SQLStatistics查询数据字典 | 部分 | 不支持SQLStatistics |
通过调用SQLConnect,SQLDataSources,SQLDisconnect和SQLDriverConnect管理数据源和连接,通过SQLDrivers获取驱动的信息,不管支持ODBC那个级别。 | 是 | |
通过调用SQLExecDirect,SQLExecute和SQLPrepare预编译和执行SQL语句。 | 是 | |
通过调用SQLFetch,或者将FetchOrientation参数设置为SQL_FETCH_NEXT之后调用SQLFetchScroll,获取一个结果集或者多行数据中的一行,只能向前 | 是 | |
通过调用SQLGetData,获得一个未绑定的列 | 是 | |
通过调用SQLGetConnectAttr、SQLGetEnvAttr、SQLGetStmtAttr,获取所有属性的当前值,或者通过调用SQLSetConnectAttr、SQLSetEnvAttr、SQLSetStmtAttr,将所有属性赋为默认值,以及为特定属性赋为非默认值。 | 部分 | 并不支持所有属性 |
通过调用SQLCopyDesc、SQLGetDescField、SQLGetDescRec、SQLSetDescField、SQLSetDescRec,操作描述符的特定字段。 | 否 | |
通过调用SQLGetDiagField、SQLGetDiagRec,获得诊断信息。 | 是 | |
通过调用SQLGetFunctions和SQLGetInfo,检测驱动兼容性,以及,通过调用SQLNativeSql,在发送到数据源之前检测SQL语句中的任何文本代换的结果 | 部分 | 未实现SQLGetFunctions,SQLGetInfo实现了一部分,实现了SQLNativeSql |
使用SQLEndTran的语法提交一个事务,驱动的核心级别不需要支持真事务,因此,应用无法指定SQL_ROLLBACK或者为SQL_ATTR_AUTOCOMMIT连接属性指定SQL_AUTOCOMMIT_OFF | 是 | |
调用SQLCancel取消数据执行对话框,以及多线程环境中,在另一个线程中取消ODBC函数的执行,核心级别的接口一致性不需要支持函数的异步执行,也不需要使用SQLCancel取消一个ODBC函数的异步执行。平台和ODBC驱动都不需要多线程地同时自主活动,然而在多线程环境中,ODBC驱动必须是线程安全的,从应用来的请求的序列化是实现这个规范的一致的方式,即使他导致了一系列的性能问题。 | 否 | 当前的ODBC驱动实现不支持异步执行也不支持数据处理特性 |
通过调用SQLSpecialColumns获得表的行标识符SQL_BEST_ROWID。 | 是 | 当前的实现总是返回空 |
函数名 | Ignite是否支持 | 一致性级别 |
---|---|---|
SQLAllocHandle | 是 | Core |
SQLBindCol | 是 | Core |
SQLBindParameter | 是 | Core |
SQLBrowseConnect | 否 | Level1 |
SQLBulkOperations | 否 | Level1 |
SQLCancel | 否 | Core |
SQLCloseCursor | 是 | Core |
SQLColAttribute | 是 | Core |
SQLColumnPrivileges | 否 | Level2 |
SQLColumns | 是 | Core |
SQLConnect | 是 | Core |
SQLCopyDesc | 否 | Core |
SQLDataSources | N/A | Core |
SQLDescribeCol | 是 | Core |
SQLDescribeParam | 否 | Level2 |
SQLDisconnect | 是 | Core |
SQLDriverConnect | 是 | Core |
SQLDrivers | N/A | Core |
SQLEndTran | 部分 | Core |
SQLExecDirect | 是 | Core |
SQLExecute | 是 | Core |
SQLFetch | 是 | Core |
SQLFetchScroll | 是 | Core |
SQLForeignKeys | 部分 | Level2 |
SQLFreeHandle | 是 | Core |
SQLFreeStmt | 是 | Core |
SQLGetConnectAttr | 否 | Core |
SQLGetCursorName | 否 | Core |
SQLGetData | 是 | Core |
SQLGetDescField | 否 | Core |
SQLGetDescRec | 否 | Core |
SQLGetDiagField | 是 | Core |
SQLGetDiagRec | 是 | Core |
SQLGetEnvAttr | 是 | Core |
SQLGetFunctions | 否 | Core |
SQLGetInfo | 是 | Core |
SQLGetStmtAttr | 部分 | Core |
SQLGetTypeInfo | 是 | Core |
SQLMoreResults | 部分 | Level1 |
SQLNativeSql | 是 | Core |
SQLNumParams | 是 | Core |
SQLNumResultCols | 是 | Core |
SQLParamData | 否 | Core |
SQLPrepare | 是 | Core |
SQLPrimaryKeys | 是 | Level1 |
SQLProcedureColumns | 否 | Level1 |
SQLProcedures | 否 | Level1 |
SQLPutData | 否 | Core |
SQLRowCount | 是 | Core |
SQLSetConnectAttr | 否 | Core |
SQLSetCursorName | 否 | Core |
SQLSetDescField | 否 | Core |
SQLSetDescRec | 否 | Core |
SQLSetEnvAttr | 是 | Core |
SQLSetPos | 否 | Level1 |
SQLSetStmtAttr | 部分 | Core |
SQLSpecialColumns | 否 | Core |
SQLStatistics | 否 | Core |
SQLTablePrivileges | 否 | Level2 |
SQLTables | 是 | Core |