这篇文章将介绍一种处理应用中的类(或库)的重复引用以及版本冲突问题的解决方案。这在应用集成阶段非常有效。适用于如下案例: NoClassDefFoundError:在classpath中有两个不同版本的同名类; 集成大型产品:检查同一个版本的jar包是否被重复引入,减少内存占用。 准备工作 安装Memory Analyzer Tools,在eclipse上搜mat插件、百度搜索、谷歌搜索都可以。 获取heap dump文件,参考这里:《关于Heap Dump》 步骤 使用MAT打开dump文件,执行Open Query browser->Java basics->Duplicated classes,如下图: 现在我们可以看到所有重复的类及相关的类加载器了: 需要注意的是:相关的类必须是被加载过才能找得到。 一个小技巧 既然我们已经做到这里了,顺便给您展示一下另外一个小tip。通过Inspector视图,可以看到被加载的类具体是在哪个jar包里。举例说,如果一个重复的类是被URLClassloader加载的,只需按以下的步骤执行: 选择目标类; 通过类属性页签进入Inspector视图; 右键点击“_context”属性; 最后点击“Go Into”。 在弹出的窗口窗口中,可以看到属性“_war”,然后就是被加载的类的位置了: ###### 本文译自下文:http://community.bonitasoft.com/blog/effective-way-fight-duplicated-libs-and-version-conflicting-classes-using-memory-analyzer-tool
[阅读更多...]-
使用MAT找出重复引用的jar或类
-
在命令行执行JMeter测试计划
有时候会需要在linux的命令行上运行JMeter,简单说一下做法: 在图形界面上创建测试计划,并保存生成的test_plan.jmx文件; 将生成的test_plan.jmx文件上传到linux服务器; 在linux的命令行执行如下指令: 其中-n说明不是在图形界面下执行,-t指示了测试计划文件的位置。 还是写一个简单的示例吧。接下来创建一个HTTP请求的测试计划。 创建测试计划: 添加一个监听器: 这里将监听结果保存到文件。因为没办法直接通过图形界面查看运行结果,需要通过文件查看监听结果。 最后在命令行执行测试计划: 执行结束后可以将执行结果文件拷贝出来,在jmeter中打开查看。
[阅读更多...] -
使用JMeter + PerfMon做远程监控
本文假设已经安装好了JMeter 下载插件 PerfMon并不是JMeter原生的工具。要使用这个工具还需要下载一些插件。下载地址是jmeter-plugins.org。在JMeter Plugins的下载页面可以看到如下需要下载的内容: 下载用红框圈起来的前两个压缩包即可。 下载完成后。将第一个压缩包JMeterPlugins-Standard-1.3.1.zip解压到JMeter的安装目录下。第二个压缩包ServerAgent-2.2.1.zip解压后可以放在要监控的服务器上的任何位置。 使用 将第二个压缩包解压后,目录下有两个启动文件startAgent.sh和startAgent.bat。视平台选择启动文件启动PerfMon。 打开JMeter,添加线程组,在线程组下添加监听器jp@PerfMon Metrics Collector。 然后在PerfMon Metrics Collector中按自己的需求稍作配置即可。 此时照说是应该配置完了。我在官网和其他的地方找到的一些文章也说就这样就可以了。但是坑爹的是,做一下就会发现只这样还是不行的——一运行就会退出。我们还需要添加一个采样器Sampler。什么采样器都行,有了采样器就不会一点运行就退出。 关于怎么使用JMeter这里说的非常简略。想要多了解些可以看一下下文中的参考文档。这里不想反复说一些大家都知道的内容,只是稍作补充并略述心得而已。 最终还是发现PerfMon不是一个好的监控方案。因为CPU的消耗太大,一启动就会占用84%的CPU。用着真心不好,可以替代的方案也太多了,比如jvisualvm。 ######### 参考文档 JMeter Plugin官网: http://jmeter-plugins.org/ JMeter 服务器性能监测插件介绍:http://blog.csdn.net/defonds/article/details/41650813 Jmeter测试工具:http://www.cnblogs.com/zhangchaoyang/articles/2530731.html
[阅读更多...] -
MySQL的索引
为什么要使用索引 使用索引主要是为了加快查询速度。通常影响查询速度的最大的也是索引的正确使用。 一个没有索引的数据表就是一个无序的数据行集合,如果我们要查询数据表的某个数据行,就要检查数据表的每一个数据行,看是否与期望值匹配。如果数据表很大,这个过程就很慢,效率很低。 针对某个(或某些)字段创建的索引中包含了数据表里每一个数据行的对应字段的值。索引中的值是经过分类排序的。使用索引可以得知匹配数据行在什么位置结束,从而跳过其余部分。这也是索引可以提高搜索效率的一个原因。另外一个原因则是定位算法的使用,使用定位算法可以不用从索引开始位置线性扫描就能快速定位到第一个匹配项。关于定位算法目前先不多说,只需要知道这些算法可以加快索引速度,并且索引的确是一个提速的好方法。 索引的存储细节 对于不同的存储引擎,索引的存储细节有所不同。 对于MyIsam数据表来说,数据表的数据行是在数据文件里,而索引值保存在索引文件里。一个数据表可以有多个索引,但如果这样的话,所有的索引都储存在同一个索引文件里。索引文件里的每一个索引都是由分类的关键记录数组组成的,这些数组用于快速访问数据文件。 Innodb存储引擎没有按照上面的方法将数据行和索引值分开。默认情况下,Innodb存储引擎使用的是一个表空间。在这个表空间里,它管理着所有的Innodb类型数据表的数据和索引的存储。虽然可以通过配置让Innodb为每个数据表创建一个自己的表空间,数据表的数据和索引也是保存在同一个表空间文件里的。 使用索引的方式 MySQL使用索引的方式有如下几种: 快速查询,一是在查询操作中把与where子句所给出的条件相匹配的数据行尽快找出来;二是在关联操作中把与其他数据表里的数据行相匹配的数据行尽快找出来; 对于使用MIN()或MAX()函数的查询,如果数据列带索引,那么它的最大值和最小值能够被迅速找到而不用通过逐行检查的方法来查找; MySQL经常使用索引来迅速地完成order by子句和group by子句的排序和分组操作; MySQL可以通过使用索引来避免为一个查询整体读取数据行。比如要从MyIsam数据表的一个有索引的数据列里选取值,而且并不打算选取数据表里的其他数据列。在这种情况下,MySQL从这个索引文件读取索引值时,实际上就已经得到了这个值,而不需要再次读取数据文件。 索引的缺点 一般情况下,使用索引都是最好的提速方案之一。但是使用索引也有一些时间和空间上的缺点: 索引加快了查询速度,但是却降低了在带索引的数据列里插入、删除以及修改数据的速度。这是因为写入一条记录时不仅会写入到数据文件,还会要求维护所有的索引。一个数据表的索引越多,需要做出的调整就越多,平均性能就下降越多。 索引要占据磁盘空间,索引越多,占据的表空间越大,也就容易更快达到数据表的尺寸极限。 有这两方面的缺点,可以得到一个结论:如果不需要某个特定的索引来加快查询速度,就不要创建它。 挑选索引 挑选索引及使用索引可以从如下几个方向来考虑。 1. 尽量为用来搜索、排序或分组的数据列编制索引,不要为作为输出显示的数据列编制索引。换句话说,适合用来作为索引的列是在where子句中、在联结子句中、在order by或者group by子句中出现的数据列。 2. 综合考虑各数据列的维度。数据列的维度等于它所容纳的非重复值的个数。当查询优化程序确定出某一个数值在数据表中出现的频率超过30%时,查询优化程序通常会跳过索引而进行全表扫描。比如标识性别的列就不值得为其创建索引。 3. 为短小的值进行索引。在创建索引时,应尽量选择比较“小”的数据类型。因此在建表时对关键字段类型的选择需要注意。 4. 为字符串值的前缀编索引。假如要为字符串的数据列编索引,应当尽可能给出前缀长度。比如有一个长度为char(200)的数据列,大多数的值的前10个或者前20个字符都是唯一的,那就不用为整个数据列编制索引,仅为前20个或前30个字符编制索引即可。这样不仅可以节省索引空间,且可以使查询进行得更快。 5. 充分利用最左边的前缀。这个是在使用复合索引时需要注意的一点。假使有这样一个学生信息表(学校编号,班级编号,学生编号,学生姓名,性别,电话,联系地址),为其中的学校编号、班级编号、学生编号创建了一个复合索引。索引中的数据行是以“学校编号/班级编号/学生编号”的顺序存储的,因此索引中的记录也是以“学校编号/班级编号”的顺序存储的,自然也是以“学校编号”的顺序存储的。有了这个复合索引后就无需以“学校编号”或“学校编号/班级编号”再创建索引。在这例子中,执行查询时,如果没有在where子句中使用学校编号,那么就无法使用索引。如果查询的是学校编号和学生编号的组合,也只能使用学校编号这个索引,而无法使用复合索引。 6. 让索引类型与打算进行比较操作的类型保持匹配。这是说在挑选索引类型的时候,一定要考虑打算在被索引的数据列上进行什么类型的比较操作。比如散列索引在判断是否相等时速度极快,在进行范围比较如“>、<、>=、<=”或者between/and操作时却表现不佳;B树索引在使用精确比较操作和范围比较操作时都很有效率。如果匹配模式是以一个纯字符串而不是一个通配符开头的话,B树索引还可以使用LIKE操作符进行模式匹配操作。 7. 谨慎使用比较函数。对于某些比较函数(比如STRCMP()),服务器必须为每个数据行计算出函数值,这就排除了使用该数据列上的索引的可能性。 8. 利用慢查询日志找出性能低劣的查询。 #######
[阅读更多...] -
Kafka high level consumer
为什么选择High Level Consumer 很多时候用户只是想从kafka中读取数据,对于如何处理消息的offset则不怎么关注。在抽象了Kafka中消费事件的大部分细节后,High Level Consumer可以让用户使用起来更为简单。 首先需要知道的就是High Level Consumer保存了从zookeeper中读取某个partition最后的offset。这个offset基于读取进程一开始时提供给kafka的名称保存。这个名称可以理解为Consumer Group的名称。 Consumer Group的名称在kafka集群中是一个全局的属性,因此在启动新的Consumer程序前,注意要关掉“旧的”Consumer。当使用已有的Consumer Group名称启动一个新的Consumer的时候,Kafka将会把这个进程的线程添加到一个现有的线程组中来消费Topic,并触发一次“re-balance”。在“re-balance”中,kafka将会把可用的partition分配给可用的线程。这就有可能把一个partition移交给另一个进程。如果同时使用新的和旧的业务处理逻辑,就很有可能把一些消息导向旧的业务处理逻辑。 设计一个High Level Consumer 首先要说明的是使用High Level Consumer的可以(或者说应该)是一个多线程应用。围绕着topic中partition的数量定义的线程模型有如下几个明显的特征: 如果提供的线程数量多于topic的partition的数量,一些线程将永远接收不到任何消息; 如果提供的线程数量少于topic的partition数量,一些线程将会收到来自多个partition的数据; 如果一个线程对应着多个partition,那么接收到的消息的有序性将会得不到保证,除非在partition内部的offset是序列化的。举例说,你可能会从partition10中获取5条消息,从partition11中获取6条消息,那么有可能在你从partition10中获取5条消息后,又继续从partition10中获取了5条消息,尽管此时partition11中的消息是可用的; 添加进程或线程将会导致re-balance,这就有可能会导致线程对应的partition会重新分配。 现在就可以尝试从Kafka集群读取数据了,如果没有新数据的话,读取数据的进程可能会阻塞。 如下是一个非常简单的Kafka High Level Consumer线程实例: 程序中值得关注的部分是“while (it.hasNext()) ”这一句,程序就是通过这一句不停地从Kafka集群读取数据的——直到用户主动停止线程。 配置测试应用 和SimpleConsumer不同的是,High Level Consumer为我们做了大量的信息记录以及故障处理工作,然而我们还是需要告诉Kafka将一些信息存储在哪儿。在下面的方法中,定义了创建一个High Level Consumer最基本的配置信息: 简单说明下这里的配置参数: “zookeeper.connect”指明了如何在kafka集群中找到启动的Zookeeper实例。Kafka使用zookeeper保存了当前ConsumerGroup从指定topic中消费消息的偏移量以及对应partition信息; “group.id”定义了当前进程所代表的Consumer Group; “zookeeper.session.timeout.ms”定义了kafka等待Zookeeper响应请求(读或者写请求)的时间,时间单位是毫秒,如果超过时间,Kafka就会放弃并继续消费消息; “zookeeper.sync.time.ms”表示了没有发生故障时,zookeeper的一个follower和master同步的时间间隔; “auto.commit.interval.ms”定义了多久更新一次写入到zookeeper的消费offset信息。注意,因为这个提交频率是基于时间的而非基于消费的消息的,如果在提交更新时发生了错误,就有可能重新消费消息。 关于配置的更多信息可以查看这里:http://kafka.apache.org/08/configuration.html。 创建线程池 在示例程序中使用java的“java.util.concurrent”包来管理线程,使用这个包可以很方便的创建一个线程池: 首先我们创建了一个Map用来告诉Kafka我们要为目标topic启动多少个线程。我们调用consumer.createMessageStreams方法来传递这个信息给Kafka。这个方法的返回值是一个Map对象,表示了topic和监听topic的KafkaStream的映射关系。注意,我们的示例程序中只向Kafka请求了一个topic,实际上我们可以请求多个topic的信息,只需要在topicCountMap加入对应的信息即可。 最后,我们成功创建了一个线程池,并为每个线程创建了一个ConsumerTest对象作为具体的业务逻辑。 安全退出和错误处理 前面已经提过,Kafka并不会在每次读取消息后就立即更新保存在Zookeeper中的消息offset,而是每隔一段时间更新一次。这就有可能产生一小段延迟,比如我们的程序已经消费了消息,但是实际上此时仍未同步到Zookeeper。如果此时客户端退出了或者崩溃了,那么此前我们消费过的消息可能会再次出现。 还要注意,有时候Broker故障或者其他事件导致的Partition的leader的改变也有可能导致消息的重复消费。 为了避免这种情况的发生,需要尽可能保证安全退出,不要使用“kill -9”这种指令。 在我们的示例程序中,主线程执行到最后sleep了10秒钟。这样后台消费线程就有了10秒钟时间消费stream中的数据。因为已经开启了自动提交,Kafka将会每秒钟提交一次offset。最后,主线程调用了shutdown方法,这个方法会先调用每个消费者线程的shutdown方法,而后才会调用ExecutorService的shutdown方法,最后会等待ExecutorService完成所有未完成的工作。这给了消费者线程一些时间来处理完成仍在stream中的少量未处理消息。如果消费者线程已经处理完了所有来自server的消息,此时关闭消费者线程,Stream的迭代器的hasNext()方法将会返回false。这样消费者线程也可以优雅的退出。另外,如果开启了自动提交,调用消费者线程的consumer方法将会在退出前提交最终的offset。 在实际工作中,通常采用的工作模式是让主线程无限期的睡眠,通过shutdown hook的方式实现安全退出。(有必要了解一下java的hook机制)。 运行示例程序 运行示例程序需要如下命令行参数: 包含端口号的Zookeeper连接字符串; 这次消费进程要使用的Consumer Group名称; 消费的消息所属的Topic; 此次消费进程启动的线程数目。 例如: 这个命令表示将会通过连接主机server01.myco.com1的2181端口与其上的Zookeeper进行通信,请求了名为“myTopic”的Topic的全部partition,并启动了4个线程来消费这些partition上的消息。这个示例中使用的Consumer Group是“group3”。 完整的代码如下(做了折叠): 参考文档 https://cwiki.apache.org/confluence/display/KAFKA/Index https://cwiki.apache.org/confluence/display/KAFKA/Consumer+Group+Example http://www.cnblogs.com/fxjwind/p/3794255.html?utm_source=tuicool&utm_medium=referral http://www.open-open.com/lib/view/open1434551761926.html http://my.oschina.net/ielts0909/blog/110280 http://my.oschina.net/infiniteSpace/blog/312890?p=1 http://www.cnblogs.com/airwindow/archive/2012/06/24/2559754.html ##############################
[阅读更多...] -
Kafka Simple Consumer
Kafka的Simple Consumer并不简单。相反的,它是直接在对Kafka的partition和offset进行操作,是一种比较接近底层的方案。 为什么要使用Simple Consumer 使用SimpleConsumer最主要的原因是想在消费消息时获取更大的权限。比如说要做下面这些事情: 多次读取同一条消息; 在一个处理过程中,只消费topic中partition的子集; 进行事务管理,保证消息被消费了一次且只消费了一次。 使用Simple Consumer的负面影响 较之ConsumerGroup,使用SimpleConsumer需要做大量额外的工作: 在应用中需要跟踪offset以便知道消费到哪里了; 需要指明topic和partition对应的leader Broker; 需要对leader Broker的改变做出应对。 使用SimpleBroker的步骤 找到一个活跃Broker,并找出要消费的Topic和Partition的leader Broker; 决定哪个Broker是要消费的Topic和Partition的副本Broker; 建立请求,并定义要抓取的数据; 抓取数据; 确认并还原leader的变化。 找到一个Topic和Partition的Leader Broker 要找到Leader Broker最简单的解决方案就是传送一组已知的Broker到处理程序中,这可以通过配置信息或者命令行来完成。 这里没必要传递集群中全部的Broker给处理程序,只要提供少量的活跃Broker,而后程序可以通过这些Broker得到Leader Broker的信息。程序如下: 在上面的程序中调用了topicsMetadata()方法,通过这个方法,程序可以向已经连接的Broker请求关于目标topic的全部细节。 对partitionsMetadata进行迭代循环会遍历所有的partitions,直到找到我们想要的partition。一旦我们找了想要的partition,将会立即跳出全部循环。 在代码中后面还记录了topic所有副本所在的broker。如果需要重新找出新的leader这些记录就可以派上用场了。 找到消费起始的offset 现在定义从哪儿开始读取数据。Kafka有两个常量可以派上用场: kafka.api.OffsetRequest.EarliestTime():从日志中找到数据最开始的位置,并从该位置开始读取数据; kafka.api.OffsetRequest.LatestTime():这个只传递新的消息。 假使已经有数据了,第一个方法会从头开始读取历史数据;第二个方法则不会读取历史数据,只读取新数据。 不要假设起始offset是0,因为随着时间推移,分区中的消息可能会被删除。 如果要读取最早的数据,在调用getLastOffset方法时,可以为whichTime赋值为kafka.api.OffsetRequest.EarliestTime();如果要读取最新的数据,可以为whichTime赋值为kafka.api.OffsetRequest.LatestTime()。 错误处理 因为SimpleConsumer不会处理关于Leader Broker的错误,需要写一些代码来解决这个问题: 如果fetchResponse.hasError()返回true,即出现了错误,我们会在日志上记录原因,并关闭consumer,然后尝试找出新的leader。 在这个方法中,我们调用了早些时候定义的findLeader()方法来找出新的leader。如果我们尝试连接的只是topic或partition的一个副本,程序将无法尝试找出新的leader。这样我们也无法再从Broker中读取到需要的数据,然后放弃读取任务并抛出异常退出。 Zookeeper需要一小段时间才能发现leader不存在了并尝试重新指定一个leader,因此处理线程在得不到回复的情况下会先sleep一小段时间。事实上,Zookeeper执行错误恢复的速度非常快,通常不需要sleep等待。 读取数据 最后从partition中读取topic数据并输出: 注意readOffset请求的是在读取消息后需要的下一个offset。这样当程序处理完当前的消息块以后就可以知道从哪里开始继续抓取消息了。 还有一点需要注意,就是在程序中我们特意判断了读取到的offset是否比readOffset的值小。这是操作是有必要的。如果Kafka正在对消息进行压缩,抓取请求将会返回一个完全压缩后的消息块,尽管最开始readOffset返回的值不是这个压缩后的消息块的起始位置。因此,我们之前曾经见到过的消息也有可能会被再次返回。还要注意的是我们请求的fetchSize的长度是100000 bytes。如果此时Kafka的Producer正在大批量写入,这个长度可能就不够,也就有可能返回空的消息集合。在这种情况下,需要调整fetchSize的值,直到不再继续返回空消息集。 最后,我们会持续记录读取的消息的数量。如果在上一次请求中没有读取到任何数据,读取线程将会sleep一秒钟。这样程序就不会在没有数据的情况下还反复向Kafka发起请求。 运行示例程序 运行示例程序需要如下参数: 要读取的消息的最大总数(一个整型值,这样我们的程序不会一直循环下去); 要读取的Topic(一个字符串,比如dmp_xxx); 要读取的Partition(一个整型值,即Partition ID); 一个用来查找Metadata的Broker(Broker IP,如127.0.0.1); Broker监听的端口(如 9092)。 源代码 程序源代码如下,这里我暂时做了折叠: ##########
[阅读更多...] -
MySQL分区 – 限制
这一节主要说明MySQL中对分区的一些限制和不足。 禁止使用的结构 如下MySQL结构禁止在分区表达式中使用: 存储过程、存储函数、UDF(用户自定函数)或者插件; 已声明的变量和用户变量。 算数运算符和逻辑运算符 分区表达式中是允许使用+、-和*这些算术运算符的,但是,运算结果必须是一个整型(Integer)值或NULL值(除非是采用了KEY或LINEAR KEY分区方案); DIV运算函数也是可以使用的,但是“/”运算符却是不被允许的; 位运算符|、&、^、<<、>>和~也不允许在分区表达式中使用。 HANDLER声明 在MySQL5.6中,不支持对分区表使用HANDLER声明。 Server SQL mode 在创建分区表后,修改SQL mode需要慎重,不然有可能会导致脏数据、数据丢失或者分区表不可访问。 数据库服务器sql mode也会影响分区表的数据复制。如果master和slave的sql mode不一致,就有可能会导致master和slave主机上的数据不一致,甚至会导致从master复制数据到slave失败。因此,要尽量让slave主机的sql mode和master保持一致。 对性能的影响 分区操作对性能有如下影响: 文件系统操作: 分区或者重分区的操作依赖于文件系统的支持。这意味着分区的效率也会受到诸如文件系统类型、字符集、硬盘转速、交换区大小、操作系统处理效率以及一些MySQL文件处理参数的影响。最重的是要确定是否已经开启了large_files_support,并为open_files_limit设置恰当的值。如果是MyIsam引擎的话,提高myisam_max_sort_file_size 的值可以提升性能;如果要提升InnoDB分区表的性能可以开启innodb_file_per_table设置。 MyIsam和分区文件描述符: 对于一个MyIsam的分区表,MySQL会为每个打开的分区提供两个文件描述符。这意味着操作一个MyIsam的分区表要比相同的不分区的表需要太多文件描述符,尤其是在执行ALTER TABLE(比如要增加一个分区)操作时(大概会是分区数量的4倍)。这就需要注意open_files_limit是不是设置得太低了。 表锁定: 在一个表上做分区操作时会对表进行写锁定。此时读取数据不会受到影响,要执行的INSERT或UPDATE操作会在分区操作完成后执行。 存储引擎: 在MyIsam引擎的表上执行分区操作、查询或是更新操作通常都会比InnoDB或NDB快一些。 索引和分区调整: 在使用索引提升查询效率这一项上,不分区的表要比分区的表效果明显。但是使用分区本身就可以极大的提升查询效率。 分区的表不支持索引条件推送。 LOAD DATA的性能: 在MySQL5.6中,LOAD DATA使用缓冲来提升性能。需要注意的是,MySQL会为每个分区使用13KB的内存来做这些事情。 分区的最大数量 不使用NDB存储引擎的话,在MySQL5.6.7版本之前,一个表的最大分区数量是1024;在MySQL5.6.7版本以后,一个表的分区数量的上限是8192。这里提到的分区也包括二级分区。 使用NDB存储引擎的话,这个上限受到MySQL集群软件的版本、数据节点的数量、还有一些其它因素的影响。 在创建一个有大量分区的表的时候,有可能会收到这样的错误信息:Got error … from storage engine: Out of resources when opening file。此时可以尝试调高open_files_limit的值。然而,这个还要看操作系统是否支持。此外考虑到其它的因素,在某些情况下,使用大量的(数百个)分区并不能得到更好的性能提升。 不支持查询缓存 分区的表不支持查询缓存。从MySQL5.6开始,遇到对分区表的查询时,查询缓存会自动关闭,而且这个不能手动开启。 每个Partition的KEY缓存 MySQL5.6支持MyIsam分区表的KEY缓存,这个可以使用CACHE_INDEX和LOAD_INDEX_INTO_CACHE声明来实现。 可以为一个、几个或者全部分区定义KEY缓存。一个、几个或者全部分区的索引也会预加载到KEY缓存中。 在InnoDB分区表中不支持外键 使用InnoDB存储引擎的分区表不支持外键。由此可以引申出如下两个声明: 一个InnoDB表中使用了分区后就不会存在外键引用,一个InnoDB表中存在外键引用就不能再被分区; 在InnoDB表中不可能存在指向分区表的外键引用,在InnoDB的分区表中不会存在被外键引用的字段。 ALTER TABLE … ORDER BY 在一个分区表上执行 ALTER TABLE … ORDER BY column 语句的结果是:不会得到全表排序,只是在每个分区里完成了排序。 使用REPLACE 调整主键的影响 在某些情况下,修改一个表的主键是有必要的。但是需要注意,如果是使用了REPLACE 语句调整主键,可能会得到完全不同的结果。 全文索引 分区表不支持全文索引或者是全文检索,即使使用的存储引擎是InnoDB或MyIsam也不行。 空间字段 分区表中不可以有空间数据类型比如POINT或者GEOMETRY的字段。 临时表 临时表不可以被分区。 系统日志表 对系统日志表进行分区也是不可行的。在系统日志表执行 ALTER TABLE … PARTITION BY … 语句会报错。 分区键的数据类型 分区键必须是一个Integer字段或者返回值为Integer的表达式。采用枚举类型字段的表达式也是不可用的。字段或表达式的值也可以是NULL。 这个限制有两个例外: 1. 当采用KEY或者是LINEAR KEY分区方案时,可以使用TEXT或BLOB之外的任意数据类型,因为MySQL内置的key-hash计算函数可以从这些数据类型计算出正确的数据类型。举例,如下的两个SQL语句都是正确的: 2. 当采用RANGE COLUMNS或者LIST COLUMNS分区方案时,可使用字符串、DATE或者是DATETIME作为分区键。 这两个例外都不适用于BLOB或TEXT类型的字段。 子查询 分区键不可以是子查询,就算子查询返回的是一个整型值或者是NULL值。 关于二次分区 二次分区必须采用HASH或者KEY分区方案。只有RANGE和LIST方案的分区可以被二次分区。HASH和KEY方案的分区不可以被二次分区。 不支持延迟操作 使用延迟INSERT DELAYED向分区表中插入记录是不被允许的。执行时会报错。 关于数据目录和索引目录 在分区表上使用 DATA DIRECTORY和INDEX DIRECTORY有如下限制: 表级别的
[阅读更多...] -
MySQL分区 – 概述
引言 MySQL的分区方案可以将一个表中的数据分别保存到不同的位置。用户用来分区的规则被称为分区函数。分区函数可以是模运算函数,数值List集合匹配,内置的hash函数或者线性Hash函数。用户可以按照自己的需要选择分区函数,并为分区函数提供一个参数表达式。这个参数表达式可以是一个字段的值,也可以是面向一个或多个字段的函数运算,也可以是一个或多个字段的值的集合,这取决于用户选择的分区方案。 MySQL的分区方案是一种水平分区方案,也就是说一个表中的行可以被分发到不同的物理分区。MySQL5.6暂时还不支持垂直分区。 要判断使用的MySQL是否支持分区可以在MySQL命令行中使用“SHOW PLUGINS;”语句查询MySQL中的插件。如果查询结果中有partition插件,且Status为Active,那么可以执行分区。这个信息也可以查询表INFORMATION_SCHEMA.PLUGINS得到。 分区和存储引擎 在创建分区表的时候,可以使用MySQL支持的大部分存储引擎。MySQL的分区引擎运行在一个独立的层面上,可以与任何存储引擎自由交互。在MySQL5.6中,一个分区表的所有分区的存储引擎必须是一致的。举个例子:对于同一个表,不允许这个表的一个分区的引擎是MyIsam,而另一个分区的引擎是Innodb。 在使用MERGE, CSV, 或者FEDERATED存储引擎的时候,不可以使用分区。 NDB存储引擎只支持KEY或LINEAR KEY分区方案。 在建表语句中,存储引擎的声明要在分区声明语句前面。如下: 需要注意的点 分区作用于一个表的全部数据上,不可能只对数据分区而不对索引分区,反过来也是的。同样,也不可能只对一个表的部分数据进行分区。 在建表时,可以在分区声明语句中使用DATA DIRECTORY和INDEX DIRECTORY将数据或索引保存到指定的目录。如果分区表使用的是MyIsam引擎,DATA DIRECTORY和INDEX DIRECTORY在windows系统上的表并不适用。如果使用的是Innodb引擎,则全平台都可以使用。 在一个表的分区表达式中使用的字段必须是这个表的全部唯一键(包括主键)的一部分。这就意味着使用如下语句创建的表不能被分区: 因为主键pk和唯一键uk并没有交集,这就意味着没有任何字段可以用在分区表达式中。 补充一点,如果表中没有主键或唯一键,可以取任意字段用在分区表达式中。 此外,可以使用MAX_ROWS和MIN_ROWS来限制每个分区中保存的记录的最大数量和最小数量。 使用分区的优势 使用分区有如下优势: 可以突破硬盘或文件系统分区的限制,存储更多的数据; 采用删除分区的方式可以轻松地从一个分区表中删除无用的数据,同样也可以创建新的分区来保存新的数据; 在执行查询时,如果在where子句中查询的记录保存在一个或少数几个分区上,查询的效率将会得到极大的提升;MySQL5.6也支持在查询语句中指定要查询的分区,如 还有一些关于分区的好处,MySQL现在并不支持,但是是可以期待的: 在使用聚集函数时,函数的运算可以在同时在几个分区上并发执行; 可以同时从多个硬盘检索数据,得到更大的吞吐量。
[阅读更多...] -
在Ubuntu上安装windows字体
在Ubuntu上安装字体多少有些麻烦,所以以前想安装却总懒得安装。终于找到了一个简单的方法,在这里记录一下。 将windows的字体拷贝到Ubuntu系统内。 在应用中心安装font manager。 打开后点击左下角灰色的小齿轮图标,选择拷贝出来的字体即可以安装。 ###
[阅读更多...] -
Kafka Producer程序示例
这次是要写一个Producer示例程序。使用的kafka版本是0.8.2。开发语言是java。 程序主体是一个Producer类。这个Producer类主要是用来为指定的topic创建消息。 首先需要引入一些支持类: 然后就是定义一些属性来告诉Producer如何找到Kafka集群、怎样对消息进行序列化以及怎样恰当地引导消息到指定的分区。这些属性是以Java Properties对象的形式定义的: 第一个属性“metadata.broker.list”定义了一个或者多个broker。Producer会为每个topic选择一个broker作为Leader。没必要将集群中所有的broker都添加到这个属性中,但是建议最少设置两个,以防止第一个broker不可用。不用考虑Kafka如何指明哪个broker作为topic(和partition)的Leader,kafka知道怎样与broker建立连接、请求元数据,并最终连接到正确的Broker。 第二个属性“serializer.class”定义了在准备传递消息给Broker时要使用的序列化类。在我们的示例程序中使用了Kafka提供的一个简单的序列化类StringEncoder。注意这里使用的encoder必须能够处理下一步在KeyedMessage中定义的类型。 也可以单独调整消息的Key使用的序列化类,这个可以通过恰当地定义“key.serializer.class”来实现。默认情况下,这个属性和“serializer.class”的值一致。 第三个属性“partitioner.class”定义了要使用哪个类来判断将消息发送给Topic中的哪个Partition。这个属性是可选的。不过在一些特殊的应用中,用户可能想自定义实现一个partition方案。稍后再讨论如何实现Partition。如果消息的key值不为null,可是又没有定义一个“partitioner.class”属性,Kafka将会使用默认的partitioner。如果key值为null,Producer将会把消息分配给一个随机的Partition。 最后一个属性“request.required.acks”告诉Kafka您希望broker在接收到消息后能发送一个确认信号给您的Producer。不设置这个属性的话,Producer将会“fire and forget(放弃并遗忘)”(消息),这有可能会导致数据的丢失。要了解更多,可以参考这个网页:http://kafka.apache.org/08/configuration.html。 然后就是定义Producer对象: 这里使用了java的泛型,您需要指明Producer两个参数的类型。第一个参数就是Partition的key的类型,第二个是消息的类型。在这个示例程序中,这两个属性都是String类型。还需要注意这两个属性需要和前面定义的的配置属性“serializer.class”和“partitioner.class”呼应。 现在开始构建要发送的消息: 这里我们伪造了一系列网站访问的IP信息。将消息以逗号分隔,第一部分是事件发生时的时间戳,第二部分是网址,第三部分是请求来源IP。在这里,我们使用java Random类来保证IP地址的最后八位字节不同,以便我们观察Partitioner是怎样工作的。 然后将消息发送到broker: “page_visits”就是将要把消息写入的Topic。这里我们将IP作为partition的key。需要注意,如果不设置partition的key的话,即使已经定义了一个Partitioner类,Kafka仍然会将消息分配给一个随机的partition。 完整的代码如下: 自定义的Partitioner类: 在自定义的Partitioner类中,我们使用IP地址作为key。我们取出IP地址的最后八位字节与Kafka中topic的partition总数进行模运算。这种partition方案的好处就是所有相同IP的访问记录都会被放置到相同的partition。此外,Consumer处理逻辑也要知道怎样对之进行处理。 在运行我们的程序前,需要确认是否创建了名为”page_visits”的Topic。新建topic的指令如下: 就这样. 参考文档 https://cwiki.apache.org/confluence/display/KAFKA/0.8.0+Producer+Example ######
[阅读更多...]