早之前曾经写过过一篇文章 总结 adb 的常用命令,这次刷机也复习了一些命令,这里再补充一下刷机中必不可少的 fastboot 命令。
就像之前 adb 中说过的那样, adb 用来向开机的 Android 设备发送指令,而 fastboot 则是向开机的 Android bootloader 来发送指令,因此在刷机过程中,如果手机重启进入 bootloader 基本之后使用的 命令都离不开 fastboot。比如在安装第三方 recovery 时,要用到 fastboot flash recovery recovery.img
这样的指令。虽然不能依靠此命令来刷入 ROM,但是 fastboot 也能够做一些 adb 命令无法实现的功能。
安装
sudo apt-get install android-tools-fastboot
常用的命令如下:
adb devices
adb reboot bootloader
fastboot devices
fastboot oem device-info
结果:
❯ fastboot oem device-info
...
(bootloader) Verity mode: true
(bootloader) Device unlocked: true
(bootloader) Device critical unlocked: true
(bootloader) Charger screen enabled: false
OKAY [ 0.006s]
finished. total time: 0.007s
关于 bootloader 部分可以参考 之前的文章
adb 和 fastboot 两个工具都是跟随着 Android SDK 分发的,但是由于这两个工具需求过高,Google 单独提供了两个工具集的下载。
在下载之后,需要将文件路径添加到系统环境变量中,以便于在各个路径下访问。
假设加压后的路径为 ~/Android/Sdk/platform-tools/
, 那需要在 ~/.bashrc 或者 ~/.zshrc 中配置。
export PATH="$PATH:/home/einverne/Android/Sdk/platform-tools"
WIndows 同理,在系统配置环境变量。
然后在终端下使用 adb devices
,就能够查看连接的设备。同样在 bootloader 下,也可以使用 fastboot devices
来查看连接设备,如果未出现已经 USB 连接的设备,查看驱动是否安装完好即可。
fastboot devices
用当前目录下的 boot.img 启动手机,在手机 boot 分区损坏的情况下可以用这个正常进入系统
fastboot boot boot.img
同理可以使用
fastboot boot recovery.img
来启动 recovery 模式
和手机上现有的系统完全无关,只要 PC 的 boot.img 或者 recovery.img 是可以正常工作的就可以。
刷机的人经常会遇到下面一些场景。
将当前目录下的 boot.img 刷入手机的 boot 分区:
fastboot flash boot boot.img
将当前目录下的 recovery.img 刷入系统的 recovery 分区:
fastboot flash recovery recovery.img
将当前目录下的 system.img 刷入系统的 system 分区:
fastboot flash system system.img
同理,刷入 data 分区
fastboot flash userdata userdata.img
清理分区的命令,慎用!!!
fastboot erase system
fastboot erase cache
fastboot erase config
fastboot erase data
fastboot erase logs
fastboot erase factory
最后重启手机
fastboot reboot
本文介绍 Android 反编译工具,只介绍工具名字及工具简单使用,详细开来再具体讲吧。本文主要包含工具的作用,工作的简单用法,以及反编译的基本步骤。
反编译 Android APK 主要需要依靠如下几个工具:
apktool
:A tool for reverse engineering Android apk files 查看 APK 包中的 AndroidManifest.xml
等 XML 资源文件
dex2jar
:Tools to work with android .dex and java .class files 将 APK 包中的 Dalvik 字节码文件(.dex)转换为 .jar 文件JD-GUI
:Java Decompiler is a tools to decompile and analyze Java 5 “byte code” and the later versions 查看 .jar 文件的 Java 源码
相关项目及工具地址后文贴出。
Apktook 是一个反编译(reverse engineering) 工具,可以用来反编译Android APK。几乎可以将APK中的 resources.arsc
, classes.dex
和 9.png
以及 XMLs 等等源文件反编译得到。
安装需要:
地址: https://ibotpeaches.github.io/Apktool/
各平台的安装指南,如果使用 Linux 可以使用如下简易步骤:
apktool
,并移动到 /usr/local/bin/
目录下ia32-libs
,使用 apt search ia32
,然后 apt install ia32-libs
安装,可跳过apktool.jar
apktool.jar
和 apktool
移动到 /usr/local/bin/
目录中,需要 root 权限chmod +x /usr/local/bin/apktool
apktool
apktool d xxx.apk
Apktool 其实还可以用来做另外一件事情,就是汉化,或者将语言包替换之后,重新打包,此时需要使用 apktool b xxx.apk
来重新打包 APK。
dex2jar 能够将 dex 转换为 jar
地址:https://github.com/pxb1988/dex2jar
JD GUI 用来反编译源文件jar包,查看源代码
总的来说 apktool 可以让你轻松的拿到应用的资源文件,包括图片,xml等,而 dex2jar 和 JD GUI 可以反编译源代码,看到一些基础的代码结构。
后来 Google 搞了一个 ClassyShark,看起来不错,不过还没来得及尝试
还有一个 不错的 smali 查看插件看起来也不错,还未尝试
https://github.com/JesusFreke/smali
另外有一个 MAC 专属的一键反编译工具,可以一试:
https://github.com/Jermic/Android-Crack-Tool
又一个查看 dex 到 jar 的反编译工具:
我们都知道 HBase 的基本结构由 rowkey,column, timestamp 组成。列存储数据不同于关系型数据库,MySQL 一旦建表,需要修改表结构时则需要执行对应的修改语句,而 HBase 在建完表之后,对于列的增加则不需要修改建表语句,但这并不意味着 HBase 的建表就可以随意建。
首先来看看 HBase 的结构
通过以上 Row, Column Family, Column Qualifier, TimeStamp 可以唯一确定一个基本存储单元 Cell。
并没有关系型数据库到列存储数据库一对一的设计迁移,在关系型数据库中,设计的重点是描述实体和其他实体之间的关系,查询和索引可以在之后设计。
而在 HBase 中,遵循着查询优先的设计模式,所有可能的查询都需要在设计中体现,因此 Schema 的设计方式也因此而来。考虑查询的方式,然后设计 Schema,这样可以使得数据能够快速被查询到。另外需要注意 HBase 被设计用来在大数据集群中使用,当预计数据量能够到达一个层级的时候再考虑使用。
rowkey 是 Table 的”主键”,设计时需要确保唯一性,数据按照 rowkey 大小顺序存储,rowkey 不是 schema 的一部分,但需要仔细设计:
[startKey, endKey)
的顺序读,吞吐量很高。rowKey 设计热点问题,rowKey 要尽量随机,不要出现连续 rowKey。
建议 HBase 设计为高表,不宜使用过多列族。
列和列族名字不宜太长,HBase 会在内存中对列和列族做索引和缓存。
我们知道如果使用 Nginx 可以使用 Virtual HOST 来 HOST 多个域名下的网站到同一台机器,那么如果使用 Docker 架设了一个 WordPress,还想用 Docker 架设一个新的网站,那么该怎么办呢?
有一种解决办法就是使用 Nginx 转发请求,比如一个网站监听了 81 端口,一个网站监听了 82 端口,那么使用 Nginx 的代理功能,将对应的流量转发给对应的服务器处理即可。因此 nginx-proxy 这个镜像的作用就如上面所述,让我们将不同的流量转发给不同的 Docker 容器进行处理。
有两种方式可以启动 nginx-proxy
容器,一种是通过 docker 命令,另一种是使用 docker-compose
。不过用这两种方式之前,先创建一个 Docker network,将多个容器关联起来
docker network create nginx-proxy
然后创建容器
docker run -d -p 80:80 --name nginx-proxy --net nginx-proxy -v /var/run/docker.sock:/tmp/docker.sock jwilder/nginx-proxy
或者创建 docker-compose.yml
:
version: "3"
services:
nginx-proxy:
image: jwilder/nginx-proxy
container_name: nginx-proxy
ports:
- "80:80"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
networks:
default:
external:
name: nginx-proxy
然后在同目录下 docker-compose up -d
Nginx Proxy Manager 的管理面板在 81 端口,默认的用户名和密码是 admin@example.com
和 changeme
。
nginx-proxy 对外暴露 80 端口,并且监听 80 端口,允许 80 端口的流量流入。而 /var/run/docker.sock:/tmp/docker.sock
这一行则表示着允许该容器访问宿主机器的 Docker socket,这也就意味着有新容器加入,或者新容器关闭时都会通知到 nginx-proxy。
这样每一次添加容器,nginx-proxy 就会通过 socket 来接收到事件,自动创建对应的配置文件,然后重启 nginx 来生效。nginx-proxy
会寻找带有 VIRTUAL_HOST
环境变量的容器,然后依照配置进行。
另外 --net nginx-proxy
和 docker compose 中 networks 块的配置,让所有的容器通过 Docker network 进行通讯。
比如增加一个 WordPress 容器
docker run -d --name blog --expose 80 --net nginx-proxy -e VIRTUAL_HOST=blog.DOMAIN.TLD wordpress
--expose 80
会允许流量从 80 端口流入--net nginx-proxy
保证 Docker 使用同一个网络-e VIRTUAL_HOST=blog.DOMAIN.TLD
开启 nginx-proxy
创建对应的配置文件将流量转发给 WordPress 容器如果使用 Docker compose
version: "3"
services:
db_node_domain:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: PASSWORD
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: PASSWORD
container_name: wordpress_db
wordpress:
depends_on:
- db_node_domain
image: wordpress:latest
expose:
- 80
restart: always
environment:
VIRTUAL_HOST: blog.DOMAIN.TLD
WORDPRESS_DB_HOST: db_node_domain:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: PASSWORD
container_name: wordpress
volumes:
db_data:
networks:
default:
external:
name: nginx-proxy
不过这里需要注意的是会创建一个数据库容器,一个 WordPress app 容器。
如果想要支持 SSL,那么 nginx-proxy
有一个对应的项目 letsencrypt-nginx-proxy-companion,他可以自动创建和续签 Let’s Encrypt 的证书。
Busybox 将很多 UNIX 下的工具集打包到一个可执行文件中,特别适合小容量的嵌入式设备,Android 等等。最近也是因为在 Android 用到才接触到。
实际的命令列表按编译时的设置决定,在有 Busybox 的系统上执行 busybox --list
即可看到一个完整的列表。
127|:/ $ busybox
BusyBox v1.30.1-osm0sis (2019-02-28 18:48:08 AST) multi-call binary.BusyBox is copyrighted by many authors between 1998-2015.
Licensed under GPLv2. See source distribution for detailed
copyright notices.
Usage: busybox [function [arguments]...]
or: busybox --list[-full]
or: busybox --show SCRIPT
or: busybox --install [-s] [DIR]
or: function [arguments]...
BusyBox is a multi-call binary that combines many common Unix
utilities into a single executable. Most people will create a
link to busybox for each function they wish to use and BusyBox
will act like whatever it was invoked as.
Currently defined functions:
[, [[, acpid, adjtimex, ar, arch, arp, arping, ash, awk,
base64, basename, bbconfig, beep, blkdiscard, blkid,
blockdev, brctl, bunzip2, bzcat, bzip2, cal, cat, chat,
chattr, chgrp, chmod, chown, chroot, chrt, chvt, cksum,
clear, cmp, comm, conspy, cp, cpio, crond, crontab,
cttyhack, cut, date, dc, dd, deallocvt, depmod, devmem, df,
dhcprelay, diff, dirname, dmesg, dnsd, dnsdomainname,
dos2unix, du, dumpkmap, dumpleases, echo, ed, egrep, eject,
env, ether-wake, expand, expr, factor, fakeidentd, false,
fatattr, fbset, fbsplash, fdflush, fdformat, fdisk,
fgconsole, fgrep, find, findfs, flock, fold, free,
freeramdisk, fsck, fsck.minix, fsfreeze, fstrim, fsync,
ftpd, ftpget, ftpput, fuser, getopt, grep, groups, gunzip,
gzip, hd, hdparm, head, hexdump, hexedit, hostname, httpd,
hush, hwclock, id, ifconfig, ifdown, ifenslave, ifplugd,
ifup, inetd, inotifyd, insmod, install, ionice, iostat, ip,
ipaddr, ipcalc, ipcrm, ipcs, iplink, ipneigh, iproute,
iprule, iptunnel, kbd_mode, kill, killall, killall5, klogd,
less, link, ln, loadfont, loadkmap, logread, losetup, ls,
lsattr, lsmod, lsof, lspci, lsscsi, lsusb, lzcat, lzma,
lzop, lzopcat, makedevs, makemime, man, md5sum, mesg,
microcom, mkdir, mkdosfs, mke2fs, mkfifo, mkfs.ext2,
mkfs.minix, mkfs.reiser, mkfs.vfat, mknod, mkswap, mktemp,
modinfo, modprobe, more, mount, mountpoint, mpstat, mv,
nameif, nanddump, nandwrite, nbd-client, nc, netstat, nice,
nl, nmeter, nohup, nologin, nslookup, nuke, od, openvt,
partprobe, paste, patch, pgrep, pidof, ping, ping6,
pipe_progress, pivot_root, pkill, pmap, popmaildir,
poweroff, powertop, printenv, printf, ps, pscan, pstree,
pwd, pwdx, raidautorun, rdate, rdev, readlink, readprofile,
realpath, reboot, reformime, renice, reset, resize, resume,
rev, rfkill, rm, rmdir, rmmod, route, rtcwake, run-init,
run-parts, rx, script, scriptreplay, sed, sendmail, seq,
setconsole, setfattr, setfont, setkeycodes, setlogcons,
setpriv, setserial, setsid, setuidgid, sh, sha1sum,
sha256sum, sha3sum, sha512sum, showkey, shred, shuf,
slattach, sleep, smemcap, sort, split, ssl_client,
start-stop-daemon, stat, strings, stty, sum, svc, svok,
swapoff, swapon, switch_root, sync, sysctl, syslogd, tac,
tail, tar, tc, tcpsvd, tee, telnet, telnetd, test, tftp,
tftpd, time, timeout, top, touch, tr, traceroute,
traceroute6, true, truncate, tty, ttysize, tunctl, tune2fs,
ubiattach, ubidetach, ubimkvol, ubirename, ubirmvol,
ubirsvol, ubiupdatevol, udhcpc, udhcpc6, udhcpd, udpsvd,
uevent, umount, uname, uncompress, unexpand, uniq,
unix2dos, unlink, unlzma, unlzop, unxz, unzip, uptime,
usleep, uudecode, uuencode, vconfig, vi, volname, watch,
watchdog, wc, wget, which, whoami, whois, xargs, xxd, xz,
xzcat, yes, zcat, zcip
:/ $
HBase Shell 是 HBase 提供的一个简单方便的命令行工具,用它可以直接操作 HBase,对 HBase 进行各种设置。 HBase Shell 提供的命令可以对对 HBase 数据进行增删改查。在上一篇 HBase 介绍 中对 HBase 做了简答的介绍,也初识了一些命令行。
根据官方的解释 Apache HBase Shell 是 (J)Ruby 下的 IRB(Interactive Ruby Shell),任何在 IRB 下的命令,在 HBase Shell 下都可以使用。1
可以在启动 HBase 之后,通过 ./bin/hbase shell
来进入 HBase Shell。
status
查询服务器状态
version
查询 HBase 版本
whoami
查看连接用户
列举数据库中所有表
list
create 命令
create 'table_name', 'cf1', 'cf2'
其中的 cf1 和 cf2 为列族名 1,列族名 2,列族需要在见表时确定,列则不需要, Column Family 是 Schema 的一部分,设计时就需要考虑。
在删除表之前需要使用 disable 命令,让表失效。在修改表结构时,也需要先执行此命令
disable "table_name'
删除表使用 drop 命令
drop 'table_name'
exists 'table_name'
会显示表是否存在:
hbase(main):002:0> exists 'test'
Table test does exist
0 row(s) in 0.2650 seconds
describe 命令查看表结构,显示 HBase 表 schema,以及 column family 设计
describe 'table_name'
enable 命令,和 disable 命令对应
enable 'table_name'
alter 修改表的结构,新增列族,删除列族。在修改之前要先 disable ,修改完成后再 enable
新增列族
alter 'table_name', '列族'
删除列族
alter 'table_name', {name=>‘列族’, METHOD=>'delete'}
举例:
hbase(main):049:0> alter 'test','cf2'
Updating all regions with the new schema...
1/1 regions updated.
Done.
0 row(s) in 1.7520 seconds
hbase(main):050:0> describe 'test'
DESCRIPTION ENABLED
'test', {NAME => 'cf', DATA_BLOCK_ENCODING => 'NONE', BLOOMFILTER => 'NONE', REPLICATION_SCOPE => '0', VERSIONS => '3', COMPRESSION => ' false
NONE', MIN_VERSIONS => '0', TTL => '2147483647', KEEP_DELETED_CELLS => 'false', BLOCKSIZE => '65536', IN_MEMORY => 'false', ENCODE_ON_DI
SK => 'true', BLOCKCACHE => 'true'}, {NAME => 'cf2', DATA_BLOCK_ENCODING => 'NONE', BLOOMFILTER => 'NONE', REPLICATION_SCOPE => '0', COM
PRESSION => 'NONE', VERSIONS => '3', TTL => '2147483647', MIN_VERSIONS => '0', KEEP_DELETED_CELLS => 'false', BLOCKSIZE => '65536', ENCO
DE_ON_DISK => 'true', IN_MEMORY => 'false', BLOCKCACHE => 'true'}
1 row(s) in 0.1680 seconds
hbase(main):052:0> alter 'test', {NAME => 'cf2', METHOD => 'delete'}
Updating all regions with the new schema...
1/1 regions updated.
Done.
0 row(s) in 1.5880 seconds
hbase(main):053:0> describe 'test'
DESCRIPTION ENABLED
'test', {NAME => 'cf', DATA_BLOCK_ENCODING => 'NONE', BLOOMFILTER => 'NONE', REPLICATION_SCOPE => '0', VERSIONS => '3', COMPRESSION => ' false
NONE', MIN_VERSIONS => '0', TTL => '2147483647', KEEP_DELETED_CELLS => 'false', BLOCKSIZE => '65536', IN_MEMORY => 'false', ENCODE_ON_DI
SK => 'true', BLOCKCACHE => 'true'}
1 row(s) in 0.2010 seconds
通常情况下列族不能被重命名,如果需要修改列族名字,通常用命令创建一个期望的列族名字,然后将数据复制过去,然后再删除旧列族。
使用 put 命令插入数据
插入数据,对于同一个 rowkey,如果执行两次 put,则认为是更新操作
put 'table_name', 'rowkey', '列族名 1: 列名 1', 'value'
put 't1', 'r1', 'c1', 'value', ts1
一般情况下 ts1(时间戳) 可以省略, Column 可以动态扩展,每行可以有不同的 Column。
增加指定表、行的值
incr
计算表的行数,count 一般比较耗时,使用
count 'table_name'
查询所有 rowkey
count 'table_name', { INTERVAL => 1 }
get 命令获取数据,HBase 的 shell 操作,大概顺序就是命令后接表名,rowkey,列名然后在后面用花括号加上其他过滤条件。
获取指定 rowkey 的指定列族指定列的数据,每个 Column 可以有任意数量的 Values,按照 Timestamp 倒序自动排序,可以使用 scan 'table_name', {VERSIONS => 10}
来验证,详细请查看 scan 命令
get 'table_name', 'rowkey', '列族名:列名'
获取指定 rowkey 的指定列族所有的数据
get 'table_name', 'rowkey', '列族名'
获取指定 rowkey 的所有数据
get 'table_name', 'rowkey'
获取指定时间戳的数据
get 'table_name', 'rowkey', {COLUMN=>'列族名:列', TIMESTAMP=>1373737746997}
获取多个版本值,查询默认返回最新的值
get 'table_name', 'rowkey', {COLUMN => '列族名:列名', VERSIONS => 2}
HBase 按照 rowkey 字典序 (1, 100, 102, 20) 自动排序,每行包含任意数量 Column,每列按照 列名 Column Key 排序。如果有列族 cf,其中有列 cf:a, cf:b, cf:c, 则按照字典序排序。
每个数据由 TabelName+RowKey+Column+Timestamp=>Value 唯一确定。
delete 命令删除表中数据,delete 命令只能用来删除某一列。
删除指定 rowkey 的指定列族的列名数据
delete 'table_name', 'rowkey', '列族名:列名'
删除指定 rowkey 指定列族的数据
delete 'table_name', 'rowkey', '列族名‘
使用 deleteall 命令来删除 rowkey 所有 column 的 Value,删除整行数据
deleteall 'table_name', ’rowkey'
使用 scan 命令全表扫描
hbase(main):043:0> scan 'test', {VERSIONS => 12}
ROW COLUMN+CELL
rowkey1 column=cf:a, timestamp=1487295285291, value=value 3
rowkey1 column=cf:a, timestamp=1487294839168, value=value 2
rowkey1 column=cf:a, timestamp=1487294704187, value=value 1
删除全表数据,这个命令也是 disable,drop,create 命令组合而成。
truncate 'table_name'
shell 命令,把所有的 hbase shell 命令写到一个文件内,类似与 Linux shell 脚本顺序执行所有命令,可以使用如下方法执行。
hbase shell test.hbaseshell
下面是比较完整的一个列表:
官方 reference
[[HBase]] — Hadoop Database,是一个分布式的、面向列的开源数据库,该技术来源于 Fay Chang 所撰写的 Google 论文《Bigtable:一个结构化数据的分布式存储系统》。HBase 是 Google Bigtable 的开源实现,就像 Bigtable 利用了 Google 文件系统(File System)所提供的分布式数据存储一样,HBase 在 Hadoop 之上提供了类似于 Bigtable 的能力,利用 Hadoop HDFS 作为文件系统,利用 Hadoop MapReduce 来处理 HBase 中海量数据,利用 [[ZooKeeper]] 作为协同服务,HBase 是 Apache 的 Hadoop 项目的子项目,
HBase 不同于一般的关系数据库,它是一个适合于非结构化数据存储的数据库。另一个不同的是 HBase 基于列的而不是基于行的模式。
HBase 是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统,利用 HBase 技术可在廉价 PC Server 上搭建起大规模结构化存储集群。HBase 不是关系型数据库,不支持 SQL。
Apache HBase™ is the Hadoop database, a distributed, scalable, big data store.
Use Apache HBase™ when you need random, realtime read/write access to your Big Data. This project’s goal is the hosting of very large tables – billions of rows X millions of columns – atop clusters of commodity hardware. Apache HBase is an open-source, distributed, versioned, non-relational database modeled after Google’s Bigtable: A Distributed Storage System for Structured Data by Chang et al. Just as Bigtable leverages the distributed data storage provided by the Google File System, Apache HBase provides Bigtable-like capabilities on top of Hadoop and HDFS.
3 维 map,三个维度分别是
HBase 的 map 是按照 key 来排序的,其将 key 转换为 byte[], 然后顺序进行存储。
行以 rowkey 作为唯一标识,rowkey 是一段字节数组,任何东西都能保存进去,字符串,数字等等。行按照字典序由低到高存储在表中。rowkey 可以是任意字符串,最大长度 64KB。
HBase 不支持条件查询和 Order by 查询,读取记录只能按照 rowkey 及其 range 或全表扫描,因此 rowkey 需要根据业务来设计以利用其字典序特性排序提高性能。
行的一次读写是原子操作,这个设计使得 HBase 的并发更新操作更加易懂。
列族是列的集合,要准确表示一个列,列族:列名
方式。列族(Column family)需要 在创建表时指定 ,列(Column)则不需要,可以随时在使用时创建。列族的成员在文件系统中都存储在一起,列族中所有列存储方式都一致。HBase 的每一个列都属于一个列族,以列族名为前缀,例如 A:a
和 A:b
都属于 A 列族。
HBase 通过 row 和 column 确定一份数据(Cell),不同版本值按照时间倒序排序,查询时默认返回最新数据。存储的值根据 tableName + RowKey + ColumnKey + Timestamp => value
唯一确定。Cell 中数据没有类型,字节码存储。
每个保存的 Cell 数据都会保存多版本,版本通过时间戳来索引,时间戳由 HBase 在数据写入时自动赋值,时间戳为系统时间毫秒。如果客户端想要显示赋值也可以,每个 Cell 中,不同时间版本数据按照时间倒序排列,最新的数据排在最前。
为了避免数据存在过多版本造成的的管理 (包括存贮和索引)负担,HBase 提供了两种数据版本回收方式。一是保存数据的最后 n 个版本,二是保存最近一段时间内的版本(比如最近七天)。用户可以针对每个列族进行单独设置。
包括安装和基本使用
从 Apache Download Mirrors 下载,从 stable 目录下下载 .tar.gz
的文件,比如 hbase-0.94.27.tar.gz
$ tar xfz hbase-0.94.27.tar.gz
$ cd hbase-0.94.27
安装依赖基础
确保 /etc/hosts
目录下
127.0.0.1 localhost
127.0.0.1 ubuntu.ubuntu-domain ubuntu
不要有 127.0.1.1 类似的出现,用 #
注释掉,如果有 ipv6 的地址也最好注释掉或者删掉。
编辑 vim conf/hbase-site.xml
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
<name>hbase.rootdir</name>
<value>file:///DIRECTORY/hbasedata</value>
</property>
<property>
<name>hbase.zookeeper.property.dataDir</name>
<value>/DIRECTORY/zookeeper</value>
</property>
</configuration>
配置其中的 hbase.rootdir
指向本地的目录,目录保存 HBase 的文件,HBase 会自动创建。默认数据存储目录为 /tmp/hbase-${user.name}
编辑 conf/hbase-env.sh
文件,配置如下两项,找到本地 Java 安装目录,可以使用 whereis java
来获取,将 JDK 的根目录配置如下:
export JAVA_HOME="/usr/lib/jdk"
export HBASE_MANAGES_ZK=true
然后使用如下命令启动 HBase:
$ ./bin/start-hbase.sh
starting Master, logging to logs/hbase-user-master-example.org.out
单机模式启动,停止的脚本也在同目录下。单机模式表示 HBase 所有服务都运行在一个 JVM 中,包括 HBase 和 Zookeeper。HBase 还有另外两种启动运行方式,伪分布式和分布式模式。
使用自带的客户端连接
$ ./bin/hbase shell
HBase Shell; enter 'help<RETURN>' for list of supported commands.
Type "exit<RETURN>" to leave the HBase Shell
Version: 0.90.0, r1001068, Fri Sep 24 13:55:42 PDT 2010
hbase(main):001:0>
创建表,插入数据
hbase(main):003:0> create 'test', 'cf'
0 row(s) in 1.2200 seconds
hbase(main):003:0> list 'test'
1 row(s) in 0.0550 seconds
hbase(main):004:0> put 'test', 'row1', 'cf:a', 'value1'
0 row(s) in 0.0560 seconds
hbase(main):005:0> put 'test', 'row2', 'cf:b', 'value2'
0 row(s) in 0.0370 seconds
hbase(main):006:0> put 'test', 'row3', 'cf:c', 'value3'
0 row(s) in 0.0450 seconds
cf 为 column family 列族,列族要求在创建表时指定,列族的成员在文件系统上存储在一起,HBase 的优化存储针对列族级别。
可以使用 list 命令来查询表名
然后可以使用 scan ‘test’ 来查询
hbase(main):006:0> scan 'test'
ROW COLUMN+CELL
row1 column=cf:a, timestamp=1480648404221, value=value1
row2 column=cf:b, timestamp=1480648416039, value=value2
row3 column=cf:c, timestamp=1480648427572, value=value3
3 row(s) in 0.0450 seconds
也可以使用 get 命令来获取记录
hbase(main):008:0> get 'test', 'row1'
COLUMN CELL
cf:a timestamp=1288380727188, value=value1
1 row(s) in 0.0400 seconds
删除记录
delete 方法只能删除 column
hbase(main):020:0> delete 'test', 'row2', 'cf:b'
0 row(s) in 0.0080 seconds
使用 deleteall 来删除 rowkey 的所有 column
hbase(main):001:0> deleteall 'test', 'row1'
0 row(s) in 0.3090 seconds
使用 disable 和 drop 来删除表
hbase(main):012:0> disable 'test'
0 row(s) in 1.0930 seconds
hbase(main):013:0> drop 'test'
0 row(s) in 0.0770 seconds
不清楚命令使用格式,可以使用 help "list"
来查看命令的具体使用帮助。
退出使用 exit<Enter>
最后退出可以使用 ./bin/stop-hbase.sh
来停止 hbase。
Mac 下安装 HBase 可以参考这篇
Hbase error zookeeper exists failed after 3 retries
16/12/02 10:45:28 INFO zookeeper.ClientCnxn: Opening socket connection to server localhost/127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown error)
16/12/02 10:45:28 WARN zookeeper.ClientCnxn: Session 0x0 for server null, unexpected error, closing socket connection and attempting
更多的配置可以参考
http://hbase.apache.org/0.94/book/configuration.html
这是清理记事本的文章,解决一个问题之后,将之前整理的内容,整理发布。清空 WizNote 计划。
手上有一些视频链接的 URL,如何快速的得到这些视频的时长信息?
经过一番调研,发现使用 ffprobe (和 ffmpeg 套件一起) 可以完美解决这个事情。将命令 -i
参数后面的地址改成线上URL 地址即可。
ffprobe -i https://cldup.com/po79gkocrO.mp4 -show_entries format=duration -v quiet -of csv="p=0"
也可以将此代码保存为 get_video_duration.sh
来使用 ./get_video_duration.sh URL
这样的方式来跑。
ffprobe -i $1 -show_entries format=duration -v quiet -of csv="p=0"
具体 bash script, 文件中每一行中,前面为 视频ID,空格,再是视频连接,使用下面脚本,将视频时长保存到 duration.txt
文件中。
set -o nounset # Treat unset variables as an error
while IFS='' read -r line || [[ -n "$line" ]]; do
lineArray=($line)
echo ${lineArray[0]}
duration=$(ffprobe -i ${lineArray[1]} -show_entries format=duration -v quiet -of csv="p=0")
echo $duration
echo "${lineArray[0]} ${duration}" >> duration.txt
done < "$1"
如果视频文件在本地的话,可能会方便很多, ffmpeg, ffprobe 都能够胜任。
ffmpeg -i input.mp4 2>&1 | grep "Duration"| cut -d ' ' -f 4 | sed s/,//
# 或者
ffprobe -show_format input.mp4 | sed -n '/duration/s/.*=//p'
Perf
全称Performance Event,是随着 Linux 2.6+ 一同发布的性能分析工具。通过它,应用程序可以利用 PMU,tracepoint 和内核中的特殊计数器来进行性能统计。它不但可以分析指定应用程序的性能问题 (per thread),也可以用来分析内核的性能问题,当然也可以同时分析应用代码和内核,从而全面理解应用程序中的性能瓶颈。
perf
工具在 linux-tools 下面,安装如下三个包即可
apt-get install linux-tools-common linux-tools-generic linux-tools-`uname -r`
Perf 能触发的事件分为三类:
当通过非 root 用户执行 perf 时会遇到权限不足的错误,需要 sudo -i
切换到 root 用户来执行
打印出 perf 可触发的 event
perf list
实时显示当前系统的性能统计信息
perf top
通过概括精简的方式提供被调试程序运行的整体情况和汇总数据
perf stat ./test
perf 命令过于复杂,他有很多子命令集,更多的信息可以参考下面的链接。
Spark 是一个依托于 Hadoop 生态的分布式内存计算框架,在吸收了 Hadoop MapReduce 优点的基础上提出以 RDD 数据表示模型,将中间数据放到内存,用于迭代运算,适用于实时计算,交互式计算场景。
简单的讲是一个通用计算引擎。
内存中比 Hadoop MapReduce 快 100 倍,磁盘中快 10 倍。
Spark 是一个用来实现快速而通用的集群计算的平台。
RDD 是 Spark 的核心概念,具有容错机制可以被并行操作的元素集合。RDD 可以通过并行化(parallelizing) 一个存在于 driver program 中的集合,或者引用外部存储系统上的数据集,比如共享文件系统,HDFS,HBase 或者任何提供了 Hadoop InputFormat 的数据源。
RDDs can be roughly viewed as partitioned, locality aware distributed vectors
通过[官方快速入门文档][1],这个文档主要通过本地 shell 运行一些例子,通过例子可以初步的学习怎么使用 Spark,用 Spark 的思维方式思考。
看过基本的 Spark 之后可以进一步了解 [RDD 官方指南][2],
Spark 本身是用 Scala 写的,运行在 Java 虚拟机 (JVM) 上。要在你的电脑或集群上运行 Spark, 你要做的准备工作只是安装 Java 6 或者更新的版本。如果你希望使用 Python 接口,你还需要一个 Python 解释器 (2.6 以上版本)。
每一个 Spark 应用都由一个驱动器(driver program)来发起集群上的各种并行操作。driver program 包含应用 main 函数,定义集群上的分布式数据集。driver program 通过 SparkContext 对象访问 Spark,这个对象代表对计算集群的一个连接。
shell 中已经默认创建了一个 SparkContext 对象 sc,SparkContext 可以用来创建 RDD。driver program 通常要管理多个执行器 (executor)。
Scala 版本
val sparkConf = new SparkConf().setAppName("wordcount")
val sc = new SparkContext(sparkConf)
val lines = sc.textFile("hdfs://...")
val words = lines.flatMap(_.split(" "))
val wordsCount = words.map(x=>(x,1))
val counts = wordsCount.reduceByKey(_ + _)
val result = counts.collect()
Fluent Style:
val result = sc.textFile("hdfs://...")
.flatMap(_.split(" "))
.map(x=>(x,1))
.reduceByKey(_ + _)
.collect()
Spark 中的 RDD 是一个不可变的分布式对象集合,每个 RDD 都被分为多个分区,这些分区运行在集群中的不同节点上。
RDD 支持两种操作:转化(transforming)和动作(action)。转化操作会由一个 RDD 生成一个新 RDD,比如 map(), filter() 等:
linesWithJava = lines.filter(lambda line: "Java" in line)
动作操作(action)会对 RDD 计算结果,并将结果返回到 driver program 中,或者把结果存储到外部系统(HDFS 等)中,比如 first(), count() 等:
linesWithJava.first()
Spark 提供如下方式创建 RDD:
最简单的方式就是将集合传给 SparkContext 的 parallilize() 方法,这种方法需要将整个数据集存放到一台机器中,所以只适合学习使用。或者使用 textFile() 来从外部存储中读取。
filter() 操作不会改变已有 inputRDD 数据,会返回一个全新的 RDD。
RDD transforming 操作都是惰性求值,被调用 action 操作之前 Spark 不会开始计算。不应当将 RDD 看做存放特定数据的数据集,最好把 RDD 当作通过转化操作构建出来的,记录如何计算数据的指令列表。把数据读取到 RDD 同样也是惰性的。当调用 sc.textFile() 时,数据并没有真正的被读取。
最常用的就是 filter 和 map。filter 接受一个函数,将 RDD 中满足该函数的值放入新的 RDD,map 接受一个函数,函数作用于每一个元素,函数返回值作为结果。
如果希望对每一个输入的元素输出多个元素,该功能操作叫做 flatMap(),提供给 flatMap() 的函数分别应用到输入的每一个元素,返回的是一个返回值序列的迭代器。输出的 RDD 不是由迭代器组成,而是一个包含各个迭代器可访问的所有元素的 RDD。 flatMap 最简单的用途,将输入的字符串切分为单词。
Scala
val lines = sc.parallelize(List("hello world", "hi"))
val words = lines.flatMap(line => line.split(" "))
words.first() // 返回"hello"
flatMap 可以看作将返回的迭代器“压扁”,得到一个由各个列表中元素组成的 RDD,而不是一个由列表组成的 RDD。
RDD 本身不是严格意义上的集合,但也支持许多数学意义的集合操作,比如合并和相交。需要注意的是 RDD 具有相同的数据类型。
常见的 reduce,接收一个函数,函数操作两个元素并返回一个同样类型的元素。
fold() 和 reduce() 类似,接收一个与 reduce() 相同签名的函数,加上一个初始值来作为每一个分区第一次调用的结果。
aggregate() 函数不要求返回值类型必须和输入的 RDD 类型相同。比如用 aggregate 来计算 RDD 的平均值
Scala:
val result = input.aggregate((0, 0))(
(acc, value) => (acc._1 + value, acc._2 + 1),
(acc1, acc2) => (acc1._1 + acc2._1, acc1._2 + acc2._2)
)
val avg = result._1 / result._2.toDouble
collect() 可以将数据返回到 driver program 中,collect() 需要将数据复制到 driver program 所以要求所有数据都必须能一同放到单台机器内存中。
take(n) 返回 RDD 中 n 个元素,并且尝试只访问尽量少的分区,会得到一个不均衡的集合。
如果数据定义了顺序,可以使用 top() 从 RDD 中获取前几个元素,top 会使用默认的顺序,也可以提供比较函数。
takeSample(withReplacement, num, seed) 函数可以从数据中获取采样,并指定是否替换。
foreach() 操作对 RDD 中每个元素进行操作,而不需要把 RDD 发回本地。
函数名 | 目的 | 示例 | 结果 |
---|---|---|---|
collect() | 返回 rdd 中的所有元素 | rdd.collect() | {1, 2, 3, 3} |
count() | rdd 中的元素个数 | rdd.count() | 4 |
countByValue() | 各元素在 rdd 中出现的次数 | rdd.countByValue() | {(1, 1), (2, 1), (3, 2)} |
take(num) | 从 rdd 中返回 num 个元素 | rdd.take(2) | {1, 2} |
top(num) | 从 rdd 中返回最前面的 num 个元素 | rdd.top(2) | {3, 3} |
takeOrdered(num)(ordering) | 从 rdd 中按照提供的顺序返回最前面的 num 个元素 | rdd.takeOrdered(2)(myOrdering) | {3,3} |
takeSample(withReplacement, num, [seed]) | 从 rdd 中返回任意一些元素 | rdd.takeSample(false, 1) | 非确定 |
reduce(func) | 并行整合 rdd 中所有数据 | rdd.reduce((x, y) => x + y) | 9 |
fold(zero)(func) | 和 reduce() 一 样,但是需要提供初始值 | rdd.fold(0)((x, y) => x + y) | 9 |
aggregate(zeroValue)(seqOp, combOp) | 和 reduce() 相似,但是通常返回不同类型的函数 | rdd.aggregate((0, 0))((x, y) =>(x._1 + y, x._2 + 1),(x, y) => (x._1 + y._1, x._2 + y._2)) | (9,4) |
foreach(func) | 对 rdd 中的每个元素使用给定的函数 | rdd.foreach(func) | 无 |
Spark SQL 用来操作结构化和半结构化的数据,Spark SQL 提供:
Spark SQL 提供特殊的 RDD,SchemaRDD,存放 Row 对象 RDD,每个 Row 对象代表一行记录。
很多应用需要实时计算,比如可能有些应用需要实时追踪 page view, 然后将数据给 machine learning model 训练,来自动检测异常。Spark Streaming 就是提供这样的功能。
Spark 建立在 RDD 基础上, Spark Streaming 提供了 DStreams 抽象,叫做 discretized streams。DStreams 是一个不断输入的序列数据。在内部,每一个 DStreams 都是时间序列的 RDD,离散的数据。
Spark 可以在各种各样的集群管理器(Hadoop YRAN,Apache Mesos )中运行。分布式环境下,Spark 集群采用主从结构,一个节点负责中央协调,中央协调节点被称为驱动器 (driver)节点,工作节点被称为执行器(executor)节点,驱动器节点可以和大量执行器节点进行通信,作为独立 Java 进程运行。驱动器节点和执行节点一起被称为一个 Spark 应用(application)。
驱动器是执行程序 main() 方法的进程。它执行用户编写的 SparkContext,创建 RDD,RDD 转化和行动操作的代码。
驱动器程序在 Spark 下职责:
执行器负责 Spark 作业运行任务,任务相互独立。执行器进程的作用:
Spark 为各种集群管理器提供了统一的工具来提交作业,这个工具是 spark-submit
bin/spark-submit --master spark://host:7077 --executor-memory 10g my_script.py
--master
标记指定要连接的集群 URL,spark:// 表示集群使用独立模式--executor-memory
执行器进程使用的内存量,以字节为单位Java 和 Scala 可以通过 spark-submit –jars 选项来提交独立的 JAR 包,因为通常都有非常复杂的依赖树,手动维护和提交全部的依赖太过麻烦,常规的做法是通过构建工具,生成单个大 JAR 包,包含应用所有的传递依赖。这个通常被称为超级 JAR(uber)或者组合 JAR(assembly)。Java 和 Scala 最广泛的构建工具是 Maven 和 sbt。