idea回撤过度使用:ctrl+shift+z
提取变量为成员属性:ctrl+alt+f
提取变量为方法参数:ctrl+alt+p
Ctrl+Shift+Alt+N,查找类中的方法或变量
使用工具类Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的add/remove/clear方法会抛出UnsupportedOperationException异常。 说明:asList的返回对象是一个Arrays内部类,并没有实现集合的修改方法。Arrays.asList体现的是适配器模式,只是转换接口,后台的数据仍是数组。 String[] str = new String[] { "you", "wu" }; List list = Arrays.asList(str); 第一种情况:list.add("xxx"); 运行时异常。 第二种情况:str[0] = "x"; 那么list.get(0)也会随之修改。
java8中map-reduce:
@Test
public void test2(){
Optional<Integer> sum = emps.stream()
.map(Employee::getName)
.flatMap(TestStreamAPI1::filterCharacter)
.map((ch) -> {
if(ch.equals('六'))
return 1;
else
return 0;
}).reduce(Integer::sum);
System.out.println(sum.get());
}
Collectors.toList/toSet/toCollection(HashSet::new) 将元素抓换到集合中
Collectors工具类中还有很多方法:groupingby(可以多级分组,在一级分组的前提下在分组)
分区:partitioningBy
连接:join
聚合:reducing
tcc分布式事务
jxls实现报表
rpc调用的返回结果一定要进行NPE检查
在代码中使用“抛异常”还是“返回错误码”,对于公司外的 http / api 开放接口必须使用“错误码” ;
而应用内部推荐异常抛出 ; 跨应用间 RPC 调用优先考虑使用 Result 方式,封
装 isSuccess 、“错误码”、“错误简短信息”。
说明:关于 RPC 方法返回方式使用 Result 方式的理由:
1 ) 使用抛异常返回方式,调用方如果没有捕获到就会产生运行时错误。
2 ) 如果不加栈信息,只是 new 自定义异常,加入自己的理解的 error message ,对于调用
端解决问题的帮助不会太多。如果加了栈信息,在频繁调用出错的情况下,数据序列化和传输的性能损耗也是问题。
日志打印要使用占位符,别用字符串拼接。
异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么往上
抛。
正例: logger.error(各类参数或者对象 toString + "_" + e.getMessage(), e);
YYY.class.isAnnotationPresent(XXX.class) 判断YYY是否含有XXX注解
YYY.class.getAnnotation(XXX.class).value()获取value属性值
isAssignableFrom:判断一个class是不是另一个class本身或者父亲
superClass.isAssignableFrom(cls) && !superClass.equals(cls)
NumberUtils/IOUtils/FileUtils/PropertyUtils
spring:
web.xml中:
springmvc的字符过滤器要优先配置前端控制器dispatcheServlet
<listener>
<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
</listener>
1、此监听器主要用于解决java.beans.Introspector导致的内存泄漏的问题
2、此监听器应该配置在web.xml中与Spring相关监听器中的第一个位置(也要在ContextLoaderListener的前面)
3、JDK中的java.beans.Introspector类的用途是发现Java类是否符合JavaBean规范如果有的框架或程序用到了Introspector类,那么就会启用一个系统级别的缓存,此缓存会
存放一些曾加载并分析过的JavaBean的引用。当Web服务器关闭时,由于此缓存中存放着这些JavaBean的引用,所以垃圾回收器无法回收Web容器中的JavaBean对象,最后导致
内存变大。而org.springframework.web.util.IntrospectorCleanupListener就是专门用来处理Introspector内存泄漏问题的辅助类。IntrospectorCleanupListener会在
Web服务器停止时清理Introspector缓存,使那些Javabean能被垃圾回收器正确回收。Spring自身不会出现这种问题,因为Spring在加载并分析完一个类之后会马上刷新
JavaBeans Introspector缓存,这就保证Spring中不会出现这种内存泄漏的问题。但有些程序和框架在使用了JavaBeans Introspector之后,没有进行清理工作(如Quartz,Struts),最后导致内存泄漏
<async-supported>true</async-supported>:
springmvc支持异步,servlet3.0之后
业务层不做异常处理,直接抛出,但是要做参数校验。控制层要分层catch处理异常,通常以json格式传输数据。@RequestBody和@ResponseBody。
spring的配置文件配置属性,是通过set方法进行的,所以在配置文件中配置的在bean类中并不一定有这个属性,而是setXXX方法
spring初始化或者销毁的时候有三种方式处理:
1.实现InitializingBean/DisposableBean接口
2.在xml中配置bean的时候配init-method/destory-method
3.使用@PostConstruct和@PreDestory
三者的顺序:Constructor > @PostConstruct > InitializingBean > init-method
拓展:
spring初始化顺序:
1. 实例化;
2. 设置属性值;
3. 如果实现了BeanNameAware接口,调用setBeanName设置Bean的ID或者Name;
4. 如果实现BeanFactoryAware接口,调用setBeanFactory 设置BeanFactory;
5. 如果实现ApplicationContextAware,调用setApplicationContext设置ApplicationContext
6. 调用BeanPostProcessor的预先初始化方法;(@PostConstruct在它的前置处理器中执行)
7. 调用InitializingBean的afterPropertiesSet()方法;
8. 调用定制init-method方法;
9. 调用BeanPostProcessor的后初始化方法;
加密及解密:
对密钥保密,对算法公开
密码分类:
对称加密:加密解密密钥相同(对称密码算法)
非对称加密:加密解密密钥不同(非对称密码算法)
散列函数:验证数据完整性,长度不限制,哈希容易计算,散列过程不可逆(单向函数)、md5、sha、mac
数字签名:
OSI安全体系:
OSI网络七层协议,对应的安全机制
java的pojo中:
如果属性是boolean类型,那么它优先调用isXXX方法,此时getXXX方法的优先级没有它高,如果没有isXXX,那么getXXX也是可以生效的。属性的名称不建议使用isXX开头,因为这样的话
它的set方法会变成setXXX,get方式会isXXX.
如果是Boolean类型,那么它都是getXXX和setXXX.此时isXXX属性和XXX属性的方法会出现冲突。
private Boolean isFailure;
public Boolean getFailure() {
return isFailure;
}
public void setFailure(Boolean failure) {
isFailure = failure;
}
private boolean isSuccess;
public boolean isSuccess() {
return isSuccess;
}
public void setSuccess(boolean success) {
isSuccess = success;
}
pojo中属性一般使用包装类,方法参数或者返回值一般使用基本数据类型。
redis相关:
rename重命名key,会覆盖(renamenx重复会失败)
key * : *是pattern
java中threadLocal对象其实就是一个map,key是线程对象,value是值,所以存储的value是放在堆中的,而不是栈中。耗费了内存,减少了线程之间同步带来的性能损耗,也减少并发的复杂度
跨域是由于浏览器的同源策略造成的安全限制。同源:协议+域名+端口要相同
localhost和127.0.0.1虽然都指向本机,但也属于跨域
浏览器执行javascript脚本时,会检查这个脚本属于哪个页面,如果不是同源页面,就不会被执行。ajax请求不允许跨域
解决:1.JSONP只支持GET请求,不支持POST请求。
2.代理,可以请求后端服务,让后端发出请求获取数据,自然可以跨过浏览器限制
3.在过滤器或者拦截器中设置:
//添加跨域CORS
response.setHeader("Access-Control-Allow-Origin", "*"); //这里通常是request.getHeader("Origin")->获得请求的域名地址response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Headers", "X-Requested-With,content-type,token");
response.setHeader("Access-Control-Allow-Credentials", "true"); //允许ajax携带cookie
response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH");
return true;
cookie:构造方法提供name和value两个属性,默认的生命周期是-1,表示浏览器关闭cookie失效。cookie中保存中文要进行Unicode字符编码(UTF-8),如果保存二进制数据
则要进行base64编码(不建议保存二进制数据)
cookie中的path属性:如果设置成"/session/",那么只有"/session"下的contextPath才能访问此cookie,一般设置为"/",注意最后一个字符是必须是"/"
cookie中的domain属性:表示可以访问此cookie的域名,如设置成".google.com",那么所有以"google.com"结尾的域名都可以,这里可以解决cookie跨域的问题,必须以"."开头
泛型是要在实例化对象的时候可以指定,静态方式是不能使用泛型的
comparable是在比较对象的内部进行比较,内比较,比较对象可以不是同一类,通过泛型指定
comparator是一个比较器,在比较对象外部进行比较,然后比较的是同一类型,常用策略设计模式,不同场景使用不同的比较器
不改变已有的,或者公共的,将不同的放入接口不同实现类中,这就是策略设计模式的主要用途,基于一个方法的不同表现的。而模板设计模式是基于父子类的,不同类的表现
bio:客户端与服务端建立连接通信数据,服务端针对每个读写请求建立一个线程处理,此时,数据有可能并未万全准备就绪,那么线程等待阻塞(由内核地址空间拷贝到用户地址空间)
nio:当客户端注册到选择器上的通道数据完全准备就绪那么就会分配一个或多个线程去处理这些数据。
java7中引入fork/join框架,将一个大任务拆分成多个子任务分配到不同的队列中,队列和每个独立线程对应,线程完成任务之后,会将所有结果汇集。为了充分利用CPU资源,
使用双端队列,采用工作窃取模式,先做完队列任务的线程回去别的队列尾部处理任务,而被窃取的线程会从头部处理任务,以减少线程访问共同数据产生的竞争。
缺点:如果双端队列中只有一个任务,那么就会出现竞争,还有消耗了双端队列和创建多线程的资源
TimeUnit.SECOUND.sleep()睡眠
java7之后可以在try()中进行资源管理,可以对资源进行自动释放,需要资源实现AutoCloseable接口
数据的可见性问题,可以使用synchronize同步锁来保证每次获取数据都会刷新内存,而使用volatile是一种轻量级的同步策略,性能会降低,指令不能重排序。
volatile不具备互斥性,不能保证线程安全,也不具备原子性。hashtable的并发操作会出现线程等待,并行转串行,使用复合操作会出现问题,集合中有提供复合操作的类
原子性:类似i++的操作,线程会先读取,然后操作写回去,这两个操作之间不具备原子性,所以会出现多线程访问的安全问题,使用原子变量代替
juc中的并发操作依靠的是cas算法,compare-and-swap算法,有个方法compareAndSet。
cas是一种无锁算法,依靠的是硬件CPU对并发的支持的一种指令
Linux命令:less:
向上一页:b 向下一页:空格
向上半页:u 向下半页:d
向上一行:k 向下一行:j
java中的日志系统:
slf4j和jcl都是日志门面,log4j/log4j2/logback/jul都是日志的具体实现
jcl是通过动态查找的机制进行具体日志实现的装配,slf4j则是在编译的时候静态绑定真正的log库
dubbo:
服务分组:在<dubbo:service>中可以为服务提供分组功能,同一服务不同实现,可以划分到不同的组
多版本:在<dubbo:service>中可以指定服务的版本号,同一服务,同一实现,不同版本。场景:针对一些接口的升级与老版本的调用
dubbo中推荐使用zookeeper作为注册中心,使用dubbo协议暴露服务,底层用的netty作为数据传输框架,序列化用的是Hessian,服务代理使用的是javaassist proxyFactory
失败策略:失败重试:针对幂等操作
失败抛错:针对非幂等操作
失败安全(failsafe):可以丢失数据,针对写日志的服务
负载均衡策略:
随机:不确定性比较大,可能会出现瞬时访问大的现象
轮循:可以分配轮循权重,会出现慢的机器访问堆积
最小活跃数:相同活跃数则随机
一致性hash:相同的请求会到同一台服务,服务不可用会出现问题,在dubbo中,会用虚拟主机代替,但是这样会出现负载过高的现象
dubbo可以为一个接口中的某个具体方法配置超时时间,也可以为具体的接口配置超时时间,或者整个消费者都可以,建议由服务提供方提供这样的配置
dubbo和spring整合,定义一套xsd的约束文件,然后在META-INF下面新建两个文件spring.handlers和spring.schemas,后者用于指定xsd文件目录,前者指定解析
xsd的namespaceHandler(命名空间处理器),所以其实以<dubbo:>开头的这些东西就是生成一些bean对象
dubbo在Zookeeper上会将具体的服务接口全限定名为路径,然后子节点有providers,下面就是某个具体的服务信息,这些信息就是某个提供者节点的配置信息,拼接为URL,encode之后存储在那个节点路径上
SPI类似策略设计模式,提供接口,具体的实现可以扩展,只要遵从他那种约定,一个接口下的所有插件功能都能遍历出来,指定目录下的扩展点
dubbo的ExtensionLoader读取扩展点的过程中用到了装饰者设计模式,判断接口实现类是否有@Adaptve注解
dubbo的SPI是通过ExtensionLoader来实现的,约定在META-INFO/dubbo/接口全限定名,内容为 配置名=接口实现类的全限定名,接口上有@SPI注解
使用方式有两种:1.手动指定 2.自动生成
手动指定:
private static final DuSPI duSPI = ExtensionLoader.getExtensionLoader(DuSPI.class).getExtension("local");
public static void main(String[] args) {
String hi = duSPI.SayHello("扩展SPI");
System.out.println(hi);
}
自动生成:
unzip dubbo.war -d ROOT :将dubbo.war解压并命名为ROOT
synchronized可以保证原子性和可见性,对于那些多线程操作对象,无顺序相关的可以不加volatile,如果有顺序相关,比如单例模式下,是会有问题的,所以加volatile,涉及到java指令重排,无序写入
concurrentHashMap的putIfAbsent是当如果key存在的时候,什么都不做,而put方法是key相同,会覆盖
异常中的fillinStackTrace方法每次执行的时候,会清空原来栈内的trace信息,然后再当前调用位置处重新建立trace信息
maven多环境下:
使用命令 -Pdev/-Ptest 指定不同环境
比如:
<profile>
<id>dev</id>
<properties>
<environment>dev</environment>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
那么通过(在build节点下)
<filters>
<filter>src/main/filters/filter-${environment}.properties</filter>
</filters>
可以指定特定目录下的环境配置文件,默认是dev
另外还要开启过滤,开启后,前面的环境专属配置加载才会有效
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
这样在你的配置文件中可以使用${xxx}来加载你maven指定的那个环境下的配置文件,当然你也可以利用profile的properties属性来做
例如:app.properties中有appUrl=${url},那么你可以指定<url>xxx</url>来指定
还可以:mvn clean package -Durl=localhost指定
参考链接:https://www.cnblogs.com/zdd-java/p/6691380.html
fastjson的SerializerFeature序列化属性:
QuoteFieldNames———-输出key时是否使用双引号,默认为true
WriteMapNullValue——–是否输出值为null的字段,默认为false
WriteNullNumberAsZero—-数值字段如果为null,输出为0,而非null
WriteNullListAsEmpty—–List字段如果为null,输出为[],而非null
WriteNullStringAsEmpty—字符类型字段如果为null,输出为”“,而非null
WriteNullBooleanAsFalse–Boolean字段如果为null,输出为false,而非null
JSON.toJsonString(hashMap,Serializer选项)
Linux下nc命令可以用来访问远程IP端口,可以做端口扫描、文件拷贝等
echo xx | nc localhost 80
在cs模式下:
server端:nc -l 9876
client端:nc ip 9876
这样可以传输内容
在此基础上,如果要传输文件:
则server: nc -l 9876 > file
client:cat file | nc ip 9876
Spring中的depends-on可以解决bean初始化的依赖顺序,被依赖的bean会优先创建
Spring中有个ApplicationListener接口可以实现,实现onApplicationEvent方法,在spring容器加载bean完成之后进行初始化会调用这个方法
public void onApplicationEvent(ContextRefreshedEvent event) {
if (event.getApplicationContext().getParent() == null) {
// 初始化工作
}
}
线程池的处理任务策略:
刚开始都是在创建新的线程,达到核心线程数量,新的任务进来后不再创建新的线程,而是将任务加入工作队列,任务队列到达上限后,新的任务又会创建新的普通线程,直到达到线程池最大的线程数量满了,后面的任务则根据配置的饱和策略来处理。我们这里没有具体配置,使用的是默认的配置AbortPolicy:直接抛出异常。
线程池的饱和策略:
1、AbortPolicy:直接抛出异常
2、CallerRunsPolicy:只用调用所在的线程运行任务
3、DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
4、DiscardPolicy:不处理,丢弃掉。
Zookeeper的客户端curator提供了丰富的操作API
curator中的InterProcessMutex提供了重入锁功能,类似于java中的ReentrantLock,还有读写锁
Mysql:
show index from table;
show columns from table;
show create table tablename;
show engines;
show table status from database;
select variables like '%auto%';
扩展,查询mysql元数据相关:
select version(); select user(); select database();
show status; show variables;
MySQL支持多种类型,大致可以分为三类:数值、日期/时间和字符串(字符)类型
NULL值处理:is null / is not null
正则:SELECT name FROM person_tbl WHERE name REGEXP '^st'; --以st开头的name
MYSQL 事务处理主要有两种方法:
1、用 BEGIN, ROLLBACK, COMMIT来实现
BEGIN 开始一个事务
ROLLBACK 事务回滚
COMMIT 事务确认
2、直接用 SET 来改变 MySQL 的自动提交模式:
SET AUTOCOMMIT=0 禁止自动提交
SET AUTOCOMMIT=1 开启自动提交
创建普通索引:CREATE INDEX indexName ON mytable(username(length));
创建唯一索引:CREATE UNIQUE INDEX indexName ON mytable(username(length))
MySQL 临时表在我们需要保存一些临时数据时是非常有用的。临时表只在当前连接可见,当关闭连接时,Mysql会自动删除表并释放所有空间。CREATE TEMPORARY TABLE
mysql复制表的步骤:show create table xxx;把语句复制,更改表名到新表,然后insert into new_table(xxx,xxx,xxx) select xxx,xxx,xxx from old_table;
在MySQL的客户端中你可以使用 SQL中的LAST_INSERT_ID( ) 函数来获取最后的插入表中的自增列的值。
truncate 和 delete的区别:
1.前者不能回滚,后者可以
2.前者删除的比较彻底,所有日志都会没有,然后不能带where条件,自增id也会清零,delete则不会清零
3.前者不会触发触发器,会删除索引
MYsql主从:
(1)Master将改变记录到二进制日志(binary log)中(这些记录叫做二进制日志事件,binary log events);//binlog线程记录
(2)Slave将Master的二进制日志事件(binary log events)拷贝到它的中继日志(relay log);//IO线程从监听主库二进制日志事件
(3)Slave重做中继日志(Relay Log)中的事件,将Master上的改变反映到它自己的数据库中。// SQL线程重做
InnoDB和MyISAM的几点不同:
1.前者支持事务,后者不能
2.前者支持行级锁,后者只能支持表级锁(锁的成本很少,但是并发性能低)
3.前者支持mvcc,后者不能
4.前者有外键支持,后者不能
5.前者不支持全文索引,后者可以
6.前者数据索引存放在一起,属聚簇索引,后者数据索引分开存储,属于非聚簇索引
InnoDB的四大特性:
插入缓冲(insert buffer),二次写(double write),自适应哈希索引(ahi),预读(read ahead)
数据的插入和更新是先插入缓冲区中,然后以一定频率插入非聚集索引的索引页中
自适应哈希索引:由于innodb不支持hash索引,但是hash比较快,所以它内部会监控表上的索引查找,当发现建立hash索引可以提高性能,,则会自动建立hash索引,使用show engine innodb status \G 查看使用情况,默认是开启的
做count(*)的话myisam更快,因为myisam内部维护了一个计数器,可以直接调取。
varchar与char的区别char是一种固定长度的类型,varchar则是一种可变长度的类型
varchar(50)表示最多存放50个字符,varchar(50)和(200)存储hello所占空间一样,但后者在排序时会消耗更多内存,因为order by col采用fixed_length计算col长度(memory引擎也一样)
事务是如何通过日志来实现的:
事务日志是通过redo和innodb的存储引擎日志缓冲(Innodb log buffer)来实现的,当开始一个事务的时候,会记录该事务的lsn(log sequence number)号; 当事务执行时,
会往InnoDB存储引擎的日志的日志缓存里面插入事务日志;当事务提交时,必须将存储引擎的日志缓冲写入磁盘(通过innodb_flush_log_at_trx_commit来控制),也就是写
数据前,需要先写日志。这种方式称为“预写日志方式”
binlog里面只是记录改变数据的sql
InnoDB是基于索引来完成行锁,例: select * from tab_with_index where id = 1 for update;for update 可以根据条件来完成行锁锁定,并且 id 是有索引键的列,
如果 id 不是索引键那么InnoDB将完成表锁,,并发将无从谈起
索引:评判索引的优劣是IO的渐进复杂度
优势
类似大学图书馆建书目索引,提高数据检索效率,降低数据库的IO成本。
通过索引对数据进行排序,降低数据排序的成本,降低了CPU的消耗。
劣势:
实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占空间的。
虽然索引大大提高了查询速度,同时确会降低更新表的速度,如对表进行INSERT、UPDATE、DELETE。
因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段。
都会调整因为更新所带来的键值变化后的索引信息。
索引的分类:主键索引、唯一索引、普通索引、全文索引(仅仅myIsam engine可用,针对大数据字段建立索引,比较耗时耗空间)、组合索引
单值索引
即一个索引只包含单个列,一个表可以有多个单列索引。
唯一索引
索引列的值必须唯一,但允许有空值。
组合索引
即一个索引包含多个列。为了更多的提高mysql效率可建立组合索引,遵循”最左前缀“原则。只有满足了最左前缀,后面才有可能使用多个索引
创建复合索引时应该将最常用(频率)作限制条件的列放在最左边,依次递减。组合索引最左字段用in是可以用到索引的,最好explain一下select
哪些情况需要创建索引:
①主键自动建立唯一索引
②频繁作为查询条件的字段应该创建索引
③查询中与其他表关联的字段,外键关系建立索引
④频繁更新的字段不适合建立索引,因为每次更新不单单是更新了记录还会更新索引
⑤WHERE条件里用不到的字段不创建索引
⑥单键/组合索引的选择问题,who?(在高并发下倾向创建组合索引)
⑦查询中排序的字段,排序的字段若通过索引去访问将大大提高排序速度
⑧查询中统计或者分组字段
索引不会包含有NULL值的列:只要列中包含有NULL值都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的。所以我们在数据库设计时不要让字段的默认值为NULL。
MySQL查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。
哪些情况不要创建索引:
①表记录太少
②经常增删改的表
提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE、和DELETE。
因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件。
数据重复且分布平均的表字段,因此应该只为最经常查询和最经常排序的数据建立索引。
③注意,如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果。
B+ tree 能控制树的高度,Degree:指一个节点存放多个数据,degree越大,树的高度越低,查询速度越快,使用叶子结点储存数据,相邻叶子结点直接加了指针指向,便于范围查找
B tree它存的是一个个元组(key,data),数据保存在所有节点上,是二元的
MyIsam的数据和索引分开存储,从MYI文件中定位索引,然后指向MYD文件中的数据
聚集索引是以主键为索引来组织数据的,数据存放在索引的数据结构上。如果要以非主键字段建立索引(副索引),那么这个索引存放的是主键,然后在主索引上找到数据
B+tree插入整型数据会有数据的连续排列(空间局部性原理),插入UUID会随机,所以整型分配page内存会有优势,在读取策略上具有优势,推荐使用自增id,UUID产生页面分裂,在走索引比较的时候也要比整型要差
MyISAM使用B-Tree实现主键索引、唯一索引和非主键索引。
InnoDB中非主键索引使用的是B-Tree数据结构,而主键索引使用的是B+Tree。
连接:https://www.cnblogs.com/shijianchuzhenzhi/p/6383117.html
java移位运算符:(32位的二进制数据)
左移运算符:<< 最高位减位数0,低位补多少个0
右移运算符:>> 最低位减位数0,最高位补多少个0
无符号右移运算符: >>> 忽略了符号位扩展,0补最高位,无符号右移规则和右移运算是一样的,只是填充时不管左边的数字是正是负都用0来填充,无符号右移运算只针对负数计算,因为对于正数来说这种运算没有意义