Arduino+RFID RC522 +继电器 - MicroHao - 博客园
连线部分
Arduino RC522 (工作电压3.3V)
· D5 <-------------> RST (这个脚不接貌似也可以)
· D10 <-------------> SDA (在RC522中即为CS)
· D11 <-------------> MOSI
· D12 <-------------> MISO
· D13 <-------------> SCK
· VCC <-------------> VCC
· GND <-------------> GND
继电器模块控制端
· D7 <-------------> In1
· VCC <-------------> VCC
· GND <-------------> GND
保留作者或是译者。
参考:
云上Java System Profiling与Debugging——蚂蚁金服观察与实践-CSDN.NET
常见问题解决思路
下面介绍一下处理一些常见的问题时,使用ZProflier和ZDebugger系统与使用传统工具在流程和思路上的一些区别。一个新上线的系统如果处理能力达不到我们的预期,或者一个老的系统处理速度突然下降了,抑或频繁抛出异常,这些都促使我们去思考系统存在性能问题该优化了,那我们通常会碰到的性能问题有OOM、CPU占用率高、Load高、频繁GC等。OOM的现象为Java进程直接退出,出错日志里可以看到OutOfMemoryError的异常。如果发现频繁的做MajorGC甚至是FullGC,一般也是OOM的前兆。解决此类问题的主要手段是分析heapdump。
JDK自带jmap工具,使用命令jmap-dump:live,format=b,file=heap.bin即可将Java进程的heap内存按照HPROFBinaryFormat的格式dump
图1ZProfiler
到名字为heap.bin的文件里。命令中的live参数,意味在dump之前会做一次FullGC,保证dump出来的对象都尽量是活的对象,减少heap.bin的大小,降低工具的分析时间。是否要设置这个参数要视具体问题,比如想知道old区到底为什么会增长这么快,都增加了些什么对象呢?这个时候你可能没必要去加live参数了,只要连续做两次heapdump,然后做一次内存对比,就知道这段时间内增加了哪些对象。但如果你想找出一些长时间不释放的对象,分析其根引用树是怎样的,这时加上live参数来dump就比较合理。
常用的heapdump分析工具是EclipseMemoryAnalyzerTool,简称MAT。这款工具功能很强大,但正由于它过于强大,因此给初学者带来的学习成本也比较高。其次它是一个客户端程序,需要安装在本地,因此它也受限于本地的机器性能,比如本机内存就4GB,而heap文件大小为8GB,这时MAT就无法完成分析工作了。因为MAT本身也是Java程序,它在分析大heap时会出现不断做GC,甚至fullgc等异常情况,导致分析没有结果。针对这些问题,我们的性能分析产品ZProfiler精减了部分不常用的功能,保证绝大部分用户使用上的简便性。另外ZProfiler是个Web系统,运行在一台服务器上,分析能力不再受限于开发人员的机器性能,而WebUI操作的方式也大大减轻了开发人员的工作量。
CPU高的问题需要分层来考虑,首先检查操作系统层面的一些原因,比如频繁的memorypaging,可能导致Java应用在内核态花费的时间较多。也可以使用perftop检查JVM内部比如JIT,GC线程的CPU使用情况。如果JIT线程使用的CPU较高,就需要看看codecache或者其他JIT相关参数是否设置合理;如果是gc线程,就需要进一步分析gclog,然后调整GC相关的参数等。
排除以上原因之后,基本可以确认是Java代码的问题,可以使用ZProfiler提供了HotMethodProfiling功能查看热点方法,这个后面有详细描述。
Load高意味着运行的线程或者运行队列里的线程比较多,此时可以通过线程dump进行排查。线程dump,可以使用JDK自带的工具jstack,执行命令jstack即可,会将进程的所有Java线程给dump出来。如果还想跟踪native的堆栈,需要增加-m参数。当拿到线程dump之后,按照线程状态进行归类【注:JVM里dump出来的线程日志,线程的状态并不是100%的准确,细节可以参考JVMBug:多个线程持有一把锁?】。对于同一个系统,Load高的机器RUNNABLE态的线程数目一定比Load低的机器多。我们可以通过threaddump来分析这些多出的线程都在干嘛,从而找到Load高的原因。
针对线程分析,ZProfiler不仅仅从状态粒度提供了分析,还从锁粒度以及热点栈粒度做了统计分析,ZProfiler可以帮助用户看到一把锁影响到了哪些线程,哪个线程持有这把锁,那些线程正在等待这把锁,对于线程数因为同步锁的问题突然大增基本可以通过ZProifiler的分析结果看出问题在哪里。同样的,对于某个方法在哪些线程里或者运行态的线程里正在栈上执行,ZProfiler也提供了统计,方便用户排查Load高等问题。
图2 ZDebugger
GC频繁是Java程序最常见的问题之一,大多数情况下都是由于相关参数配置不合理导致的。HotSpotGC相关的设置参数比较多,要找到比较合理的参数设置,首先要对应用的内存情况有一个总体的了解:比如应用运行稳定后,LiveDataSize大概是多少,这个会影响到-Xmx/-Xms/-Xmn的设置大小【注:CharlieHunt/BinuJohn合写的JavaPerformance提供了很好的Guide】;运行过程中创建的对象temp对象和longlived对象的大概比率,这个会影响到heap老区新区的设置比例。
查看GC情况通常有两种方式,一种是打开gclog相关的参数,事后分析gclog;另外一种是实时获取GC信息,可以打开JMX接口通过相关的MXbean获取,或者通过jstat命令。
gclog分析是个苦差,因为gclog输出的信息非常多,但是很多时候我们需要关注的是长期的趋势和整体的统计信息,人工分析太耗时间。目前也有一些不错的gclog分析工具,比如GCViewer。ZProfiler除了做了和GCViewer类似的工作之外,前面谈到过,ZProfiler系统本身就是部署在金融云里的,这意味着用户可以非常方便地通过简单的WebUI操作把gclog直接复制到ZProfiler上来分析。另外,我们也正在开发一些更高级的一些功能,在gclog分析完成后,给用户一些直接的建议,比如是否GCPause的时间过长,设置的GC参数应该如何调整,GC花费的CPU时间是不是过长等。
实时获取GC信息方面,VisualVM的VisualGC插件做的不错,非常直观地显示出当前应用的GC情况,但是分析方面做的不够。ZProfiler同样使用MXBean的方式获取实时的GC信息,将更有价值和更具统计意义的信息展示出来。而对于云上难以复现、偶发性的问题,我们推荐使用在线调试平台ZDebugger对运行中的程序进行在线调试。在线调试的原则是方便用户简单快速地打开调试器,且不能影响应用的正常运行。基于此思路,ZDebugger设计为基于Jetty的Web服务器,用户使用浏览器即可发送调试命令。调试器使用JDWP协议通过网络与被调试的JVM后端连接,在不重启JVM的情况下动态打开JVM调试功能,JVM在不阻断应用运行的前提下即时抓取断点处的运行信息并将其返回。
JDK定制
最后谈一下我们在OpenJDK中的一些定制。主要加入了Debug-on-Late-Attach(aka.DOLA)、Fast-SnapshotAt-Breakpoint(aka.FSAB)以及HotMethodProfiling等功能【注:后续我们也会和OpenJDK社区讨论怎么能把这些改动标准化,并贡献到社区,其中的一些部分已经开始了讨论】。
DOLA即在不重新启动JVM的情况下,可以把“有限的”的Java调试功能打开。这个功能对于生产环境的调试来说非常有意义。尽管目前主流的商业JVM基本都宣称“full-speeddebugging”,但在实际的使用中,我们没有多少人直接把JVM的调试能力在生产环境直接打开【注:在JVM启动时加-agentlib:jdwp设置参数,就可以打开JVM调试】,因为事实上还是对应用的性能有影响的。比如,对于JIT的影响,调试模式下EscapeAnalysis功能是被自动关掉的,而且一些解释器的fastpath也可会受到影响,而走slowpath。所以当线上问题出现时,往往要求重新启动问题JVM,把Debug功能打开调试,而尴尬在于,重启JVM往往会导致很难再现问题场景。这个是我们改造JDK,加入DOLA的初衷。
DOLA允许用户通过jcmd命令动态把JVM的“有限的”调试功能打开,通过ZDebugger,用户可以对被调试的JVM设置断点以及查看变量的值,这两个功能对我们来说已经足够帮助我们发现和诊断线上的大部分问题。
DOLA定制解决的难点主要在这么几个方面。
- jdwpagent改造,支持Agent_OnAttach接口,这样可以通过扩展的jcmd命令把jdwpagent在运行时加载到TargetJVM。
- Interpreter改造,在Bytecodepatch路径上支持断点设置逻辑【注:关于HotSpotBytecodepatch,感兴趣的读者可以参考RewriteBytecodes/RewriteFrequentPairs等相关实现机制】。
- 方法De-optimization机制改造:标准的HotSpotJVM实现,用户设置断点,设置断点的当前方法被deoptimize到解释器版本。而我们定制的HotSpot会把所有的方法在这个点上都de-optimize掉,这是由于我们在JVM启动的时候没有开启调试,JIT就没有记录用于设置断点的方法dependency信息,De-optimize所有的方法会帮助JIT在后续的编译版本里记录下dependency信息。
FSAB的作用是配合Zdebugger系统的watchpoint调试模式。这种模式借鉴自Google的clouddebugger,其应用场景是:在云环境中,传统的SingleStep调试模式根本不可行,因为设置断点并单步调试,会造成线程阻塞,导致系统出现超时等各种无关的错误,甚至威胁整个系统正常运行。
而watchpoint调试模式是在断点停住之后,自动将调用堆栈、各层的局部变量,以及用户设置的表达式的值快速地取出来,然后Disable断点,并Continue当前执行。断点需要用户主动Enable,才会再次生效。这种模式可以尽量减少调试动作对运行系统的影响。
取局部变量最早是直接使用jdi接口实现的。为了一次断点触发能给用户提供足够的信息,会递归地将每个局部变量的成员都取出来。当设置为递归四层的时候,jdi的性能问题暴露了出来,断点触发之后停顿时间长达几秒钟。因此我们扩展了jdwp,增加了FSAB,使用Native方法直接批量获取数据。
另外我们还提供了两项比较独特的功能。一个是基于watchpoint调试模式的“多人在线调试”,即ZDebugger可以让用户创建或者订阅自己感兴趣的断点,当断点触发时将相关信息推送给订阅该断点的用户。这样多个用户可以互不干扰地对同一个运行系统进行调试。另一个是自动跟踪用户关注的数据。首次断点触发后,通过FSAB获取局部变量成员数据的时候默认是广度遍历。最终在用户界面上的展示是一颗可以展开的树,用户展开去查看自己关注的成员。这时系统会记录下用户关注的是哪个成员,下次断点触发通过FSAB获取数据时,会根据用户关注成员的路径进行深度遍历。获取更多用户关注的路径上的数据,而忽略其他分支。
HotMethodProfiling顾名思义就是热点函数剖析。热点函数统计剖析是性能分析中很重要的一项工作。因为按照已有的经验,程序运行过程中大部分时间都花费在少量的代码上。为了优化工作的投入产出比,应该先考虑优化程序中的热点函数。
热点函数统计有两种方法,一种是插桩,一种是采样。前者的例子有gprof,通过编译时增加一个特殊的编译选项,在所有函数的入口和出口插桩。运行时,在桩函数中记录时间等信息,输出到结果文件。事后可以得到每个函数精确的调用次数、执行时长以及整个函数调用关系图。其缺点是需要重新编译,而且要程序运行结束后才能拿到结果。后者的例子如perf,通过高频率的定时中断去打断运行中的程序,并在中断处理中记录当前程序运行的上下文信息。这样的结果显然是一种统计意义上的结果。如果采样频率不够高(出于性能考虑,一般也不会特别高),有可能会漏掉一些频繁执行,但每次执行时间短于采样间隔的代码。但其优点是不用重编程序,随时动态开关,马上就能得到结果。
ZProfiler采用是后一种方法。因为在用户态程序中,无法像perf一样采用硬件中断,而是采用了定时信号(linux系统提供了一种SIGPROF定时器)。我们提供了start和stop两个jmx命令,其实就是启动和停止一个定时器。在定时器信号的处理函数中去回溯Java调用栈。
回溯Java调用栈,ZProfiler使用了HotSpotJVM的一个非标准接口AsyncGetCallTrace(据说Oracle商业软件JavaMissionControl的MethodProfiling也是用的这个接口)。通过调用这个接口可以获得当前被定时器信号打断的线程的Java调用栈,而不需要等到JVM的safepoint才能收集线程堆栈。依赖safepoint来收集堆栈(比如通过调用Thread.getAllStackTraces)的profiling机制是有缺陷的,往往收集到的热点堆栈并不精确。在运行JITed的代码情况下,safepoint的时机往往是由JIT来决定的,比如在执行一些非常Hot的循环时,为了保证执行效率,JIT生成的代码就不会插入safepoint的轮询。这样的情况下,依赖safepoint的profiling机制就不会profiling到这些热点。有关AsyncGetCallTrace的使用,感兴趣的读者可以在网上找到更多的资料,或者可以直接阅读HotSpot的源代码,理解它的原理和用法。
Arduino教程汇总贴(2014.12.29更新)-Arduino中文社区
初识arduino http://www.arduino.cn/thread-1083-1-1.html
关于Arduino及其周边配件的购买建议 http://www.arduino.cn/thread-11017-1-1.html
常见arduino版本比较 http://www.arduino.cn/thread-1192-1-1.html
关于使用Arduino做开发的二三理解 http://www.arduino.cn/thread-5414-1-1.html
Arduino IDE下载 http://www.arduino.cn/thread-5838-1-1.html
Arduino驱动安装方法 http://www.arduino.cn/thread-1008-1-1.html
Arduino驱动安装失败的解决方法:
Arduino驱动问题一键修复工具beta http://www.arduino.cn/thread-12349-1-1.html
安装驱动数据无效 http://www.arduino.cn/thread-7531-1-1.html
系统找不到指定文件 http://www.arduino.cn/thread-2485-1-1.html
Arduino入门套件官方视频教程 http://edu.elecspark.com/course/1
Arduino Robot官方视频教程 http://edu.elecspark.com/course/2
Arduino语言参考 http://www.arduino.cn/reference
Cooper Maa的Arduino教程(适合用各种元件学习Arduino的朋友) http://www.arduino.cn/thread-2814-1-1.html
海神的Arduino模块使用教程(适合用Arduino模块的朋友) http://www.arduino.cn/thread-3274-1-1.html
珜羽的教程(通俗易懂) http://www.arduino.cn/thread-4435-1-1.html
2.扩展库的使用
Arduino教程(提高篇)——SR04超声波类库的使用 http://www.arduino.cn/thread-1003-1-1.html
Arduino教程(提高篇)——舵机的驱动 http://www.arduino.cn/thread-1038-1-1.html
Arduino教程(提高篇)——使用EEPROM断电也能保存数据 http://www.arduino.cn/thread-1157-1-1.html
用共用体结构保存其他类型的数据到EEPROM: http://www.arduino.cn/thread-2684-1-1.html
Arduino教程(提高篇)——红外遥控(接收篇) http://www.arduino.cn/thread-1220-1-1.html
Arduino教程(提高篇)——红外遥控(发射篇) http://www.arduino.cn/thread-1394-1-1.html
使用IRremote库红外遥控家里的电器 http://www.arduino.cn/thread-3618-1-1.html
Arduino教程(提高篇)——驱动12864LCD模块 http://www.arduino.cn/thread-1930-1-1.html
3.进阶
Arduino教程(提高篇)——外部中断的使用 http://www.arduino.cn/thread-2421-1-1.html
Arduino教程(提高篇)——编写扩展库 http://www.arduino.cn/thread-1009-1-1.html
Arduino教程(提高篇)——使用Visual Studio开发Arduino http://www.arduino.cn/thread-1683-1-1.html
Arduino教程(提高篇)——把arduino变成AVRISP烧写器 http://www.arduino.cn/thread-1245-1-1.html
Arduino教程(提高篇)——红外遥控控制舵机 http://www.arduino.cn/thread-1242-1-1.html
Arduino教程(提高篇)——使用Ethernet构建简易的Web Server http://www.arduino.cn/thread-8514-1-1.html
在Arduino中使用看门狗定时器 http://www.arduino.cn/thread-2638-1-1.html
串口的高级用法 http://www.arduino.cn/thread-2710-1-1.html
修改Arduino串口缓冲区大小 http://www.arduino.cn/thread-7885-1-1.html
在Arduino上使用printf格式化输出到串口 http://www.arduino.cn/thread-8366-1-1.html
4.各种传感器、模块使用
传感器扩展板的使用 http://www.arduino.cn/thread-2779-1-1.html
五向倾斜模块 http://www.arduino.cn/thread-2327-1-1.html
旋转编码器 http://www.arduino.cn/thread-2423-1-1.html
无源蜂鸣器模块 http://www.arduino.cn/thread-1513-1-1.html
DS18B20温度传感器 http://www.arduino.cn/thread-1345-1-1.html
DHT11温湿度传感器 http://www.arduino.cn/thread-1429-1-2.html
LM35线性温度传感器 http://www.arduino.cn/thread-1055-1-1.html
韦根协议(26位)及其读取算法 http://www.arduino.cn/thread-2587-1-2.html
GSM/GPRS/GPS系列教程 http://www.arduino.cn/thread-6985-1-1.html
5.实例制作
基于Arduino的数字示波器 http://www.arduino.cn/thread-3703-1-1.html
模型用GPS测速仪 http://www.arduino.cn/thread-3623-1-1.html
Arduino实现自动浇水、遮阳 http://www.arduino.cn/thread-3437-1-1.html
基于arduino 控制的语音控制调节台灯 http://www.arduino.cn/thread-3520-1-1.html
智能壁障小车(带控制端) http://www.arduino.cn/thread-3445-1-1.html
Arduino制作自行车POV制作 http://www.arduino.cn/thread-3748-1-1.html
远程遥控侦查机器人 http://www.arduino.cn/thread-3768-1-1.html
基于Arduino的穿戴式LED灯条表演服 http://www.arduino.cn/thread-3400-1-1.html
Aduino 制作音乐播放器 http://www.arduino.cn/thread-2944-1-1.html
Arduino小车制作教程(巡线、壁障、遥控) http://www.arduino.cn/thread-3728-1-1.html
人工智能之五子棋机器人——人机对弈 http://www.arduino.cn/thread-1108-1-1.html
基于arduino控制器智能垃圾桶 http://www.arduino.cn/thread-975-1-1.html
Arduino 蓝牙遥控小车 http://www.arduino.cn/thread-2247-1-1.html
Ulink基于微信公众平台的远程物联网控制方案 http://www.arduino.cn/thread-7368-1-1.html
自平衡机器人——蛋黄物语 http://www.arduino.cn/thread-6246-1-1.html
艺术类项目——如影随形 http://www.arduino.cn/thread-7211-1-1.html
基于arduino的迈克尔逊干涉仪测量自动化装置 http://www.arduino.cn/thread-6220-1-1.html
6.Arduino Leonardo专题
Arduino Leonardo 中文介绍 http://www.arduino.cn/thread-1205-1-1.html
leonardo做的无线鼠标 http://www.arduino.cn/thread-2436-1-1.html
关于leonardo在模拟USB设备后,无法下载的问题 http://www.arduino.cn/thread-1653-1-1.html
7.Arduino Due专题
Arduino Due 中文介绍 http://www.arduino.cn/thread-2216-1-1.html
第三方CANBUS库 http://www.arduino.cn/thread-5735-1-1.html
8.Arduino Yun专题
Arduino Yun 中文介绍 http://www.arduino.cn/thread-4075-1-1.html
9.Arduino Zero专题
Arduino Zero 中文介绍
10.Arduino Ethernet专题
Arduino Ethernet中文介绍 http://www.arduino.cn/thread-8384-1-1.html
11.Intel Galileo/Edison专题
Intel Galileo/Edison资源汇总贴 http://www.arduino.cn/thread-4526-1-1.html
X.其他
Yeelink-物联网跟我做 http://www.arduino.cn/thread-1142-1-1.html
arduino学习笔记 - Mega 2560+TFT3.2寸屏的演示实验 http://www.arduino.cn/thread-989-1-3.html
Arduino UNO + GP2D12红外测距传感器+LCD1602详细演示过程 http://www.arduino.cn/thread-1107-1-1.html
Arduino Uno + HY-SRF05 超声波测距模块详细讲解演示实验 http://www.arduino.cn/thread-1110-1-1.html
arduino学习笔记 - Arduino Uno + MMA7361三轴加速度传感器 http://www.arduino.cn/thread-1141-1-1.html
AVR学习
AVRGCC用户手册 http://www.arduino.cn/avrgcc/
Arduino示例教程模块版汇总
1、Arduino示例教程模块版——开始您的Aduino之旅(WINDOWS版)
2、Arduino示例教程模块版——传感器扩展板的使用
3、Arduino示例教程模块版——按键实验
4、Arduino示例教程模块版——模拟声控路灯实验
5、Arduino示例教程模块版——直滑电位器与光互动实验
6、Arduino示例教程模块版——数字抢答器实验
7、Arduino示例教程模块版——超声波测距实验
8、Arduino示例教程模块版——红外遥控实验
9、Arduino示例教程模块版——舵机控制实验
10、Arduino示例教程模块版——人体红外报警实验
11、Arduino示例教程模块版——避障传感器与光互动实验
12、Arduino示例教程模块版——电子琴实验
13、Arduino示例教程模块版——智能环境监控实验
Cooper Maa系列教程汇总贴
使用者可以在 Arduino 板子上接上各種電子裝置,例如 LED 燈、喇叭、馬達、開關、溫濕度感測器、紅外線發射與接收器、LCD 顯示裝置,以及 Ethernet, WiFi, XBee, Bluetooth, RFID, GPS 等各種通訊模組。若再配合撰寫一些自動控制的程式,就能利用 Arduino 做出各式各樣的自動控制應用,例如利用溫度感測器控制風扇的運轉、使用可變電阻控制燈光的明暗、控制馬達的轉速、利用紅外線遙控家電/ 利用伺服機 (Servo) 控制機械手臂或機器人,以及製作自走車、飛行器等等。
這系列教學將讓你認識 Arduino,並且向你介紹各種基本電子裝置的使用方法。
教學目標
學習撰寫 Arduino 程式,以及使用各種基本電子裝置,包括: LED, 蜂鳴器、觸動開關、可變電阻、PWM 調光器、光敏電阻、七段顯示器、字元 LCD、74HC595 移位暫存器、Matrix LED、Keypad、伺服馬達、紅外線移動感測器、紅外線測距以及繼電器等。
授課對象
Arduino 進入門檻低,即便你沒有電子電機相關科系的背景,也可以很容易學會使用 Arduino,因此只要具備基本電腦操作能力就可以學習。
认识 Arduino http://www.arduino.cn/thread-2815-1-1.html
准备开发环境 http://www.arduino.cn/thread-2823-1-1.html
Lab1 Blinking a LED http://www.arduino.cn/thread-2853-1-1.html
Lab2 使用按鍵控制 LED 燈號 http://www.arduino.cn/thread-2854-1-1.html
Lab3 控制 LED 灯亮度 http://www.arduino.cn/thread-2855-1-1.html
Lab4 使用电位器调光 http://www.arduino.cn/thread-2871-1-1.html
Lab5 LED Bar Graph http://www.arduino.cn/thread-2872-1-1.html
Lab6 控制蜂鸣器发声 http://www.arduino.cn/thread-2877-1-1.html
Lab7 使用七段顯示器製作倒數功能 http://www.arduino.cn/thread-2881-1-1.html
Lab8 使用光敏电阻控制 LED 的开关 http://www.arduino.cn/thread-2907-1-1.html
Lab9 在1602 LCD 上显示 "Hello World" http://www.arduino.cn/thread-2908-1-1.html
Lab10 使用 SHT15 溫溼度計 http://www.arduino.cn/thread-2913-1-1.html
實例介紹:只做一个Arduino温度记录器(三部曲) http://www.arduino.cn/thread-2928-1-1.html
http://www.arduino.cn/thread-2929-1-1.html
http://www.arduino.cn/thread-2930-1-1.html
Lab11 使用 74HC595 和三支腳位控制 8 个LED http://www.arduino.cn/thread-3006-1-1.html
Lab12 使用兩顆 74HC595 和三支腳位控制 16个LED http://www.arduino.cn/thread-3007-1-1.html
Lab13 使用 74HC595 与7段数码管制作倒数功能 http://www.arduino.cn/thread-3010-1-1.html
Lab14 使用 74HC595 控制 HD44780 兼容 LCD http://www.arduino.cn/thread-3135-1-1.html
Lab15 使用四位七段数码管制作计数器 http://www.arduino.cn/thread-3140-1-1.html
Lab16 使用电位器控制舵机 http://www.arduino.cn/thread-3321-1-1.html
Lab17 使用光敏电阻控制舵机 http://www.arduino.cn/thread-3322-1-1.html
Lab18 讀取 3x4 Keypad 的輸入 http://www.arduino.cn/thread-3396-1-1.html
Lab19 使用 5x7 LED Matrix http://www.arduino.cn/thread-3397-1-1.html
Lab20 用紅外線動作感測器(PIR Motion Sensor)控制 LED 的開關 http://www.arduino.cn/thread-3398-1-1.html
Lab21 用繼電器控制 12V 風扇 http://www.arduino.cn/thread-3399-1-1.html
java - Why is TimeZone.getTimeZone(id) synchronized, and why this isn't documented? - Stack Overflow
有JavaEE系统一个服务器实例耗尽32核CPU,看到有大量如下线程堆栈信息:
"[ACTIVE] ExecuteThread: '255' for queue: 'weblogic.kernel.Default (self-tuning)'" daemon prio=10 tid=0x00002aaad2ba8000 nid=0x247f waiting for monitor entry [0x00002aaaefa37000]
java.lang.Thread.State: BLOCKED (on object monitor)
at java.util.TimeZone.getDefaultInAppContext(TimeZone.java:627)
- locked <0x00000006e05a43e8> (a java.lang.Class for java.util.TimeZone)
at java.util.TimeZone.getDefaultRef(TimeZone.java:523)
at java.util.GregorianCalendar.<init>(GregorianCalendar.java:541)
at com.aspire.mo.framework.util.DateUtil.getCacheDate(DateUtil.java:292)
at com.itindex.mo.framework.base.QueryTemplate$2.query(QueryTemplate.java:279)
上面这个锁导致了50多个线程被阻塞。
分析:在使用大量内存的大型多线程环境中,TimeZone是一个瓶颈,它使用了一个 synchronized HashMap。
建议:使用Joda-Time时间类来代替Calendar类消除这个瓶颈。
参见下列Java 日期类性能测试:
I tested single threaded performance, where 1M Dates were created using each method in a single thread. Then multi-threaded with 4 threads, each thread creating 250k Dates. In other words: both methods ended up creating the same number of Dates.
看到Calendar日期类性能最差。
参考:
http://blog.lick-me.org/2013/08/java-date-performance-subtleties/
(8)资深内部人士:给广州各大初中重新排名——李高奇(LGQ)培优学校_西工大李高奇培优学校_新浪博客
这几年广州小升初政策年年有变花样百出
小孩赶考之复杂性简直可以用“帙繁浩叠”一词形容
众家长精疲力竭之余希望恢复小升初考试的呼声一年赛一年
在这种复杂背景之下各大初中的排位不可避免地与“我们小时候”大相径庭
不可以旧眼光旧思维去讨论
目前广州各大初中主要分成两大类:民办初中和公办初中,分成几大集团军:假定是第一集团军,第二,第三,第四。
民办初中,由于实行入学考试,通过考试筛选了生源,因此在这几年小升初考试中大放异彩。但是民办初中有不少是打了当年公办初中的名字,不知就里的家长很可能将两者混淆,例如以为育才实验是育才中学。
通过中考平均成绩来判断民办初中的佼佼者有:广雅实验、育才实验、中大附中、应元二中
第二集团的有:番禺华附、南武实验、祈福英语、省实天河等
而公办初中里,不少在前些年都停办了初中,仅办高中,最出名的有这几家:执信中学、广雅中学、六中。但是这两年在舆论压力和生源诱惑的双重驱动下,执信和六中已经在内部恢复小规模的初中招生。
这些年来,公办初中的佼佼者有:七中、省实、华侨外国语、广大附中、十六中、47中等。但是这类学校由于有部分是派位生源,因此总体成绩不能和民办初中最好的相比,甚至比不上民办初中第二集团,但是这些学校的重点班成绩,不比民办初中最好的差,有时甚至更好
仅以近三年成绩来论
目前广州市初中处于第一名还是华师附中
但是该初中仅在全省招收两个班共80-90人
因此不将该校成绩列入其中,仅将其当作武侠小说里“独狐求败”式的人物.
第一集团军:
广雅实验(民办,与广雅有关系但并非从前的广雅)、育才实验(民办,并非育才中学)、应元二中(民办,与二中有关系但并非从前的二中)、中大附中(民办,与中大附小没有直升关系,父母在中大工作子女可直接就读该校)、西关外国语(公办)、四中聚贤(非以前的四中)、六中课改班(仅招收两个班,生源基本是海珠区各校尖子生)
第二集团军:
民办:海珠区南武实验(非以前的南武)、海珠区珠江中学、番禺区华附(并非从前的华师附中)、番禺区祈福英语、白云区华附新世界(并非从前的华师附中)、白云区广外附设、白云区云景培英、天河区省实天河、荔湾区一中实验,
公办:花都邝中,以及越秀区的那一把名校:铁一中、省实、七中、侨外、广大附中、16中、二中、八一(八一2010年中考成绩不错,暂时先放第二集团),尤其是越秀区这一把公办的名校,重点班的成绩不比第一集团军差
第三集团:培正中学、育才中学、三中、47中,番禺东风中学,番禺侨诚中学
比培正、育才略低10-20分的,同样属于630-600分这一组的还有
汇景47中,
其他也还可以考虑的初中
解读广州高中学校生源的分组_2015广州中考-广州家长帮社区

