一、B+树javascript
咱们常常听到B+树就是这个概念,用这个树的目的和红黑树差很少,也是为了尽可能保持树的平衡,固然红黑树是二叉树,但B+树就不是二叉树了,节点下面能够有多个子节点,数据库开发商会设置子节点数的一个最大值,这个值不会过小,因此B+树通常来讲比较矮胖,而红黑树就比较瘦高了。
关于B+树的插入,删除,会涉及到一些算法以保持树的平衡,这里就不详述了。ORACLE的默认索引就是这种结构的。
若是常常须要同时对两个字段进行AND查询,那么使用两个单独索引不如创建一个复合索引,由于两个单独索引一般数据库只能使用其中一个,而使用复合索引由于索引自己就对应到两个字段上的,效率会有很大提升。html
二、散列索引java
第二种索引叫作散列索引,就是经过散列函数来定位的一种索引,不过不多有单独使用散列索引的,反而是散列文件组织用的比较多。
散列文件组织就是根据一个键经过散列计算把对应的记录都放到同一个槽中,这样的话相同的键值对应的记录就必定是放在同一个文件里了,也就减小了文件读取的次数,提升了效率。
散列索引呢就是根据对应键的散列码来找到最终的索引项的技术,其实和B树就差很少了,也就是一种索引之上的二级辅助索引,我理解散列索引都是二级或更高级的稀疏索引,不然桶就太多了,效率也不会很高。mysql
三、位图索引程序员
位图索引是一种针对多个字段的简单查询设计一种特殊的索引,适用范围比较小,只适用于字段值固定而且值的种类不多的状况,好比性别,只能有男和女,或者级别,状态等等,而且只有在同时对多个这样的字段查询时才能体现出位图的优点。
位图的基本思想就是对每个条件都用0或者1来表示,若有5条记录,性别分别是男,女,男,男,女,那么若是使用位图索引就会创建两个位图,对应男的10110和对应女的01001,这样作有什么好处呢,就是若是同时对多个这种类型的字段进行and或or查询时,可使用按位与和按位或来直接获得结果了。web
为了进一步榨取MySQL的效率,就要考虑创建组合索引。就是将 name, city, age建到一个索引里:算法
ALTER TABLE mytable ADD INDEX name_city_age (name(10),city,age);
建表时,usernname长度为 16,这里用 10。这是由于通常状况下名字的长度不会超过10,这样会加速索引查询速度,还会减小索引文件的大小,提升INSERT的更新速度。spring
若是分别在 usernname,city,age上创建单列索引,让该表有3个单列索引,查询时和上述的组合索引效率也会大不同,远远低于咱们的组合索引。虽然此时有了三个索引,但MySQL只能用到其中的那个它认为彷佛是最有效率的单列索引。sql
创建这样的组合索引,实际上是至关于分别创建了下面三组组合MySQL数据库索引:数据库
CREATE TABLE mytable( ID INT NOT NULL, username VARCHAR(16) NOT NULL, city VARCHAR(50) NOT NULL, age INT NOT NULL );
usernname,city,age usernname,city usernname 为何没有 city,age这样的组合索引呢?这是由于MySQL组合索引“最左前缀”的结果。简单的理解就是只从最左面的开始组合。并非只要包含这三列的查询都会用到该组合索引,下面的几个SQL就会用到这个组合MySQL数据库索引:
SELECT * FROM mytable WHREE username="admin" AND city="郑州" SELECT * FROM mytable WHREE username="admin"
而下面几个则不会用到:
SELECT * FROM mytable WHREE age=20 AND city="郑州" SELECT * FROM mytable WHREE city="郑州"
使用索引的代价
一、索引须要占用数据表之外的物理存储空间
二、建立索引和维护索引要花费必定的时间
三、当对表进行更新操做时,索引须要被重建,这样下降了数据的维护速度
什么状况下不适合创建索引?
1.对于在查询过程当中不多使用或参考的列,不该该建立索引。
2.对于那些只有不多数据值的列,不该该建立索引。
3.对于那些定义为image,text和bit数据类型的列,不该该建立索引。
4.当修改性能远大于检索性能,不该该创建索引。
(1) 直接提交的队列:该功能由SynchronizedQueue对象提供。SynchronizedQueue是一个特殊的阻塞队列。SynchronizedQueue没有容量,每个插入操做都要等待一个相应的删除操做,反之每个删除操做都须要等待对应的插入操做。使用SynchronizedQueue时提交的任务不会被真实的保存,而老是将新任务提交给线程执行,若是没有空闲的线程则尝试建立新的线程,若是线程数量达到最大值就执行决绝策略。使用SynchronizedQueue队列一般要设置很大的maxnumPoolSize,不然很容易执行拒绝策略。能够当作大小为0的队列来理解。
(2) 有界的任务队列:有界的任务队列可使用ArrayBlockingQueue实现。当使用有界的任务队列时,如有新的任务须要执行,若是线程池的实际线程数小于核心线程数,则有优先建立新的线程,若大于核心线程数,则会将新任务加入等待队列。若队列已满,没法加入则在总线程数不大于最大线程数的前提下,建立新的线程。若大于最大线程数,则执行拒绝策略。也就是说,有界队列仅当任务队列满时才可能将线程数提高到核心线程数只上,不然确保线程数维持在核心线程数大小。
(3) 无界任务队列:无界任务队列能够经过LinkedBlockingQueue类来实现。与有界队列相比,除非系统资源耗尽,不然无界队列不存在任务入队失败的状况。当有新的任务到来,系统的线程数小于核心线程数时线程池会生成新的线程执行任务,但当系统线程数大于核心线程数后,就不会继续增长。若后续有新的任务,则将任务放入无界队列中等待。
(4) 优先任务队列:优先任务队列是带有执行优先级的队列,经过PriorityBlockingQueue实现,能够控制任务的执行前后顺序,是一个特殊的无界队列。不管是有界队列还ArrayBlockingQueue仍是未指定大小的无界队列LinkedBlockingQueue,都是按照先进先出算法处理任务的,而有限队列则能够根据任务自身的优先级顺序执行,在确保系统性能的同时,也能有很好的质量保证。
回过头看Executor框架提供了几种线程池,newFixedThreadPool()返回的固定大小线程池中核心线程数和最大线程数同样,而且使用了无界队列。由于对于固定大小的线程池来讲,不存在线程数量的动态变化,因此最大线程数等于核心线程数。同时,使用无界队列存放没法当即执行的任务,当任务提交很是频繁时,队列可能迅速膨胀,从而耗尽系统资源。
newSingleThreadExecutor()返回的单线程线程池,是固定大小线程池的一种退化,只是简单的将线程池数量设置为1。
newCachedThreadExecutor()返回核心线程数为0,最大线程数为无穷大的线程池。使用直接提交队列SynchronizedQueue。
当Executor提供的线程池不知足使用场景时,则须要使用自定义线程池,选择合适的任务队列来做为缓冲。不一样的并发队列对系统和性能的影响均不一样。
思路一:
初看题目,最容易想到的方法就是遍历。首先遍历一遍单链表,得出整个链表的长度n(元素个数从1到n),而后找到倒数第k个元素的位置n-k+1,接着从头遍历到第n-k+1元素,就是倒数第k个元素。可是该方法须要对链表进行两次遍历,遍历的元素个数为n+n-k+1=2n+1-k个。
思路二:
有了思路一的提示,是否是能够想到用两个指针,让它们之间的距离保持为k-1,同时对链表进行遍历,当第一个指针到达链表的最后一个元素(即倒数第一个元素时),第二个指针恰好停留在倒数第k个元素上。此方法看似对链表进行了一次遍历,实际上是用两个指针对链表进行了同时遍历,对链表自己而言,它被遍历的元素个数还是n+n-k+1=2n+1-k个。
思路三:
思路一和思路二是两种不一样思路,但就本质而言,都是两次对链表进行2次遍历,一次遍历n个元素,另外一次遍历n-k+1个,总共遍历2n+1-k个元素。此时,想一想可否再减小遍历的元素个数而找到倒数第k个元素呢?咱们注意到思路二,是用两个指针,保持k-1个元素的距离同时进行遍历的,能否按着每次k个元素这样的遍历下去呢。这样遍历的结果就是,每次遍历k个元素,遍历m次(m=n/k),最后一次遍历的个数为i个(i=n%k),咱们只需记录最后一次遍历k个元素的起始位置,而后再遍历i个元素,此时的位置即为倒数第k个元素。此时,对链表遍历的元素个数为n+i(i为n除以k的余数)。
一、将给定的工做量划分给过多的线程会形成每一个线程的工做量过少,所以可能致使线程启动和终止时的开销比程序实际工做的开销还要多;
二、过多并发线程的存在将致使共享有限硬件资源的开销增大。
线程相对于进程的优势:
一、开销小
二、资源共享性好。
线程相对于进程的缺点:
一、共享资源须要耗费必定的锁资源,同步相对复杂。
二、一个线程崩溃可能致使整个进程崩溃,这个固然是本身的应用程序有问题
递归与迭代都是基于控制结构:迭代用重复结构,而递归用选择结构。
递归与迭代都涉及重复:迭代显式使用重复结构,而递归经过重复函数调用实现重复。
递归与迭代都涉及终止测试:迭代在循环条件失败时终止,递归在遇到基本状况时终止。
使用计数器控制重复的迭代和递归都逐渐到达终止点:迭代一直修改计数器,直到计数器值使循环条件失败;递归不断产生最初问题的简化副本,直到达到基本状况。迭代和递归过程均可以无限进行:若是循环条件测试永远不变成false,则迭代发生无限循环;若是递归永远没法回推到基本状况,则发生无穷递归。
递归函数是经过调用函数自身来完成任务,并且在每次调用自身时减小任务量。而迭代是循环的一种形式,这种循环不是由用户输入而控制,每次迭代步骤都必须将剩余的任务减小;也就是说,循环的每一步都必须执行一个有限的过程,并留下较少的步骤。
相同点:
1.truncate和不带where子句的delete、以及drop都会删除表内的数据。
2.drop、truncate都是DDL语句(数据定义语言),执行后会自动提交。
不一样点:
truncate 和 delete 只删除数据不删除表的结构(定义)
语句将删除表的结构被依赖的约束(constrain)、触发器(trigger)、索引(index);依赖于该表的存储过程/函数将保留,可是变为 invalid 状态。
delete 语句是数据库操做语言(dml),这个操做会放到 rollback segement 中,事务提交以后才生效;若是有相应的 trigger,执行的时候将被触发。
truncate、drop 是数据库定义语言(ddl),操做当即生效,原数据不放到 rollback segment 中,不能回滚,操做不触发 trigger。
3.delete 语句不影响表所占用的 extent,高水线(high watermark)保持原位置不动
drop 语句将表所占用的空间所有释放。
truncate 语句缺省状况下见空间释放到 minextents个 extent,除非使用reuse storage;truncate 会将高水线复位(回到最开始)。
4.速度,通常来讲: drop> truncate > delete
5.安全性:当心使用 drop 和 truncate,尤为没有备份的时候.不然哭都来不及
使用上,想删除部分数据行用 delete,注意带上where子句. 回滚段要足够大.
想删除表,固然用 drop;
想保留表而将全部数据删除,若是和事务无关,用truncate便可。若是和事务有关,或者想触发trigger,仍是用delete。
若是是整理表内部的碎片,能够用truncate跟上reuse stroage,再从新导入/插入数据。
6.delete是DML语句,不会自动提交。drop/truncate都是DDL语句,执行后会自动提交。
七、TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同:两者均删除表中的所有行。但 TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少。DELETE 语句每次删除一行,并在事务日志中为所删除的每行记录一项。TRUNCATE TABLE 经过释放存储表数据所用的数据页来删除数据,而且只在事务日志中记录页的释放。
八、TRUNCATE TABLE 删除表中的全部行,但表结构及其列、约束、索引等保持不变。新行标识所用的计数值重置为该列的种子。若是想保留标识计数值,请改用 DELETE。若是要删除表定义及其数据,请使用 DROP TABLE 语句。
九、对于由 FOREIGN KEY 约束引用的表,不能使用 TRUNCATE TABLE,而应使用不带 WHERE 子句的 DELETE 语句。因为 TRUNCATE TABLE 不记录在日志中,因此它不能激活触发器。
十、TRUNCATE TABLE 不能用于参与了索引视图的表。
一、Index索引
二、少用SELECT *
可能有的人查询数据库时,遇到要查询的都会select,这是不恰当的行为。咱们应该取咱们要用的数据,而不是全取,由于当咱们select时,会增长web服务器的负担,增长网络传输的负载,查询速度天然就降低 。
三、开启查询缓存
大多数的MySQL服务器都开启了查询缓存。这是提升性最有效的方法之一,并且这是被MySQL的数据库引擎处理的。当有不少相同的查询被执行了屡次的时候,这些查询结果会被放到一个缓存中,这样,后续的相同的查询就不用操做表而直接访问缓存结果了。
四、使用NOT NULL
不少表都包含可为NULL(空值)的列,即便应用程序并不须要保存 NULL 也是如此 ,这是由于可为NULL是列的默认属性。一般状况下最好指定列为 NOT NULL,除非真的须要存储NULL值。若是查询中包含可为NULL的列,对 MySQL 来讲更难优化 ,由于可为 NULL 的列使 得索引、索引统计和值比较都更复杂 。可为NULL 的列会使用更多的存储空间 ,在MySQL里也须要特殊处理 。当可为NULL 的列被索引肘,每一个索引记录须要一个额 外的字节,在 MyISAM 里甚至还可能致使固定大小 的索引 (例如只有一个整数列的 索引) 变成可变大小的索引。
一般把可为 NULL 的列改成 NOT NULL 带来的性能提高比较小 ,因此 (调优时) 没有 必要首先在现有schema中查找井修改掉这种状况 ,除非肯定这会致使问题。可是, 若是计划在列上建索引 ,就应该尽可能避免设计成可为 NULL 的列。固然也有例外 ,例如值得一提的是,InnoDB 使用单独的位 (bit ) 存储 NULL 值 ,所 以对于稀疏数据由有很好的空间效率 。但这一点不适用于MyISAM 。
五、避免在 where 子句中使用 or 来链接
若是一个字段有索引,一个字段没有索引,将致使引擎放弃使用索引而进行全表扫描,如:
select id from t where num=10 or Name = 'admin'
能够这样查询:
select id from t where num = 10 union all select id from t where Name = 'admin'
六、多使用varchar/nvarchar
使用varchar/nvarchar代替 char/nchar ,由于首先变长字段存储空间小,能够节省存储空间,其次对于查询来讲,在一个相对较小的字段内搜索效率显然要高些。
七、避免大数据量返回
这里要考虑使用limit,来限制返回的数据量,若是每次返回大量本身不须要的数据,也会下降查询速度。
八、where子句优化
where 子句中使用参数,会致使全表扫描,由于SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然 而,若是在编译时创建访问计划,变量的值仍是未知的,于是没法做为索引选择的输入项。
应尽可能避免在 where 子句中对字段进行表达式操做,避免在where子句中对字段进行函数操做这将致使引擎放弃使用索引而进行全表扫描。不要在 where 子句中的“=”左边进行函数、算术运算或其余表达式运算,不然系统将可能没法正确使用索引。
ResultSet executeQuery(String sql); 执行SQL查询,并返回ResultSet 对象。
int executeUpdate(String sql); 可执行增,删,改,返回执行受到影响的行数。
boolean execute(String sql); 可执行任何SQL语句,返回一个布尔值,表示是否返回ResultSet 。
execute是executeQuery和executeUpdate的综合.
executeUpdate() 这是 PreparedStatement 接口中的方法
executeUpdate(String sql) 这是 PreparedStatement 从父接口 Statement 中继承过来的方法
executeUpdate() 中执行 SQL 语句须要在建立 PerparedStatement 时经过 Connection 的 prepareStatement(String sql) 方法中写出,由于 PerparedStatement 中的 SQL 语句数据库须要进行预编译和缓存,所以要在建立 PerparedStatement 对象时给出 SQL 语句。
而 executeUpdate(String sql) 是 Statement 中的方法,参数中的 SQL 语句只是提交给数据库去执行,并不须要预编译。
<font color=red>若是 SQL 语句中有 ? 占位符,那么在设置好占位符中的值后,必须使用 executeUpdate() 执行。而 executeUpdate(String sql) 只是提交一个 SQL 语句,且这个语句中不能带有 ? 占位符。
一、在Java中如何使用execute()、executeQuery()、executeUpdate()三个方法?
execute(String sql)
执行给定的 SQL 语句,该语句可能返回多个结果。
executeQuery(String sql)
执行给定的 SQL 语句,该语句返回单个 ResultSet 对象
executeUpdate(String sql)
执行给定 SQL 语句,该语句可能为 INSERT、UPDATE 或 DELETE 语句,或者不返回任何内容的 SQL 语句(如 SQL DDL 语句)
头2种通常在查询中使用
最后一个在插入、更新、删除时使用
二、executeQuery()是干什么用的?实现什么功能啊?
使用JDBC链接数据库须要四步,第一步加载驱动程序;第二步,链接数据库;第三步,访问数据库;第四步,执行查询;其中在第四步执行查询时,要用statement类的executeQuery()方法来下达select指令以查询数据库,executeQuery()方法会把数据库响应的查询结果存放在ResultSet类对象中供咱们使用。即语句:String sql="select * from"+tableName; ResultSet rs=s.executeQuery(sql);
三、executeQuery、executeUpdate或execute方法区别?
在用纯JSP作一个页面报警功能的时候习惯性的用executeQuery来执行SQL语句,结果执行update时就遇到问题,语句能执行,但返回结果出现问题,另外还忽略了executeUpdate的返回值不是结果集ResultSet,而是数值!特收藏以下一篇文章(感谢网友们对各类信息的贡献):
JDBCTM中Statement接口提供的execute、executeQuery和executeUpdate之间的区别
Statement 接口提供了三种执行 SQL 语句的方法:executeQuery、executeUpdate 和 execute。使用哪个方法由 SQL 语句所产生的内容决定。
方法executeQuery
用于产生单个结果集的语句,例如 SELECT 语句。 被使用最多的执行 SQL 语句的方法是 executeQuery。这个方法被用来执行 SELECT 语句,它几乎是使用最多的 SQL 语句。
方法executeUpdate
用于执行 INSERT、UPDATE 或 DELETE 语句以及 SQL DDL(数据定义语言)语句,例如 CREATE TABLE 和 DROP TABLE。INSERT、UPDATE 或 DELETE 语句的效果是修改表中零行或多行中的一列或多列。executeUpdate 的返回值是一个整数,指示受影响的行数(即更新计数)。对于 CREATE TABLE 或 DROP TABLE 等不操做行的语句,executeUpdate 的返回值总为零。
使用executeUpdate方法是由于在 createTableCoffees 中的 SQL 语句是 DDL (数据定义语言)语句。建立表,改变表,删除表都是 DDL 语句的例子,要用 executeUpdate 方法来执行。你也能够从它的名字里看出,方法 executeUpdate 也被用于执行更新表 SQL 语句。实际上,相对于建立表来讲,executeUpdate 用于更新表的时间更多,由于表只须要建立一次,但常常被更新。
方法execute:
用于执行返回多个结果集、多个更新计数或两者组合的语句。由于多数程序员不会须要该高级功能
execute方法应该仅在语句能返回多个ResultSet对象、多个更新计数或ResultSet对象与更新计数的组合时使用。当执行某个已存储过程 或动态执行未知 SQL 字符串(即应用程序程序员在编译时未知)时,有可能出现多个结果的状况,尽管这种状况不多见。
由于方法 execute 处理很是规状况,因此获取其结果须要一些特殊处理并不足为怪。例如,假定已知某个过程返回两个结果集,则在使用方法 execute 执行该过程后,必须调用方法 getResultSet 得到第一个结果集,而后调用适当的 getXXX 方法获取其中的值。要得到第二个结果集,须要先调用 getMoreResults 方法,而后再调用 getResultSet 方法。若是已知某个过程返回两个更新计数,则首先调用方法 getUpdateCount,而后调用 getMoreResults,并再次调用 getUpdateCount。
对于不知道返回内容,则状况更为复杂。若是结果是 ResultSet 对象,则方法 execute 返回 true;若是结果是 Java int,则返回 false。若是返回 int,则意味着结果是更新计数或执行的语句是 DDL 命令。在调用方法 execute 以后要作的第一件事情是调用 getResultSet 或 getUpdateCount。调用方法 getResultSet 能够得到两个或多个 ResultSet 对象中第一个对象;或调用方法 getUpdateCount 能够得到两个或多个更新计数中第一个更新计数的内容。
当 SQL 语句的结果不是结果集时,则方法 getResultSet 将返回 null。这可能意味着结果是一个更新计数或没有其它结果。在这种状况下,判断 null 真正含义的惟一方法是调用方法 getUpdateCount,它将返回一个整数。这个整数为调用语句所影响的行数;若是为 -1 则表示结果是结果集或没有结果。若是方法 getResultSet 已返回 null(表示结果不是 ResultSet 对象),则返回值 -1 表示没有其它结果。也就是说,当下列条件为真时表示没有结果(或没有其它结果):
((stmt.getResultSet() == null) && (stmt.getUpdateCount() == -1))
若是已经调用方法 getResultSet 并处理了它返回的 ResultSet 对象,则有必要调用方法 getMoreResults 以肯定是否有其它结果集或更新计数。若是 getMoreResults 返回 true,则须要再次调用 getResultSet 来检索下一个结果集。如上所述,若是 getResultSet 返回 null,则须要调用 getUpdateCount 来检查 null 是表示结果为更新计数仍是表示没有其它结果。
当 getMoreResults 返回 false 时,它表示该 SQL 语句返回一个更新计数或没有其它结果。所以须要调用方法 getUpdateCount 来检查它是哪种状况。在这种状况下,当下列条件为真时表示没有其它结果:
((stmt.getMoreResults() == false) && (stmt.getUpdateCount() == -1))
下面的代码演示了一种方法用来确认已访问调用方法 execute 所产生的所有结果集和更新计数:
stmt.execute(queryStringWithUnknownResults);
while (true) {
int rowCount = stmt.getUpdateCount();
if (rowCount > 0) { // 它是更新计数
System.out.println("Rows changed = " + count);
stmt.getMoreResults();
在传统的Java应用中,Bean的生命周期很是简单。Java的关键词new用来实例化Bean(或许他是非序列化的)。这样就够用了。相反,Bean 的生命周期在spring容器中更加细致。理解Spring Bean的生命周期很是重要,由于你或许要利用Spring提供的机会来订制Bean的建立过程。
1.容器寻找Bean的定义信息而且将其实例化。
2.使用依赖注入,Spring按照Bean定义信息配置Bean的全部属性。
3.若是Bean实现了BeanNameAware接口,工厂调用Bean的setBeanName()方法传递Bean的ID。
4.若是Bean实现了BeanFactoryAware接口,工厂调用setBeanFactory()方法传入工厂自身。
5.若是BeanPostProcessor和Bean关联,那么它们的postProcessBeforeInitialzation()方法将被调用。
6.若是Bean指定了init-method方法,它将被调用。
7.最后,若是有BeanPsotProcessor和Bean关联,那么它们的postProcessAfterInitialization()方法将被调用。
到这个时候,Bean已经能够被应用系统使用了,而且将被保留在Bean Factory中知道它再也不须要。有两种方法能够把它从Bean Factory中删除掉。
1.若是Bean实现了DisposableBean接口,destory()方法被调用。
2.若是指定了订制的销毁方法,就调用这个方法。
Bean在Spring应用上下文的生命周期与在Bean工厂中的生命周期只有一点不一样,惟一不一样的是,若是Bean实现了ApplicationContextAwre接口,setApplicationContext()方法被调用。
MySQL Hash索引结构的特殊性,其检索效率很是高,索引的检索能够一次定位,不像B-Tree 索引须要从根节点到枝节点,最后才能访问到页节点这样屡次的IO访问,因此 Hash 索引的查询效率要远高于 B-Tree 索引。
可能不少人又有疑问了,既然 Hash 索引的效率要比 B-Tree 高不少,为何你们不都用 Hash 索引而还要使用 B-Tree 索引呢?任何事物都是有两面性的,Hash 索引也同样,虽然 Hash 索引效率高,可是 Hash 索引自己因为其特殊性也带来了不少限制和弊端,主要有如下这些。
(1)MySQL Hash索引仅仅能知足"=","IN"和"< >"查询,不能使用范围查询。
因为 MySQL Hash索引比较的是进行 Hash 运算以后的 Hash 值,因此它只能用于等值的过滤,不能用于基于范围的过滤,由于通过相应的 Hash 算法处理以后的 Hash 值的大小关系,并不能保证和Hash运算前彻底同样。
(2)MySQL Hash索引没法被用来避免数据的排序操做。
因为 MySQL Hash索引中存放的是通过 Hash 计算以后的 Hash 值,并且Hash值的大小关系并不必定和 Hash 运算前的键值彻底同样,因此数据库没法利用索引的数据来避免任何排序运算;
(3)MySQL Hash索引不能利用部分索引键查询。
对于组合索引,Hash 索引在计算 Hash 值的时候是组合索引键合并后再一块儿计算 Hash 值,而不是单独计算 Hash 值,因此经过组合索引的前面一个或几个索引键进行查询的时候,Hash 索引也没法被利用。
(4)MySQL Hash索引在任什么时候候都不能避免表扫描。
前面已经知道,Hash 索引是将索引键经过 Hash 运算以后,将 Hash运算结果的 Hash 值和所对应的行指针信息存放于一个 Hash 表中,因为不一样索引键存在相同 Hash 值,因此即便取知足某个 Hash 键值的数据的记录条数,也没法从 Hash 索引中直接完成查询,仍是要经过访问表中的实际数据进行相应的比较,并获得相应的结果。
(5)MySQL Hash索引遇到大量Hash值相等的状况后性能并不必定就会比B-Tree索引高。
对于选择性比较低的索引键,若是建立 Hash 索引,那么将会存在大量记录指针信息存于同一个 Hash 值相关联。这样要定位某一条记录时就会很是麻烦,会浪费屡次表数据的访问,而形成总体性能低下。
单例的特色:
一、保证某类只存在惟一实例。
二、该类自己完成自身的初始化。
三、获取该惟一实例的方式很是明确,能够经过该类自己定义的静态方法getInstance()获取该类的惟一实例引用。
静态变量定义某类的实例引用特色:
一、该类的实例引用的静态变量可定义在任何文档类当中。
二、获取该类的实例引用的静态变量,能够经过定义该静态变量的类名经过点语法进行访问该引用。
三、任何位置能够对该静态变量进行从新赋值。
经过这二者方式的特色,咱们能够很明显的看出二者之间的区别。(这一切都是基于某类只须要存在一个实例对象的前提来讨论)
首先静态变量方式不能确保某类的实例的惟一性,这样在项目中,可能由于在某个文档类中对该静态变量进行再次赋值,存不可意料的风险(这种风险能够规避)。一样的,由于静态变量的定义的位置不肯定,因此须要协议商定,这些静态变量分类别进行定义在一个固定的位置(好比说某个专门存放静态变量方式的某类的对象的引用的文档类当中)。
而单例模式也就是静态变量方式建立一个类的实例引用所带来的缺陷的改善。首先解决引用的惟一实例可能被从新赋值的问题,单例模式中的getInstance()静态方法实现时,采用懒汉式建立一个对象(固然这只是建立方式的一种),规避了这一风险,无则建立,有则跳过建立。其次,getInstance()静态方法定义在该类的内部,获取该类对象的引用位置很是明确,无需额外的沟通商定,团队成员拿起即用。最后一个区别并非很明显,声明一个静态变量,实际上,咱们会直接对其进行初始化赋值,这样,在内存占用上,所占用的内存为该初始化赋值对象实际的内存。而单例模式能够经过懒汉建立法延迟该内存的占用,要知道,当一个静态变量只进行声明,而不进行初始化时,实际的内存占用只有4个字节(笔者我的推测,这四个字节只是一个指针地址所占用的内存空间)。
<font color=red>HashMap/HashTable初始容量大小和每次扩充容量大小的不一样</font>
能够看到HashTable默认的初始大小为11,以后每次扩充为原来的2n+1。HashMap默认的初始化大小为16,以后每次扩充为原来的2倍。还有我没列出代码的一点,就是若是在建立时给定了初始化大小,那么HashTable会直接使用你给定的大小,而HashMap会将其扩充为2的幂次方大小。
也就是说HashTable会尽可能使用素数、奇数。而HashMap则老是使用2的幂做为哈希表的大小。咱们知道当哈希表的大小为素数时,简单的取模哈希的结果会更加均匀(具体证实,见这篇文章),因此单从这一点上看,HashTable的哈希表大小选择,彷佛更高明些。但另外一方面咱们又知道,在取模计算时,若是模数是2的幂,那么咱们能够直接使用位运算来获得结果,效率要大大高于作除法。因此从hash计算的效率上,又是HashMap更胜一筹。
因此,事实就是HashMap为了加快hash的速度,将哈希表的大小固定为了2的幂。固然这引入了哈希分布不均匀的问题,因此HashMap为解决这问题,又对hash算法作了一些改动。具体咱们来看看,在获取了key对象的hashCode以后,HashTable和HashMap分别是怎样将他们hash到肯定的哈希桶(Entry数组位置)中的。
正如咱们所言,HashMap因为使用了2的幂次方,因此在取模运算时不须要作除法,只须要位的与运算就能够了。可是因为引入的hash冲突加重问题,HashMap在调用了对象的hashCode方法以后,又作了一些位运算在打散数据。关于这些位计算为何能够打散数据的问题,本文再也不展开了。感兴趣的能够看这里。
<font color=red>HashMap是支持null键和null值的,而HashTable在遇到null时,会抛出NullPointerException异常。</font>
这并非由于HashTable有什么特殊的实现层面的缘由致使不能支持null键和null值,这仅仅是由于HashMap在实现时对null作了特殊处理,将null的hashCode值定为了0,从而将其存放在哈希表的第0个bucket中。
1:<jsp:include page="top.jsp">先将top.jsp中的java脚本和jsp指令都执行完毕之后再将top.jsp页面加入到引用页面中。
2:<%@ include file="top.jsp"%>静态读取:则是将top.jsp的整个页面不加解析(不管是脚本仍是指令)通通读入到引用页面中,而后和引用页面一块儿进行解析(即开始执行脚本和指令)。
3:区别:其实上边的两条就是区别,可是须要注意的是用<%@ include file=""%>的时候被引用页面中不能再出现其余网页标签和page指令了,不然会冲突的
(jsp:include page="")
父页面和包含进来的页面单独编译,单独翻译成servlet后,在前台拼成一个HTML页面。
(%@include file=""%)
父页面和包含进来的页面,代码合并后,才一块儿翻译成servlet,反馈到前台,造成一个HTML页面。
由此咱们知道:jsp页面是把include指令元素(<%@ include file=""%>)所指定的页面的实际内容(也就是代码段)加入到引入它的jsp页面中,合成一个文件后被jsp容器将它转化成servlet。能够看到这时会产生一个临时class文件和一个servlet源文件。而动做元素(<jsp:include page=""/>)是在请求处理阶段引入的,会被JSP容器生成两个临时class文件和两个servlet原文件。而引入的只是servlet的输出结果,即JspWriter对象的输出结果,而不是jsp的源代码。
java是在服务器端运行的代码,jsp在服务器的servlet里运行,而javascript和html都是在浏览器端运行的代码。因此加载执行顺序是是java>jsp>js。
全部的JSP都会在客户端发出请求后被容器转译成servlet的源代码(java),而后再将源码(java)编译成servlet的类(class),放入到内存里面。
1.可重入锁
若是锁具有可重入性,则称做为可重入锁。
像Synchronized和ReentrantLock都是可重入锁,可重入性在我看来实际上代表了锁的分配机制:
基于线程的分配,而不是基于方法调用的分配。
举个简单的例子,当一个线程执行到某个Synchronized方法时,好比说method1,而在method1中会调用另一个Synchronized方法method2,此时线程没必要从新去申请锁,而是能够直接执行方法method2。
class MyClass { public synchronized void method1() { method2(); } public synchronized void method2() { } }
上述代码中的两个方法method1和method2都用Synchronized修饰了。
假如某一时刻,线程A执行到了method1,此时线程A获取了这个对象的锁,而因为method2也是Synchronized方法,假如Synchronized不具有可重入性,此时线程A须要从新申请锁。
可是这就会形成一个问题,由于线程A已经持有了该对象的锁,而又在申请获取该对象的锁,这样就会线程A一直等待永远不会获取到的锁。
而因为Synchronized和Lock都具有可重入性,因此不会发生上述现象。
2.可中断锁
可中断锁:顾名思义,就是能够响应中断的锁。
在Java中,Synchronized就不是可中断锁,而Lock是可中断锁。
若是某一线程A正在执行锁中的代码,另外一线程B正在等待获取该锁,可能因为等待时间过长,线程B不想等待了,想先处理其余事情,咱们可让它中断本身或者在别的线程中中断它,这种就是可中断锁。
在前面演示LockInterruptibly()的用法时已经体现了Lock的可中断性。
3.公平锁
公平锁即尽可能以请求锁的顺序来获取锁。好比同是有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最早请求的线程)会得到该所,这种就是公平锁。
非公平锁即没法保证锁的获取是按照请求锁的顺序进行的。这样就可能致使某个或者一些线程永远获取不到锁。
在Java中,Synchronized就是非公平锁,它没法保证等待的线程获取锁的顺序。
而对于ReentrantLock和ReentrantReadWriteLock,它默认状况下是非公平锁,可是能够设置为公平锁。这一点由构造函数可知:
public ReentrantLock() { sync = new NonfairSync(); } public ReentrantLock(boolean fair) { sync = (fair)? new FairSync() : new NonfairSync(); }
在ReentrantLock中定义了2个静态内部类,一个是NotFairSync,一个是FairSync,分别用来实现非公平锁和公平锁。
咱们能够在建立ReentrantLock对象时,经过知道布尔参数来决定使用非公平锁仍是公平锁。
若是参数为true表示为公平锁,为fasle为非公平锁。默认状况下,若是使用无参构造器,则是非公平锁。
另外在ReentrantLock类中定义了不少方法,好比:
isFair() //判断锁是不是公平锁
isLocked() //判断锁是否被任何线程获取了
isHeldByCurrentThread() //判断锁是否被当前线程获取了
hasQueuedThreads() //判断是否有线程在等待该锁
在ReentrantReadWriteLock中也有相似的方法,一样也能够设置为公平锁和非公平锁。
不过要记住,ReentrantReadWriteLock并未实现Lock接口,它实现的是ReadWriteLock接口。
4.读写锁
读写锁将对一个资源(好比文件)的访问分红了2个锁,一个读锁和一个写锁。
正由于有了读写锁,才使得多个线程之间的读操做不会发生冲突。
ReadWriteLock就是读写锁,它是一个接口,ReentrantReadWriteLock实现了这个接口。
能够经过readLock()获取读锁,经过writeLock()获取写锁。
乐观锁是假定读取的数据,在写以前不会被更新。适用于数据更新不频繁的场景。
相反,当数据更新频繁的时候,乐观锁的效率很低,由于基本上每次写的时候都要重复读写两次以上。
对于数据更新频繁的场合,悲观锁效率更高 ;
对于数据更新不频繁的场合,乐观锁效率更高;
通常来讲若是并发量很高的话,建议使用悲观锁,不然的话就使用乐观锁。
若是并发量很高时使用乐观锁的话,会致使不少的并发事务回滚、操做失败。
线程工做内存是cpu寄存器和高速缓存的抽象描述,使用频率高的数据从主存拷贝到高速缓存中,每一个线程在cpu高速缓存中对拷贝的数据进行读取、计算、赋值,再在合适的时候同步更新到主存的该数据,如i=1,i+1=2,若2在更新到主存前,其余线程是不知道该值被改变了,其余线程高速缓存中该值依然为1。
解决方法:须要各线程间可见的变量前加上volatile修饰,在一个线程的高速缓存中改变该值时,其余线程会得到该值的更新值。
垃圾收集器:
串行:Serial、Parallel、CMS
CMS:每一个垃圾收集周期只有两次短的停顿,开始时有一个短的停顿,称为初始标记;标记从外部直接可达的老年代对象;而后在并发标记阶段,标记全部从这些对象可达的存活对象;因为在标记期间应用可能正在运行并更新引用,因此到并发标记阶段结束时,未必全部存活的对象都能确保被标记;因此必须再次停顿,称为从新标记;最后一个阶段是并发清除。
dbcp没有自动地去回收空闲链接的功能 c3p0有自动回收空闲链接功能 。
二者主要是对数据链接的处理方式不一样!c3p0提供最大空闲时间,dbcp提供最大链接数。
前者当链接超过最大空闲链接时间时,当前链接就会被断掉。dbcp当链接数超过最大链接数时,全部链接都会被断开。
dbcp和c3p0都是单线程的,在高并发的环境下性能会很是低下;tomcat jdbc pool 近乎兼容 dbcp ,性能更高,异步方式获取链接。