Phoenix
Phoenix的介绍
-
功能
-
专门基于Hbase所设计的SQL on Hbase 工具
-
使用Phoenix实现基于SQL操作Hbase
-
使用Phoenix自动构建二级索引并维护二级索引
-
-
原理
-
上层提供了SQL接口
- 底层全部通过Hbase Java API来实现,通过构建一系列的Scan和Put来实现数据的读写
-
功能非常丰富
- 底层封装了大量的内置的协处理器,可以实现各种复杂的处理需求,例如二级索引等
-
-
特点
- 优点
- 支持SQL接口
- 支持自动维护二级索引
- 缺点
- SQL支持的语法不全面
- Bug比较多
- Hive on Hbase对比
- Hive:SQL更加全面,但是不支持二级索引,底层通过分布式计算工具来实现
- Phoenix:SQL相对支持不全面,但是性能比较好,直接使用HbaseAPI,支持索引实现
- 优点
-
应用
-
Phoenix适用于任何需要使用SQL或者JDBC来快速的读写Hbase的场景
-
或者需要构建及维护二级索引场景
-
Phoenix的安装配置
- **需求**
-
http://phoenix.apache.org/
-
安装部署配置Phoenix,集成Hbase
-
分析
- step1:上传解压安装
- step2:修改配置,指定Hbase连接地址
- step3:启动Phoenix,连接Hbase
-
实现
-
下载:http://phoenix.apache.org/download.html
-
第一台机器上传
cd /export/software/ rz
-
第一台机器解压
tar -zxvf apache-phoenix-5.0.0-HBase-2.0-bin.tar.gz -C /export/server/ cd /export/server/ mv apache-phoenix-5.0.0-HBase-2.0-bin phoenix-5.0.0-HBase-2.0-bin
-
修改三台Linux文件句柄数
vim /etc/security/limits.conf #在文件的末尾添加以下内容,*号不能去掉 * soft nofile 65536 * hard nofile 131072 * soft nproc 2048 * hard nproc 4096
-
将Phoenix所有jar包分发到Hbase的lib目录下
#拷贝到第一台机器 cd /export/server/phoenix-5.0.0-HBase-2.0-bin/ cp phoenix-* /export/server/hbase-2.1.0/lib/ cd /export/server/hbase-2.1.0/lib/ #分发给第二台和第三台 scp phoenix-* node2:$PWD scp phoenix-* node3:$PWD
-
修改hbase-site.xml,添加一下属性
cd /export/server/hbase-2.1.0/conf/ vim hbase-site.xml
<!-- 关闭流检查,从2.x开始使用async --> <property> <name>hbase.unsafe.stream.capability.enforce</name> <value>false</value> </property> <!-- 支持HBase命名空间映射 --> <property> <name>phoenix.schema.isNamespaceMappingEnabled</name> <value>true</value> </property> <!-- 支持索引预写日志编码 --> <property> <name>hbase.regionserver.wal.codec</name> <value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value> </property> <!-- 配置NS映射 --> <property> <name>phoenix.schema.isNamespaceMappingEnabled</name> <value>true</value> </property>
-
同步给其他两台机器
scp hbase-site.xml node2:$PWD scp hbase-site.xml node3:$PWD
-
同步给Phoenix
cp hbase-site.xml /export/server/phoenix-5.0.0-HBase-2.0-bin/bin/
-
重启Hbase
stop-hbase.sh start-hbase.sh
-
启动Phoenix
cd /export/server/phoenix-5.0.0-HBase-2.0-bin/ bin/sqlline.py node1:2181
-
测试
!tables
-
-
总结
- 解压安装
- 修改配置
- 启动服务
- 测试环境
Phoenix的语法:DDL:NS
-
http://phoenix.apache.org/language/index.html
-
需求
- 实现基于SQL的数据库管理:创建、切换、删除
-
分析
- step1:创建Namespace
- step2:切换Namespace
- step3:删除Namespace
-
实现
-
创建NS
create schema if not exists student;
-
切换NS
use student;
-
删除NS
drop schema if exists student;
-
-
总结
- 基本与SQL语法一致
- 注意:Phoenix中默认会将所有字符转换为大写,如果想要使用小写字母,必须加上双引号
Phoenix的语法:DDL:Table
-
需求
- 实现基于SQL的数据表管理:创建、列举、查看、删除
-
分析
- step1:列举当前所有的表
- step2:创建表
- step3:查询表信息
- step4:删除表
-
实现
-
列举
!tables
-
创建
-
语法:http://phoenix.apache.org/language/index.html#create_table
CREATE TABLE my_schema.my_table ( id BIGINT not null primary key, date Date ); CREATE TABLE my_table ( id INTEGER not null primary key desc, m.date DATE not null, m.db_utilization DECIMAL, i.db_utilization ) m.VERSIONS='3'; CREATE TABLE stats.prod_metrics ( host char(50) not null, created_date date not null, txn_count bigint CONSTRAINT pk PRIMARY KEY (host, created_date) ); CREATE TABLE IF NOT EXISTS "my_case_sensitive_table"( "id" char(10) not null primary key, "value" integer ) DATA_BLOCK_ENCODING='NONE',VERSIONS=5,MAX_FILESIZE=2000000 split on (?, ?, ?); CREATE TABLE IF NOT EXISTS my_schema.my_table ( org_id CHAR(15), entity_id CHAR(15), payload binary(1000), CONSTRAINT pk PRIMARY KEY (org_id, entity_id) ) TTL=86400
-
-
-
如果Hbase中没有这个表
use default; create table if not exists ORDER_DTL( ID varchar primary key, C1.STATUS varchar, C1.PAY_MONEY float, C1.PAYWAY integer, C1.USER_ID varchar, C1.OPERATION_DATE varchar, C1.CATEGORY varchar );
-
如果Hbase中已存在会自动关联
create table if not exists ORDER_INFO( "ROW" varchar primary key, "C1"."USER_ID" varchar, "C1"."OPERATION_DATE" varchar, "C1"."PAYWAY" varchar, "C1"."PAY_MONEY" varchar, "C1"."STATUS" varchar, "C1"."CATEGORY" varchar ) column_encoded_bytes=0 ;
-
查看
!desc order_info;
-
删除
drop table if exists order_dtl;
-
-
总结
-
创建表时,必须指定主键作为Rowkey,主键列不能加列族
create table if not exists ORDER_INFO( --不能这么写 "C1"."ROW" varchar primary key, "C1"."USER_ID" varchar, "C1"."OPERATION_DATE" varchar, "C1"."PAYWAY" varchar, "C1"."PAY_MONEY" varchar, "C1"."STATUS" varchar, "C1"."CATEGORY" varchar ) column_encoded_bytes=0 ;
- Phoenix 4.8版本之前只要创建同名的Hbase表,会自动关联数据 - Phoenix 4.8版本以后,不推荐关联表的方式 - 推荐使用视图关联的方式来实现,如果你要使用关联表的方式,必须加上以下参数
column_encoded_bytes=0 ; ```
-
如果关联已存在的表,Rowkey字段叫做ROW,使用时必须加上双引号
select "ROW","C1".USER_ID,"C1"."PAYWAY" from ORDER_INFO;
-
Phoenix的语法:DML:upsert
列名 | 数值 | 描述 |
---|---|---|
Rowkey | 02602f66-adc7-40d4-8485-76b5632b5b53 | 行健,编码生成 |
USER_ID | 4944191 | 用户id |
OPERATION_DATE | 2020-04-25 12:09:16 | 操作时间 |
PAYWAY | 1 | 支付方式 |
PAY_MONEY | 4070 | 支付金额 |
STATUS | 已提交 | 提交状态 |
CATEGORY | 手机; | 分类 |
-
需求
- 基于order_info订单数据实现DML插入数据
-
分析
-
Phoenix中插入更新的命令为:upsert
- 功能:insert + update
- MySQL:replace
- 如果存在就更新,如果不存在就插入
- 功能:insert + update
-
语法及示例
UPSERT INTO TEST VALUES('foo','bar',3); UPSERT INTO TEST(NAME,ID) VALUES('foo',123); UPSERT INTO TEST(ID, COUNTER) VALUES(123, 0) ON DUPLICATE KEY UPDATE COUNTER = COUNTER + 1; UPSERT INTO TEST(ID, MY_COL) VALUES(123, 0) ON DUPLICATE KEY IGNORE;
-
-
实现
-
插入一条数据
upsert into order_info values('z8f3ca6f-2f5c-44fd-9755-1792de183845','4944191','2020-04-25 12:09:16','1','4070','未提交','电脑');
-
更新USERID为123456
upsert into order_info("ROW","USER_ID") values('z8f3ca6f-2f5c-44fd-9755-1792de183845','123456');
-
-
总结
-
语法类似于insert语法
-
功能:insert + update
-
Phoenix的语法:DML:delete
-
需求
- 基于order_info订单数据实现DML删除数据
-
分析
-
Phoenix中插入更新的命令为:delete
-
语法及示例
DELETE FROM TEST; DELETE FROM TEST WHERE ID=123; DELETE FROM TEST WHERE NAME LIKE 'foo%';
-
-
实现
-
删除USER_ID为123456的rowkey数据
delete from order_info where USER_ID = '123456';
-
-
总结
- 与MySQL是一致的
## Phoenix的语法:DQL:select
-
需求
- 基于order_info订单数据实现DQL查询数据
-
分析
-
Phoenix中插入更新的命令为:select
-
语法及示例
SELECT * FROM TEST LIMIT 1000; SELECT * FROM TEST LIMIT 1000 OFFSET 100; SELECT full_name FROM SALES_PERSON WHERE ranking >= 5.0 UNION ALL SELECT reviewer_name FROM CUSTOMER_REVIEW WHERE score >= 8.0
-
-
实现
-
查询支付方式为1的数据
select "ROW",payway,pay_money,category from order_info where payway = '1';
-
查询每种支付方式对应的用户人数,并且按照用户人数降序排序
- 分组:每、各个、不同
- 排序:用户人数
select payway, count(distinct user_id) as numb from order_info group by payway order by numb desc;
-
查询数据的第60行到66行
--以前的写法:limit M,N --M:开始位置 --N:显示的条数 --Phoenix的写法:limit N offset M select * from order_info limit 6 offset 60;//总共66行,显示最后6行
-
函数支持
- http://phoenix.apache.org/language/functions.html
-
-
总结
- 基本查询与MySQL也是一致的
- 写的时候注意数据类型以及大小写的问题即可
- 如果遇到SQL报错,检查语法是否支持
Phoenix的使用:预分区
需求
-
Hbase命令建表
create Ns;tbname,列族,预分区
-
- 创建表的时候,需要根据Rowkey来设计多个分区
-
分析
-
Phoenix也提供了创建表时,指定分区范围的语法
CREATE TABLE IF NOT EXISTS "my_case_sensitive_table"( "id" char(10) not null primary key, "value" integer ) DATA_BLOCK_ENCODING='NONE',VERSIONS=5,MAX_FILESIZE=2000000 split on (?, ?, ?)
实现
-
-
创建数据表,四个分区
drop table if exists ORDER_DTL; create table if not exists ORDER_DTL( "id" varchar primary key, C1."status" varchar, C1."money" float, C1."pay_way" integer, C1."user_id" varchar, C1."operation_time" varchar, C1."category" varchar ) CONPRESSION='GZ' SPLIT ON ('3','5','7');
-
-
总结
-
实现效果与命令实现的效果一致
-
通过SQL建表语句实现
create table() split
-
Phoenix的使用:加盐salt
- **需求**
-
Rowkey设计的时候为了避免连续,构建Rowkey的散列,如果rowkey设计是连续的,怎么解决?
-
分析
-
在Phoenix创建一张盐表,写入的数据会自动进行编码写入不同的分区中
CREATE TABLE table ( a_key VARCHAR PRIMARY KEY, a_col VARCHAR ) SALT_BUCKETS = 20;
-
-
实现
-
创建一张盐表,指定分区个数为10
drop table if exists ORDER_DTL; create table if not exists ORDER_DTL( "id" varchar primary key, C1."status" varchar, C1."money" float, C1."pay_way" integer, C1."user_id" varchar, C1."operation_time" varchar, C1."category" varchar ) CONPRESSION='GZ', SALT_BUCKETS=10;
-
总结
- 由Phoenix来实现自动编码,解决Rowkey的热点问题,不需要自己设计散列的Rowkey
Phoenix的使用:视图
-
需求
- 直接关联Hbase中的表,会导致误删除,对数据的权限会有影响,容易出现问题,如何避免?
-
分析
- Phoenix中建议使用视图的方式来关联Hbase中已有的表
- 通过构建关联视图,可以解决大部分数据查询的数据,不影响数据
- 视图:理解为只读的表
实现
-
创建视图,关联Hbase中已经存在的表
create view if not exists "MOMO_CHAT"."MSG" ( "pk" varchar primary key, -- 指定ROWKEY映射到主键 "C1"."msg_time" varchar, );
-
-
总结
- 工作中主要构建的都是视图
- MySQL:视图
- Hive:外部表
- Phoenix:视图
Phoenix的使用:JDBC
-
需求
- 工作中实际使用SQL,会基于程序中使用JDBC的方式来提交SQL语句,在Phoenix中如何实现?
-
分析
-
Phoenix支持使用JDBC的方式来提交SQL语句
-
例如:聊天分析案例中需求:查询条件为日期【年-月-日】 + 发送人ID + 接受人ID
select * from "MOMO_CHAT"."MSG" where substr("msg_time",0,10) = '2021-03-22' and "sender_account" = '17351912952' and "receiver_account" = '17742251415';
-
可以在代码中基于JDBC来提交SQL查询
-
-
实现
-
构建JDBC连接Phoenix
package cn.itcast.momo_chat.service.impl; import cn.itcast.momo_chat.entity.Msg; import cn.itcast.momo_chat.service.ChatMessageService; import org.apache.phoenix.jdbc.PhoenixDriver; import java.sql.*; import java.util.ArrayList; import java.util.List; /** * @ClassName PhoenixChatMessageService * @Description TODO JDBC连接Phoenix实现数据查询 * @Create By Frank */ public class PhoenixChatMessageService implements ChatMessageService { private Connection connection; public PhoenixChatMessageService() throws ClassNotFoundException, SQLException { try { //申明驱动类 Class.forName(PhoenixDriver.class.getName()); // System.out.println(PhoenixDriver.class.getName()); //构建连接 connection = DriverManager.getConnection("jdbc:phoenix:node1,node2,node3:2181"); } catch (ClassNotFoundException e) { throw new RuntimeException("加载Phoenix驱动失败!"); } catch (SQLException e) { throw new RuntimeException("获取Phoenix JDBC连接失败!"); } } @Override public List<Msg> getMessage(String date, String sender, String receiver) throws Exception { PreparedStatement ps = connection.prepareStatement( "SELECT * FROM MOMO_CHAT.MSG T WHERE substr(\"msg_time\", 0, 10) = ? " + "AND T.\"sender_account\" = ? " + "AND T.\"receiver_account\" = ? "); ps.setString(1, date); ps.setString(2, sender); ps.setString(3, receiver); ResultSet rs = ps.executeQuery(); List<Msg> msgList = new ArrayList<>(); while(rs.next()) { Msg msg = new Msg(); msg.setMsg_time(rs.getString("msg_time")); msg.setSender_nickyname(rs.getString("sender_nickyname")); msg.setSender_account(rs.getString("sender_account")); msg.setSender_sex(rs.getString("sender_sex")); msg.setSender_ip(rs.getString("sender_ip")); msg.setSender_os(rs.getString("sender_os")); msg.setSender_phone_type(rs.getString("sender_phone_type")); msg.setSender_network(rs.getString("sender_network")); msg.setSender_gps(rs.getString("sender_gps")); msg.setReceiver_nickyname(rs.getString("receiver_nickyname")); msg.setReceiver_ip(rs.getString("receiver_ip")); msg.setReceiver_account(rs.getString("receiver_account")); msg.setReceiver_os(rs.getString("receiver_os")); msg.setReceiver_phone_type(rs.getString("receiver_phone_type")); msg.setReceiver_network(rs.getString("receiver_network")); msg.setReceiver_gps(rs.getString("receiver_gps")); msg.setReceiver_sex(rs.getString("receiver_sex")); msg.setMsg_type(rs.getString("msg_type")); msg.setDistance(rs.getString("distance")); msgList.add(msg); } return msgList; } @Override public void close() { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } public static void main(String[] args) throws Exception { ChatMessageService chatMessageService = new PhoenixChatMessageService(); List<Msg> message = chatMessageService.getMessage("2021-03-22", "17351912952", "17742251415"); for (Msg msg : message) { System.out.println(msg); } chatMessageService.close(); } }
-
运行查看结果
-
-
总结
- Phoenix支持SQL
- 支持JDBC方式提交SQL语句实现数据处理
Phoenix二级索引设计
-
目标
- 基于Phoenix构建Hbase二级索引并维护二级索引
-
分析
- 为什么需要构建二级索引?
- 因为Hbase使用Rowkey作为唯一索引,无法满足大部分的查询走索引,导致性能较差
- 构建二级索引:通过走两次索引代替全表扫描
- Phoenix如何实现二级索引?
- Phoenix底层构建了大量的协处理器,来实现二级索引的构建
- 为什么需要构建二级索引?
-
实现
-
step1:根据数据存储需求,创建原始数据表,将数据写入原始数据表
rowkey:id name age
-
step2:根据数据查询需求,构建二级索引,Phoenix自动创建索引表
create index indexName on tbName(colName);
rowkey:name_id
-
step3:查询数据时,Phoenix根据过滤条件是否存在二级索引,优先判断走二级索引代替全表扫描
-
step4:原始数据表发生数据变化时,Phoenix会自动更新索引表的数据
-
-
总结
- 在Phoenix中二级索引实现完全由Phoenix自主实现
- 不需要关心底层的实现,只需要使用语法创建索引即可
- http://phoenix.apache.org/secondary_indexing.html
二级索引:全局索引设计
-
目标
- 了解二级索引中全局索引的设计思想
-
分析
- 什么是全局索引?
- 当为某一列创建全局索引时,Phoenix自动创建一张索引表,将创建索引的这一列加上原表的rowkey作为新的rowkey
- 什么是全局索引?
-
实现
-
原始数据表
rowkey:id name age
-
需求:根据name进行数据查询
-
创建全局索引
create index index01 on tbname(name);
-
自动构建索引表
rowkey:name_id col:占位值
-
查询
- 先查询索引表:通过rowkey获取名称对应的id
- 再查询数据表:通过id查询对应的数据
-
-
总结
-
**特点:**默认只能对构建索引的字段做索引查询,如果查询中包含了不是索引的字段或者条件不是索引字段,不走索引
-
走索引
select name from table select name from table where name = value
-
不走索引
select * from table where name = value
-
-
**应用:**写少读多
- 当原表的数据发生更新操作提交时,会被拦截
- 先更新所有索引表,然后再更新原表
-
二级索引:全局索引实现
-
目标
- 基于Phoenix实现全局索引的测试
-
分析
- step1:先创建原始数据表
- step2:基于查询条件创建索引
- step3:实现索引查询
-
实现
-
基于user_id构建全局索引
create index GBL_IDX_ORDER_DTL on ORDER_DTL(C1."user_id");
查看索引表
!tables
查询数据及查询计划
select "user_id" from ORDER_DTL where "user_id" = '8237476';
explain select "user_id" from ORDER_DTL where "user_id" = '8237476';
-
强制走索引
explain select /*+ INDEX(ORDER_DTL GBL_IDX_ORDER_DTL) */ * from ORDER_DTL where "user_id" = '8237476';
-
删除索引
drop index GBL_IDX_ORDER_DTL on ORDER_DTL;
总结
-
全局索引是最常用的基础二级索引类型
-
索引表结构
- rowkey:查询条件字段 + 原表rowkey
-
应用:适合于读多写少的场景
-
特点:如果查询条件或者查询内容中包含非索引字段,将不走索引,可以强制走索引
二级索引:覆盖索引设计
-
目标
- 了解二级索引中覆盖索引的设计思想
-
分析
- 什么是覆盖索引?
- 在构建全局索引时,将经常作为查询条件的列放入索引表中,直接通过索引表来返回数据结果
- 什么是覆盖索引?
-
实现
-
原始数据表
rowkey:id name age addr phone
-
需求:根据name进行数据查询
-
创建全局索引
create index index01 on tbname(name);
-
自动构建索引表
rowkey:name_id col:占位值
-
如果需求发生改变,查询name和age,上面的全局索引会失效
-
创建全局+覆盖:include(age)
create index index01 on tbname(name) include(age);
-
自动构建索引表
rowkey:name_id col:age
select name from table; select name from table where age = 20 select name , age from table
-
-
总结
- **特点:**基于全局索引构建,将常用的查询结果放入索引表中,直接从索引表返回结果
- **应用:**适合于查询条件比较固定,数据量比较小的场景下
- 注意:不建议将大部分列都放入覆盖索引,导致索引表过大,性能降低
二级索引:覆盖索引实现
-
目标
- 基于Phoenix实现覆盖索引的测试
-
分析
- step1:先创建原始数据表
- step2:基于查询条件创建覆盖索引
- step3:实现索引查询
-
实现
-
基于user_id构建全局索引,运行通过user_id查询订单id和支付金额
create index GBL_IDX_ORDER_DTL on ORDER_DTL(C1."user_id") INCLUDE("id", C1."money");
-
查看索引表
!tables
-
查询数据
select "user_id", "id", "money" from ORDER_DTL where "user_id" = '8237476';
-
查看执行计划
explain select "user_id", "id", "money" from ORDER_DTL where "user_id" = '8237476';
-
- 使用HINT强制走索引
explain select /*+ INDEX(ORDER_DTL GBL_IDX_ORDER_DTL) */ * from ORDER_DTL where "user_id" = '8237476';
-
删除索引
drop index GBL_IDX_ORDER_DTL on ORDER_DTL;
-
总结
-
覆盖索引是基于全局索引实现的
-
目的是将常用的查询结果放入索引表中,直接从索引表返回数据
二级索引:本地索引设计
-
目标
- 了解二级索引中本地索引的设计思想
-
分析
- 什么是本地索引?
- 将索引数据与对应的原始数据放在同一台机器,避免了跨网络传输,提高了写的性能
- 什么是本地索引?
-
实现
- 构建索引数据时,将索引数据直接存储在原表中,用一个列族来实现
-
总结
- 特点
- 即使查询数据中包含了非索引字段,也会走本地索引
- 本地索引会修改原始数据
- 如果构建了本地索引,不能通过Hbase的API来读写数据的,必须通过Phoenix来实现读写
- 本地索引对盐表不生效的
- 应用:写多读少
- 提高构建索引时对写的性能的影响
- 最终所有索引都是为了提高读的性能的
- 特点
二级索引:本地索引实现
-
目标
- 基于Phoenix实现本地索引的测试
-
分析
- step1:先创建原始数据表
- step2:基于查询条件创建本地索引
- step3:实现索引查询
-
实现
-
关闭Hbase及Phoenix
-
修改hbase-site.xml中Zookeeper的连接,所有Hbase节点及Phoenix的bin目录下
<property> <name>hbase.zookeeper.quorum</name> <value>node1,node2,node3:2181</value> </property>
-
重启Hbase和Phoenix
-
创建本地索引
create local index LOCAL_IDX_ORDER_DTL on ORDER_DTL("id", "status", "money", "pay_way", "user_id") ;
-
-
删除索引
drop index LOCAL_IDX_ORDER_DTL on ORDER_DTL;
总结
- 本地索引设计:将索引与数据存储在同一台机器,提高读写的性能,通过建列族来实现的
- 本地索引不局限于是否对列构建索引,优先走本地索引实现数据查询
二级索引:函数索引【了解】
-
目标
- 了解Hbase中的函数索引的概念
-
分析
- 本地、覆盖、全局:都是基于查询的列构建索引
- 什么是函数索引?
- 可以基于一个函数表达式去构建索引
-
实现
-
查询:过滤的条件是一个函数
SELECT EMP_ID FROM EMP WHERE UPPER(FIRST_NAME||' '||LAST_NAME)='JOHN DOE'
-
构建索引
CREATE INDEX UPPER_NAME_IDX ON EMP (UPPER(FIRST_NAME||' '||LAST_NAME))
-
Hive中
-
SQL:语法
-
函数
-
字符串
- substr
-
replace_regex
-
concat/concat_ws
- concat:直接拼接每个字符串,只有有1列为null,整个结果为null
concat(str1,str2,str3……)
-
concat_ws:可以指定分隔符,只要有1列不为null,整个结果不为null
concat(分隔符,str1,str2,str3……)
-
instr
-
……
-
-
日期
- 转换类:from_unix,unix_timestamp
- 获取:year、ceil、quator、month、day、hour
-
条件
- if
- case when
-
JSON处理:json_tuple,get_json_object
-
窗口函数:重点:row_number,rank,dense_rank,lead,lag,sum
-
URL解析:parse_url_tuple
-
-
-
-
总结
- 函数索引就是基于函数表达式创建索引,当执行相同的函数表达式查询时,可以通过索引实现快速查询
- 一般不用