通常,在每年的三月下旬,市教育局会向各区教育局和学校发布《年度普通高中毕业班工作质量目标和学校生源分组情况的通知》,其中的附件之一就是学校生源的分组情况。
Lucene权限实现 - 简单之美 - 企业应用与站点完美解决方案
1、权限过滤
Lucene的基于关键字的评分机制,适用于基于相关度的过滤和排序。它是基于矢量模型,其中给文档分配一个相应的分数,分数越高相关的文档也越多。然而,应用系统有时因为用户级权限仅需返回相关文档的子集。
过滤的权限问题实际上是查询时将一个布尔过滤器作用于文档的普遍问题的子问题。我们将探讨这种过滤的实现方法。
2、查询改写
上述权限过滤明显的实现方法,就是改写搜索查询为要求某字段包含特定的值。
例如,如果有一个“类别(category)”字段(Field),并假设仅显示历史(history)和科学(science)类的文档,然后对给定的用户查询:
<query>
查询可被改写为:
<query>+category:history+category:science
3、查询过滤器
此种假设对于过滤的字段(Field)可能是不适宜或不可行的(也许由于字段(Field)是易变的和频繁的变化将导致索引的大量修改)。另一方法是通过 实现Filter接口创建一个过滤器,只需实现一个方法,即bits()方法,该方法返回BitSet,包含命中的所有允许文档ID。
在bits()方法中,可使用TermEnum通过Term筛选(速度慢!),或使用FieldCache检索字段的所有值(速度快,但为内存密集型!)。
4、HitCollector + FieldCache
另一个筛选文档(Documents)的方法是使用HitCollector而非Hits对象。在collect()方法以文档(Document)ID和评分(score)作为参数,可以此来判断文档允许访问与否。
使用HitCollector有一个小缺点,Hits类中遍历搜索结果的有用方法都没有公布,但解决方法容易。
5、范例
比方说,你有一个多用户博客程序,并希望让用户搜索全部(默认)博客文章,或允许用户只搜索他发表的博客。博客应用的Lucene模型将每一博客映射为一Document。
使用查询改写方法,可以轻松地为该查询附加搜索(条件)子句:
<query>+author:<authorid>
这将只返回作者的文档。问题解决了。
现在,扩展该示例。假设在应用中存在3种访问角色:管理员(admin),编辑(editor),作家(author)。这些访问角色的权力是递减的,所 以一个编辑(editor)对作者的博客有写(write)权限,但对管理员(admin)的博客无此权限。如何才能让用户只搜索他具有写访问权限的博客 呢?
该查询改写方法可用来为每一文档(Document)添加一个“角色(role)”字段(Field),并现场填入作者角色(role)。假设用户的角色为编辑(editor),那么改写后查询为:
<query>+(role:editor role:writer)
这种方法是有效的,但不是最理想的,因为每次作者的角色会改变,就需要更新他所写所有博客文档(Documents)。
另一种使用查询改写的方法是获得角色为编辑(editor)和作者(writer)的所有用户列表,然后追加到查询后,如下所示:
<query>+(author:1author:2 author:......)
这方法更好,但庞大的或-查询子句对于布尔过滤可能会妨碍搜索性能。
实现该功能的第三种方法是获取期望的用户列表,如同上前一方法,但不是改写查询,而是使用HitCollector + FieldCache的方法,因此只接受所期望的博客。这种方法具有第二种方法的优势但无性能问题。
performance - High load average, low CPU usage - why? - Server Fault
We're seeing huge performance problems on a web application and we're trying to find the bottleneck. I am not a sysadmin so there is some stuff I don't quite get. Some basic investigation shows the CPU to be idle, lots of memory to be available, no swapping, no I/O, but a high average load.
The software stack on this server looks like this:
. Solaris 10 . Java 1.6 . WebLogic 10.3.5 (8 domains)
The applications running on this server talk with an Oracle database on a different server.
This server has 32GB of RAM and 10 CPUs (I think).
Running prstat -Z gives something like this:
PID USERNAME SIZE RSS STATE PRI NICE TIME CPU PROCESS/NLWP
3836 ducm0101 2119M 2074M cpu348 58 0 8:41:56 0.5% java/225
24196 ducm0101 1974M 1910M sleep 59 0 4:04:33 0.4% java/209
6765 ducm0102 1580M 1513M cpu330 1 0 1:21:48 0.1% java/291
16922 ducm0102 2115M 1961M sleep 58 0 6:37:08 0.0% java/193
18048 root 3048K 2440K sleep 59 0 0:06:02 0.0% sa_comm/4
26619 ducm0101 2588M 2368M sleep 59 0 8:21:17 0.0% java/231
19904 ducm0104 1713M 1390M sleep 59 0 1:15:29 0.0% java/151
27809 ducm0102 1547M 1426M sleep 59 0 0:38:19 0.0% java/186
2409 root 15M 11M sleep 59 0 0:00:00 0.0% pkgserv/3
27204 root 58M 54M sleep 59 0 9:11:38 0.0% stat_daemon/1
27256 root 12M 8312K sleep 59 0 7:16:40 0.0% kux_vmstat/1
29367 root 297M 286M sleep 59 0 11:02:13 0.0% dsmc/2
22128 root 13M 6768K sleep 59 0 0:10:51 0.0% sendmail/1
22133 smmsp 13M 1144K sleep 59 0 0:01:22 0.0% sendmail/1
22003 root 5896K 240K sleep 59 0 0:00:01 0.0% automountd/2
22074 root 4776K 1992K sleep 59 0 0:00:19 0.0% sshd/1
22005 root 6184K 2728K sleep 59 0 0:00:31 0.0% automountd/2
27201 root 6248K 344K sleep 59 0 0:00:01 0.0% mount_stat/1
20964 root 2912K 160K sleep 59 0 0:00:01 0.0% ttymon/1
20947 root 1784K 864K sleep 59 0 0:02:22 0.0% utmpd/1
20900 root 3048K 608K sleep 59 0 0:00:03 0.0% ttymon/1
20979 root 77M 18M sleep 59 0 0:14:13 0.0% inetd/4
20849 daemon 2856K 864K sleep 59 0 0:00:03 0.0% lockd/2
17794 root 80M 1232K sleep 59 0 0:06:19 0.0% svc.startd/12
17645 root 3080K 728K sleep 59 0 0:00:12 0.0% init/1
17849 root 13M 6800K sleep 59 0 0:13:04 0.0% svc.configd/15
20213 root 84M 81M sleep 59 0 0:47:17 0.0% nscd/46
20871 root 2568K 600K sleep 59 0 0:00:04 0.0% sac/1
3683 ducm0101 1904K 1640K sleep 56 0 0:00:00 0.0% startWebLogic.s/1
23937 ducm0101 1904K 1640K sleep 59 0 0:00:00 0.0% startWebLogic.s/1
20766 daemon 5328K 1536K sleep 59 0 0:00:36 0.0% nfsmapid/3
20141 daemon 5968K 3520K sleep 59 0 0:01:14 0.0% kcfd/4
20093 ducm0101 2000K 376K sleep 59 0 0:00:01 0.0% pfksh/1
20797 daemon 3256K 240K sleep 59 0 0:00:01 0.0% statd/1
6181 root 4864K 2872K sleep 59 0 0:01:34 0.0% syslogd/17
7220 ducm0104 1268M 1101M sleep 59 0 0:36:35 0.0% java/138
27597 ducm0102 1904K 1640K sleep 59 0 0:00:00 0.0% startWebLogic.s/1
27867 root 37M 4568K sleep 59 0 0:13:56 0.0% kcawd/7
12685 ducm0101 4080K 208K sleep 59 0 0:00:01 0.0% vncconfig/1
ZONEID NPROC SWAP RSS MEMORY TIME CPU ZONE
42 135 22G 19G 59% 87:27:59 1.2% dsuniucm01
Total: 135 processes, 3167 lwps, load averages: 54.48, 62.50, 63.11
I understand that CPU is mostly idle, but the load average is high, which is quite strange to me. Memory doesn't seem to be a problem.
Running vmstat 15 gives something like this:
kthr memory page disk faults cpu
r b w swap free re mf pi po fr de sr s0 s1 s4 sd in sy cs us sy id
0 0 0 32531400 105702272 317 1052 126 0 0 0 0 13 13 -0 8 9602 107680 10964 1 1 98
0 0 0 15053368 95930224 411 2323 0 0 0 0 0 0 0 0 0 23207 47679 29958 3 2 95
0 0 0 14498568 95801960 3072 3583 0 2 2 0 0 3 3 0 21 22648 66367 28587 4 4 92
0 0 0 14343008 95656752 3080 2857 0 0 0 0 0 3 3 0 18 22338 44374 29085 3 4 94
0 0 0 14646016 95485472 1726 3306 0 0 0 0 0 0 0 0 0 24702 47499 33034 3 3 94
I understand that the CPU is mostly idle, no processes are waiting in the queue to be executed, little swapping is happening.
Running iostat 15 gives this:
tty sd0 sd1 sd4 ssd0 cpu
tin tout kps tps serv kps tps serv kps tps serv kps tps serv us sy wt id
0 676 324 13 8 322 13 8 0 0 0 159 8 0 1 1 0 98
1 1385 0 0 0 0 0 0 0 0 0 0 0 0 3 4 0 94
0 584 89 6 24 89 6 25 0 0 0 332 19 0 2 1 0 97
0 296 0 0 0 0 0 0 0 0 0 0 0 0 2 2 0 97
1 1290 43 5 24 43 5 22 0 0 0 297 20 1 3 3 0 94
Running netstat -i 15 gives the following:
When you say 'High Load average' I assume you mean that prstat shows for 'load average' at the bottom of the output figures of
Total: 135 processes, 3167 lwps, load averages: 54.48, 62.50, 63.11These numbers, look similar to the ones that top provides and probably mean the average queue size of running process. This isn't the percentage of processor time being used but how many 'things' are harassing the CPU for time to run. Admittedly, these do look quite high but this all depends on the app that you are running; the processes may not actually be doing much once they get their slot. See herefor a nice explanation regarding top.
I'm not familiar with WebLogic but I have noticed that, generally, with Apache Tomcat many Java threads can be spawned simultaneously for what appears as not many requests. It could be this that is causing those high average load numbers. Make sure that you are using connection pooling where appropriate to connect to the backend and consider upping the number of idle threads that are available to your app to handle connections (not sure how you do this on WebLogic; Tomcat has a per connector thread pool or a general executor thread pool). If you don't do this then brand new threads may be being spawned to process requests.
As to performance, you need to nail down what part of your app is suffering. Is it the processing that is happening in the WebLogic/Java side of things, the database access, DNS lookups (if they're being done for some reason...), network issues or something on the OS.
99% of the time it will be your code and how it talks to the database that is holding things up. Then it will be configuration of the web app. Past this point you will be working on squeezing the last milliseconds out of your app or looking at providing higher concurrency with the same hardware. For this finer grained performance tuning you need metrics.
For Java I'd suggest installing Java Melody. It can provide a lot of info regarding what your program is doing and help narrow down where it is spending time. I've only used it with Tomcat but should work fine with any Java EE container/servlet thingy.
There are a number of ways you can tune Java, so take a look at their performance guidelines (I'm sure you probably have) and make sure you're setting the correct Heap Size etc. suitable for your program. Java Melody can help you track down the size of Java's heap you're consuming as well as how hard the garbage collector is working/how often it is interrupting your program to clear objects.
I hope that has been helpful. If you provide any more information, I may be able to update this answer and hone it more towards your needs.
With some further investigation, it appears that the performance problem is mostly due to a high number of network calls between two systems (Oracle SSXA and UCM). The calls are quick but plenty and serialized, hence the low CPU usage (mostly waiting for I/O), the high load average (many calls waiting to be processed) and especially the long response times (by accumulation of small response times).
(2) how to understand the load average on the first line ("load average: 14.04, 14.02, 14.00")?
This article on load average uses a nice traffic analogy and is the best one I've found so far:Understanding Linux CPU Load - when should you be worried?. In your case, as people pointed out:
On multi-processor system, the load is relative to the number of processor cores available. The "100% utilization" mark is 1.00 on a single-core system, 2.00, on a dual-core, 4.00 on a quad-core, etc.
So, with a load average of 14.00 and 24 cores, your server is far from being overloaded.
参考:
http://www.linuxjournal.com/article/9001
http://blog.scoutapp.com/articles/2009/07/31/understanding-load-averages
[ lucene扩展 ] spellChecker原理分析 - MR-fox - 博客园
lucene中spellchecker简述
lucene 的扩展包中包含了spellchecker,利用它我们可以方便的实现拼写检查的功能,但是检查的效果(推荐的准确程度)需要开发者进行调整、优化。
lucene实现“拼写检查”的步骤
步骤1:建立spellchecker所需的索引文件
spellchecker也需要借助lucene的索引实现的,只不过其采用了特殊的分词方式和相关度计算方式。
建立spellchecker所需的索引文件可以用文本文件提供内容,一行一个词组,类似于字典结构。
例如(dic.txt):
麻辣烫中文测试麻辣酱麻辣火锅中国人中华人民共和国 |
建立spellchecker索引的关键代码如下:
/** * 根据字典文件创建spellchecker所使用的索引。 * * @param spellIndexPath * spellchecker索引文件路径 * @param idcFilePath * 原始字典文件路径 * @throws IOException */public void createSpellIndex(String spellIndexPath, String idcFilePath) throws IOException { Directory spellIndexDir = FSDirectory.open(new File(spellIndexPath)); SpellChecker spellChecker = new SpellChecker(spellIndexDir); IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_35, null); spellChecker.indexDictionary(new PlainTextDictionary(new File( idcFilePath)), config, false); // close spellIndexDir.close(); spellChecker.close();} |
这里使用了PlainTextDictionary对象,他实现了Dictionary接口,类结构如下图所示:

除了PlainTextDictionary(1 word per line),我们还可以使用:
- FileDictionary(1 string per line, optionally with a tab-separated integer value | 词组之间用tab分隔)
- LuceneDictionary(Lucene Dictionary: terms taken from the given field of a Lucene index | 用现有的index的term建立索引)
- HighFrequencyDictionary(HighFrequencyDictionary: terms taken from the given field of a Lucene index, which appear in a number of documents above a given threshold. | 在LuceneDictionary的基础上加入了一定的限定,term只有出现在各document中的次数满足一定数量时才被spellchecker采用)
例如我们采用luceneDictionary,主要代码如下:
/** * 根据指定索引中的字典创建spellchecker所使用的索引。 * * @param oriIndexPath * 指定原始索引 * @param fieldName * 索引字段(某个字段的字典) * @param spellIndexPath * 原始字典文件路径 * @throws IOException */public void createSpellIndex(String oriIndexPath, String fieldName, String spellIndexPath) throws IOException { IndexReader oriIndex = IndexReader.open(FSDirectory.open(new File( oriIndexPath))); LuceneDictionary dict = new LuceneDictionary(oriIndex, fieldName); Directory spellIndexDir = FSDirectory.open(new File(spellIndexPath)); SpellChecker spellChecker = new SpellChecker(spellIndexDir); IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_35, null); spellChecker.indexDictionary(dict, config, true);} |
我们对dic.txt建立索引后,可以对其内部文档和term进行进一步了解,如下:
Document<stored,indexed,omitNorms,indexOptions=DOCS_ONLY<word:麻辣烫>>Document<stored,indexed,omitNorms,indexOptions=DOCS_ONLY<word:中文测试>>Document<stored,indexed,omitNorms,indexOptions=DOCS_ONLY<word:麻辣酱>>Document<stored,indexed,omitNorms,indexOptions=DOCS_ONLY<word:麻辣火锅>>Document<stored,indexed,omitNorms,indexOptions=DOCS_ONLY<word:中国人>>Document<stored,indexed,omitNorms,indexOptions=DOCS_ONLY<word:中华人民共和国>>end1:人 end1:烫 end1:试 end1:酱 end1:锅 end2:国人 end2:测试 end2:火锅 end2:辣烫 end2:辣酱 end3:共和国 end4:民共和国 gram1:中 gram1:人 gram1:国 gram1:文 gram1:测 gram1:火 gram1:烫 gram1:试 gram1:辣 gram1:酱 gram1:锅 gram1:麻 gram1: gram2:中国 gram2:中文 gram2:国人 gram2:文测 gram2:测试 gram2:火锅 gram2:辣火 gram2:辣烫 gram2:辣酱 gram2:麻辣 gram2:麻 gram3:中华人 gram3:人民共 gram3:共和国 gram3:华人民 gram3:民共和 gram4:中华人民 gram4:人民共和 gram4:华人民共 gram4:民共和国 start1:中 start1:麻 start1: start2:中国 start2:中文 start2:麻辣 start2:麻 start3:中华人 start4:中华人民 word:中华人民共和国 word:中国人 word:中文测试 word:麻辣火锅 word:麻辣酱 word:麻辣烫 |
可以看出,每一个词组(dic.txt每一行的内容)被当成一个document,然后采用特殊的分词方式对其进行分词,我们可以看出field的名称比较奇怪,例如:end1,end2,gram1,gram2等等。
为什么这么做,什么原理?我们先留下这个疑问,看完效果后再说明!
步骤二:spellchecker的“检查建议”
我们使用第一步创建的索引,利用spellChecker.suggestSimilar方法进行拼写检查。全部代码如下:
package com.fox.lab;import java.io.File;import java.io.IOException;import java.util.Iterator;import org.apache.lucene.index.IndexReader;import org.apache.lucene.search.spell.LuceneDictionary;import org.apache.lucene.search.spell.SpellChecker;import org.apache.lucene.store.Directory;import org.apache.lucene.store.FSDirectory;/** * @author huangfox * @createDate 2012-2-16 * @eMail [email protected] */public class DidYouMeanSearcher { SpellChecker spellChecker = null; LuceneDictionary dict = null; /** * * @param spellCheckIndexPath * spellChecker索引位置 */ public DidYouMeanSearcher(String spellCheckIndexPath, String oriIndexPath, String fieldName) { Directory directory; try { directory = FSDirectory.open(new File(spellCheckIndexPath)); spellChecker = new SpellChecker(directory); IndexReader oriIndex = IndexReader.open(FSDirectory.open(new File( oriIndexPath))); dict = new LuceneDictionary(oriIndex, fieldName); } catch (IOException e) { e.printStackTrace(); } } /** * 设定精度,默认0.5 * * @param v */ public void setAccuracy(float v) { spellChecker.setAccuracy(v); } /** * 针对检索式进行spell check * * @param queryString * 检索式 * @param suggestionsNumber * 推荐的最大数量 * @return */ public String[] search(String queryString, int suggestionsNumber) { String[] suggestions = null; try { // if (exist(queryString)) // return null; suggestions = spellChecker.suggestSimilar(queryString, suggestionsNumber); } catch (IOException e) { e.printStackTrace(); } return suggestions; } private boolean exist(String queryString) { Iterator<String> ite = dict.getWordsIterator(); while (ite.hasNext()) { if (ite.next().equals(queryString)) return true; } return false; }} |
测试效果:
package com.fox.lab;import java.io.IOException;public class DidYouMeanMainApp { /** * @param args */ public static void main(String[] args) { // 创建index DidYouMeanIndexer indexer = new DidYouMeanIndexer(); String spellIndexPath = "D:\\spellchecker"; String idcFilePath = "D:\\dic.txt"; String oriIndexPath = "D:\\solrHome\\example\\solr\\data\\index"; String fieldName = "ab"; DidYouMeanSearcher searcher = new DidYouMeanSearcher(spellIndexPath, oriIndexPath, fieldName); searcher.setAccuracy(0.5f); int suggestionsNumber = 15; String queryString = "麻辣将";// try {// indexer.createSpellIndex(spellIndexPath, idcFilePath); // indexer.createSpellIndex(oriIndexPath, fieldName, spellIndexPath); // } catch (IOException e) { // e.printStackTrace(); // } String[] result = searcher.search(queryString, suggestionsNumber); if (result == null || result.length == 0) { System.out.println("我不知道你要什么,或许你就是对的!"); } else { System.out.println("你是不是想找:"); for (int i = 0; i < result.length; i++) { System.out.println(result[i]); } } }} |
输出:
你是不是想找:麻辣酱麻辣火锅麻辣烫 |
将queryString改为“中文测式”,输出:
你是不是想找:中文测试 |
当输入正确时,例如“中文测试”,则输出:
我不知道你要什么,或许你就是对的! |
拼写检查的基本功能实现了,虽然还存在很多问题需要改进调整。我们先来了解其中两个基本原理。
第一原理:N-gram
我们要实现spellchecker,其实简单理解就是将用户输入的词组(英文为单词,中文为词组)和字典里面“标准”的词组进行“相似性”比较,并给出相似程度最高的词组。
那么如何比较两个字符串的相似程度就是spellchecker的关键所在。
字符串P 的N-gram 是P 中任意长度为N 的子串。例如,单词waist 的Bigram 有wa、ai、is 和st 四个。对于给定的字符串P 和W,其N-gram 相似度gram-count(P,W) 定义为同时在P 和W 中出现的N-gram 数目。在lucene的spellchecker中对N-gram进行了扩展,对整个单词、单词的头尾都做了处理,例如:麻辣烤翅,分解成:
start2:麻 start3:麻辣end2:烤翅 end3:辣烤翅gram2:烤翅 gram2:辣烤 gram2:麻辣 gram2:麻 gram3:辣烤翅 gram3:麻辣烤 gram3:麻辣 word:麻辣烤翅 |
当用户输入“麻辣靠翅”时,被分解成:
end2:靠翅 end3:辣靠翅 gram2:靠翅 gram2:辣靠 gram2:麻辣 gram2:麻 gram3:辣靠翅 gram3:麻辣靠 gram3:麻辣 start2:麻 start3:麻辣 word:麻辣靠翅
并将这些term组成一个用OR连接的检索式(不同的term可能赋予不同的权重),在spellchecker的索引里进行检索,即可匹配到文档“麻辣烤翅”。但是不是就要把它推荐(suggest)出来呢?还要看他们的相识度是否符合要求。在lucene的spellchecker中,默认相似度为0.5。
lucene——spellchecker的n-gram分词算法如下:
private static void addGram(String text, Document doc, int ng1, int ng2) { int len = text.length(); for (int ng = ng1; ng <= ng2; ng++) { String key = "gram" + ng; String end = null; for (int i = 0; i < len - ng + 1; i++) { String gram = text.substring(i, i + ng); Field ngramField = new Field(key, gram, Field.Store.NO, Field.Index.NOT_ANALYZED); // spellchecker does not use positional queries, but we want freqs // for scoring these multivalued n-gram fields. ngramField.setIndexOptions(IndexOptions.DOCS_AND_FREQS); doc.add(ngramField); if (i == 0) { // only one term possible in the startXXField, TF/pos and norms aren't needed. Field startField = new Field("start" + ng, gram, Field.Store.NO, Field.Index.NOT_ANALYZED); startField.setIndexOptions(IndexOptions.DOCS_ONLY); startField.setOmitNorms(true); doc.add(startField); } end = gram; } if (end != null) { // may not be present if len==ng1 // only one term possible in the endXXField, TF/pos and norms aren't needed. Field endField = new Field("end" + ng, end, Field.Store.NO, Field.Index.NOT_ANALYZED); endField.setIndexOptions(IndexOptions.DOCS_ONLY); endField.setOmitNorms(true); doc.add(endField); } }} |
第二原理:相似度计算(stringDistance)
在lucene的spellchecker中,StringDistance作为接口,有三个实现类,如下:
- JaroWinklerDistance
- LevensteinDistance
- NGramDistance
我们这里采用LevensteinDistance进行字符串相似度计算。LevensteinDistance就是edit distance(编辑距离)。
编辑距离,又称Levenshtein距离(也叫做Edit Distance),是指两个字串之间,由一个转成另一个所需的最少编辑操作次数。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。
sitten (k→s)
sittin (e→i)
sitting (→g)
俄罗斯科学家Vladimir Levenshtein在1965年提出这个概念。
lucene中算法如下:
public float getDistance (String target, String other) { char[] sa; int n; int p[]; //'previous' cost array, horizontally int d[]; // cost array, horizontally int _d[]; //placeholder to assist in swapping p and d /* The difference between this impl. and the previous is that, rather than creating and retaining a matrix of size s.length()+1 by t.length()+1, we maintain two single-dimensional arrays of length s.length()+1. The first, d, is the 'current working' distance array that maintains the newest distance cost counts as we iterate through the characters of String s. Each time we increment the index of String t we are comparing, d is copied to p, the second int[]. Doing so allows us to retain the previous cost counts as required by the algorithm (taking the minimum of the cost count to the left, up one, and diagonally up and to the left of the current cost count being calculated). (Note that the arrays aren't really copied anymore, just switched...this is clearly much better than cloning an array or doing a System.arraycopy() each time through the outer loop.) Effectively, the difference between the two implementations is this one does not cause an out of memory condition when calculating the LD over two very large strings. */ sa = target.toCharArray(); n = sa.length; p = new int[n+1]; d = new int[n+1]; final int m = other.length(); if (n == 0 || m == 0) { if (n == m) { return 1; } else { return 0; } } // indexes into strings s and t int i; // iterates through s int j; // iterates through t char t_j; // jth character of t int cost; // cost for (i = 0; i<=n; i++) { p[i] = i; } for (j = 1; j<=m; j++) { t_j = other.charAt(j-1); d[0] = j; for (i=1; i<=n; i++) { cost = sa[i-1]==t_j ? 0 : 1; // minimum of cell to the left+1, to the top+1, diagonally left and up +cost d[i] = Math.min(Math.min(d[i-1]+1, p[i]+1), p[i-1]+cost); } // copy current distance counts to 'previous row' distance counts _d = p; p = d; d = _d; } // our last action in the above loop was to switch d and p, so p now // actually has the most recent cost counts return 1.0f - ((float) p[n] / Math.max(other.length(), sa.length)); } |
需要改进的地方
1.精度不高,特别是对于两个字的词组。可以在距离计算(相似度计算)方面进行调整。
2.没有拼音的功能,例如麻辣kao翅,将无法进行校正。
3.对于字符串中出现的错误无法进行校正,例如“常州哪里有卖变态麻辣靠翅”。


