-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontent.json
More file actions
1 lines (1 loc) · 92 KB
/
Copy pathcontent.json
File metadata and controls
1 lines (1 loc) · 92 KB
1
{"meta":{"title":"terrence mu's blog","subtitle":null,"description":"keep moving","author":"terrence mu","url":"http://yoursite.com"},"pages":[{"title":"about","date":"2017-07-21T05:09:54.000Z","updated":"2017-07-21T05:09:54.000Z","comments":true,"path":"about/index.html","permalink":"http://yoursite.com/about/index.html","excerpt":"","text":"","raw":null,"content":null}],"posts":[{"title":"Spring boot in Action 笔记","slug":"springbootinaction","date":"2018-02-18T02:18:50.000Z","updated":"2018-02-25T15:04:47.000Z","comments":true,"path":"2018/02/18/springbootinaction/","link":"","permalink":"http://yoursite.com/2018/02/18/springbootinaction/","excerpt":"","text":"Spring boot 提供的四个核心功能 auto config - 声明式的标识,如@Bean来实现实例的自动注入。 starter pom - 利用maven的传递依赖,组合常用的库。避免了多个库之间的存在的可能不兼容问题。 命令行界面 - Spring boot CLI Actuator 提供如应用程序运行时内部运行的监控 Spring boot CLI命令初始化项目: spring init -dweb,jpa,security - -build maven -p war -x -d后面跟依赖的信息。把CLI生成的项目解压到当前目录,使用 - - extract 或者 -x参数。 spring help init 查看帮助 sping init -l 列出init参数 Spring boot 自动配置@ConditionalOnMissingBean注解,可以用于覆盖自动配置。 如@ConditionalOnMissingBean(ABC.class)如果当前不存在ABC类型,添加该注解的bean就会生效。 Spring Boot能从多种属性源获得属性,具体如下,优先级从高到低, 命令行参数 java:comp/env里的JNDI属性 JVM系统属性 操作系统环境变量 随机生成的带random.*前缀的属性(在设置其他属性时,可以引用它们,比如${random. long}) 应用程序以外的application.properties或者appliaction.yml文件 打包在应用程序内的application.properties或者appliaction.yml文件 通过@PropertySource标注的属性源 application.properties和application.yml文件能放在以下四个位置,优先级从高到低 外置,在相对于应用程序运行目录的/config子目录里。 外置,在应用程序运行的目录里。 内置,在config包内。 内置,在Classpath根目录。另外,如果你在同一优先级位置同时有application.properties和application.yml,那么application.yml里的属性会覆盖application.properties里的属性。 Spring boot默认的系统是logback,如果需要用别的日志系统,需要在starter中排除掉自动依赖,再重新单独引入。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> 如引入log4j 1234<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j</artifactId></dependency> Spring boot 官方提供的starter Spring-boot-starter SpringBoot核心Starter,包含自动配置,日志,yaml配置文件的支持。 Spring-boot-starter-actuator 准生产特性,用来监控和管理应用 Spring-boot-starter-remote-shell 提供SSH协议的监控和管理 Spring-boot-starter-remote-amqp 使用Spring-rabbit对AMQP协议支持 Spring-boot-starter-remote-aop 使用Spring-aop和AspectJ面向切面编程支持 Spring-boot-starter-remote-batch Spring batch的支持 Spring-boot-starter-cloud-connectors 对云平台提供服务提供简化的连接方式 Spring-boot-starter-data-elasitcsearch 通过spring-data-elasticsearch对elasticsearch支持 Spring-boot-starter-data-gemfire 通过spring-data-gemfire 对gemfire 支持 Spring-boot-starter-data-jpa 对 JPA支持,包含spring-data-jpa,spring-orm和Hibernate Spring-boot-starter-data-mangodb 通过spring-data-mangoDB 对mangoDB支持 Spring-boot-starter-data-rest 通过spring-data-rest-webmvc 对spring data repositoty暴露未REST形式的服务 Spring-boot-starter-data-solr 通过spring-data-solr对apache solr数据检索平台的支持 Spring-boot-starter-freemaker 对freemaker模板引擎的支持 Spring-boot-starter-groovy-templates 对Groovy模板引擎的支持 Spring-boot-starter-hateoas 通过spring-hateoas对基于HATEOAS的REST形式的网络服务的支持 Spring-boot-starter-hornetq 通过HornetQ对JMS的支持 Spring-boot-starter-integration 对系统继承框架spring-integeration的支持 Spring-boot-starter-jdbc 对JDBC数据库的支持 Spring-boot-starter-jersey 对Jersey REST形式的网络服务的支持 Spring-boot-starter-atomikos 通过Atomikos对分布式事务的支持 Spring-boot-starter-bitronix 通过Bitronix对分布式事务的支持 Spring-boot-starter-mail 对javax.mail的支持 Spring-boot-starter-mobile 对spring-mobile的支持 Spring-boot-starter-mustache 对Mustache模板引擎的支持 Spring-boot-starter-redis 对键值对内存数据库redis的支持,包含spring-redis Spring-boot-starter-security 对spring-security的支持 Spring-boot-starter-social-facebook 通过 Spring-social-facebook对facebook的支持 Spring-boot-starter-social-linkin 通过 Spring-social-linkin对linkin的支持 Spring-boot-starter-social-twitter 通过 Spring-social-twitter对twitter的支持 Spring-boot-starter-test 对常用测试框架Junit,Hamcrest和mokito的支持,包含spring-test模块 Spring-boot-starter-thymeleaf 对thymeleaf模板引擎的支持 Spring-boot-starter-velocity 对velocity模板引擎的支持 Spring-boot-starter-web 对web项目开发的支持,包含tomcat和spring-webmvc Spring-boot-starter-Tomcat spring boot默认的servlet容器Tomcat Spring-boot-starter-Jetty 使用Jetty作为servlet容器替换tomcat Spring-boot-starter-Undertow 使用undertow作为servlet容器替换tomcat Spring-boot-starter-logging spingboot 默认支持日志框架logback Spring-boot-starter-log4j 对log4j的支持 Spring-boot-starter-websocket 对websocket的支持 Spring-boot-starter-ws 对spring web services的支持 @ConfigurationProperties可以获取application.properties中的数据,通过prefix属性指定properties的配置的前缀。具体可以参见ConfigurationProperties的源码。 通过logging.file=日志目录,logging.level.包名=日志级别 application-{profile}.properties 中的profile用于不同环境的配置。可以在application.properties中设置spring.profiles.active=prod来指定活动的Profile。 Spring boot 运行原理核心功能是由@EnableAutoConfiguration提供的。它由AutoConfigurationImportSelector通过SpringFactoriesLoader.loadFactoryNames来获取META-INF/spring.factories下的jar包。具体如下: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546# PropertySource Loadersorg.springframework.boot.env.PropertySourceLoader=\\org.springframework.boot.env.PropertiesPropertySourceLoader,\\org.springframework.boot.env.YamlPropertySourceLoader# Run Listenersorg.springframework.boot.SpringApplicationRunListener=\\org.springframework.boot.context.event.EventPublishingRunListener# Application Context Initializersorg.springframework.context.ApplicationContextInitializer=\\org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\\org.springframework.boot.context.ContextIdApplicationContextInitializer,\\org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\\org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer# Application Listenersorg.springframework.context.ApplicationListener=\\org.springframework.boot.ClearCachesApplicationListener,\\org.springframework.boot.builder.ParentContextCloserApplicationListener,\\org.springframework.boot.context.FileEncodingApplicationListener,\\org.springframework.boot.context.config.AnsiOutputApplicationListener,\\org.springframework.boot.context.config.ConfigFileApplicationListener,\\org.springframework.boot.context.config.DelegatingApplicationListener,\\org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\\org.springframework.boot.logging.ClasspathLoggingApplicationListener,\\org.springframework.boot.logging.LoggingApplicationListener# Environment Post Processorsorg.springframework.boot.env.EnvironmentPostProcessor=\\org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\\org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor# Failure Analyzersorg.springframework.boot.diagnostics.FailureAnalyzer=\\org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\\org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\\org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\\org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\\org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\\org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\\org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer# FailureAnalysisReportersorg.springframework.boot.diagnostics.FailureAnalysisReporter=\\org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter Web相关配置自动配置的ViewResolver ContentNegotiatingViewResolver 代理不同的viewResolver来处理不同的view,拥有最高的优先级。 BeanNameViewResolver 会查找对应的Bean名称来返回字符串的View渲染视图 InternalResourceViewResolver 通过设置前缀,后缀,以及控制器中的方法来返回视图名的字符串,已得到实际页面。 自动配置静态资源,通过addResourceHandlers方法来定义。 在WebAutoConfigure中定义Formatter,Conveter,HttpMessageConverters。可以自定义HttpMessageConveter,通过@Bean的方式来定义。 替代Spring boot的MVC配置,定义一个配置类并继承WebMvcConfigurerAdapter。 @Configuration public class WebMvcConfig extends WebMvcConfigureAdapter{ @Override public void addViewControllers(ViewControllerRegistry registry){ registry.addViewController(“/xx”).setViewName(“/xx”); } } 注册Servlet,Filter,Listener 直接声明Spring Bean的方式 @Bean 分别实例ServletRegistrationBean来注册servlet,FilterRegistrationBean注册Filter,ServletListenerRegistrationBean来注册Listener 代码配置Servlet容器可以实现一个EmbeddedServletContainerCustomizer接口的Bean,如果想直接配置Tomcat,Jetty或者Undertow,可以通过对应的定义来直接定义TomcatEmbeddedServletContainerFactory, JettyEmbeddedServletContainerFactory, UndertowEmbeddedServletContainerFactory。 SSL配置 生成证书 keytool生成自签名证书,输入命令keytool -genkey -alias tomcat,在当前目录生成一个.keystore文件。 添加配置 把.keystore复制到根目录,并配置 server.port = 8443 server.ssl.key-store=.keystore server.ssl.key-store-password=111111 server.ssl.keyStoreType=JKS server.ssl.keyAlias:tomcat 重定向HTTP到HTTPS 重新设置Http Connector Port 8080到8443。 WebSocket支持,需要引入spring-boot-starter-websocket。 Spring Cache缓存 Spring支持CacheManager SimpleCacheManager — 使用简单的Collection来存储缓存,主要用来测试用途 ConcurrentMapCacheManager — 使用ConcurrentMap来存储缓存 NoOpCacheManager — 仅测试用途,不会实际存储操作 EhCacheManager, GuavaCacheManager, HazelcastCacheManager, JCacheCacheManager(支持JSR-107标准的实现), RedisCacheManager。 声名式缓存注解 @Cacheable 在方法执行前Spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据,若没有则写入。 @CachePut 无论如何都会把返回值写入到缓存 @CacheEvict 将一条或多条数据从缓存中删除 @Caching 可以通过@Caching注解组合多个注解策略在一个方法上 开启声明式缓存 @Configuration @EnableCaching public class AppConfig{} Spring boot 默认使用SimpleCacheConfiguration,即使用ConcurrentMap来做缓存。Spring Boot支持以 spring.cache未前缀来配置缓存。 spring.cache.type=#可选 generic,ehcache,hazelcast,redis,guava,simple,none,infinispan,jcache等 spring.cache.cache-name=#程序启动创建缓存名称 spring.cache.guava.spec=#guava specs Application上也可开启@EnableCaching。 异步消息的支持异步消息中两个很重要的概念,消息代理和目的地。目的地主要包含两种形式:队列和主题,队列用于点对点式的消息通信,主题用于发布/订阅式的消息通信。 Spring为JMS,AMQP提供了@JmsListener和@RabbitListner注解在方法上监听消息代理发布的消息,需要@EnableJms或者@EnableRabbit来开启。 ActiveMQConnectionFactoryFactory来配置ActiveMQ的Bean连接,并通过前缀为spring.activemq的属性来配置对应的参数。 Spring Integration提供基于Spring的EIP的实现,解决不同系统之间的交互问题,通过异步消息驱动来达到系统交互时的系统之间的松耦合。","raw":null,"content":null,"categories":[],"tags":[{"name":"spring boot","slug":"spring-boot","permalink":"http://yoursite.com/tags/spring-boot/"}]},{"title":"Spring key point mark on","slug":"springmark","date":"2018-01-29T05:58:51.000Z","updated":"2018-02-25T15:02:51.000Z","comments":true,"path":"2018/01/29/springmark/","link":"","permalink":"http://yoursite.com/2018/01/29/springmark/","excerpt":"","text":"Spring & Spring boot related mark EventApplicationEvent - create application eventApplicationListener - listen application eventApplicationContext 继承自 ApplicationEventPublisher - publish applicaton event AwareBeanNameAware - 获取容器中BeanName BeanFactoryAware - 获得当前bean factory ApplicationContextAware - 当前的applicationContext,继承类ApplicationObjectSupport MessageSourceAware - 获取MessageSource,获取文本信息 ApplicationEventPublisherAware - 应用事件发布器,发布事件 ResourceLoaderAware - 获取资源加载器,获取外部资源 Conditionalcondition来match不同的条件,并进行不同的处理。 组合注解将多个元注解合并,定义为组合注解,简化配置。 Enable*注解@EnableAspectJAutoProxy开启对AspectJ自动代理的支持。 @EnableAsync开启异步方法的支持。 @EnableScheduling 开启计划任务的支持。 @EnableWebMvc开启Web MVC的支持 @EnableConfigurationProperties开启对@ConfigurationProperties注解配置Bean的支持。 @EnableJpaRepositories开启对Spring DataJPA Repository的支持。 @EnableTransactionManagement开启注解事务的支持。 @EnableCaching开启注解式的缓存支持。 所有Enable都包含一个@Import注解,用来导入配置类的。导入了一些自动配置的Bean。这些导入配置的方式主要分为三种类型: 直接导入配置类 依据条件选择配置类 动态注册Bean servlet3.0可以通过webApplicationInitializer替代web.xml,实现该接口将会被SpringServletContainerInitializer获取到。 通过重写WebMvcCOnfigureAdapter的addInterceptors方法来注册自定义的拦截器。 @ControllerAdvice用于controller的全局配置,@ExceptionHandler 用于全局处理控制器的异常。 @InitBinder,用来设置WebDataBinder,WebDataBinder用来自动绑定前台请求参数到model中。 @ModelAttribute,绑定键值对到Model中,此处是让全局的@RequestMapping都能获得在此处设置的键值对。 Spring通过MultipartResolver来上传文件。 spring的消息推送 RequestMapping中通过produces设置为「text/event-stream」来实现服务端往浏览器的消息推送。 servlet3.0的异步方式,setAsyncSupported为true。 @SpringBootApplication 注解组合了@Configuration,@EnableAutoConfiguration,@ComponentScan, 可以通过exclude参数关闭特定的自动配置,如@SpringBootApplication(exclude={DataSourceAutoConfiguration.class}) Banner定制src/main/resources下新建banner.txt。http://patorjk.com/software/taag生成字符。","raw":null,"content":null,"categories":[],"tags":[{"name":"spring, spring boot, key point","slug":"spring-spring-boot-key-point","permalink":"http://yoursite.com/tags/spring-spring-boot-key-point/"}]},{"title":"高性能MySQL 学习笔记","slug":"mysqlstudy","date":"2017-10-28T15:59:22.000Z","updated":"2018-01-29T06:01:57.000Z","comments":true,"path":"2017/10/28/mysqlstudy/","link":"","permalink":"http://yoursite.com/2017/10/28/mysqlstudy/","excerpt":"","text":"第一章 Mysql架构和历史 MySQL的两种基本锁 表锁和行级锁 表锁读锁互不排斥,写锁互斥。Mysql 服务器层会根据情况添加表锁,如ALTER TABLE操作。 行级锁主要在数据库的InnoDB,XtraDB等存储引擎中实现。 ACID 原子性,一致性,隔离性,持久性。 四种隔离级别 READ UNCOMMITTED(未提交读)事务中的修改,即使没提交,对其他事务也是可见的。事务未提交,其他事务也可以读取,即为脏读。 READ COMMITTED(提交读)绝大多数数据库的默认隔离级别(MySQL不是)。一个事务的修改在未提交之前对其他事务是不可见的。也可以叫做不可重复读,即两次同样的查询,读取到的结果可能不一致。 REPEATABLE READ(可重复读)可重复读保证了在同一事务中多次读取同样的查询,结果是一致的。它解决了脏读的问题。无法解决幻读的问题。可重复读是MySQL的默认隔离级别。幻读指的是某个事务在读取一定范围内的记录时,另一个事务在该范围内插入了新的记录,当这个事务再次读取时,会产生幻行的问题。InnoDB,XtraDB等引擎,通过多版本并发控制解决了幻读问题。 SERIALIZABLE(串行化)强制事务串行执行,解决了幻读和脏读问题。一般性能太差,不会使用。 隔离级别 脏读可能性 不可重复读可能性 幻读可能性 加锁读READ UNCOMMITTED YES YES YES NOREAD COMMITTED NO YES YES NOREPEATABLE READ NO NO YES NOSERIALIZABLE NO NO NO YES 死锁 死锁处理: InnoDB处理死锁的方式是将持有最少行级排他锁的事务回滚。 MySQL中的事务 MySQL提供的官方支持事务的引擎有InnoDB和NDB Cluster,第三方的有XtraDB和PBXT。- MySQL默认采用AUTOCOMMIT模式,无显示指定的情况下默认每一次查询都是一次事务提交。可以通过命令SHOW VARIABLES LIKE ‘AUTOCOMMIT’来获取当前是否开启。没开启则直到显式的调用COMMIT或者ROLLBACK才表示事务结束。注意:存在一些命令会在执行前导致强制执行COMMIT操作的。如DDL中的ALTER TABLE。请检查官方给出的会导致强制提交的语句。 MySQL可以通过执行SET TRANSACTION ISOLATION LEVEL来设置隔离级别,可以设置session级别的或者在配置文件中设置整个数据库级别的。mysql>SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; MySQL服务器层不管理事务,事务由下层的存储引擎实现,所以同一事务中,使用多种存储引擎是不可靠的。 特定语句的加锁方式 SELECT …… LOCK IN SHARE MODE SELECT …… FOR UPDATE MySQL的多版本并发控制(MVCC) 典型的MVCC有乐观并发控制和悲观并发控制。 以InnoDB的MVCC为例,它是通过在每行记录后面保存两个隐藏的列来实现的。两个列分别保存了行的创建时间和过期时间(删除时间)。(注意这里实际存储的是系统版本号,每开始一个新事务,系统版本号都会加1) SELECT 查找<=当前事务版本的数据行,以确保事务读取的行,要么是事务开始前已存在的,要么是当前事务新增或修改过的。 删除的版本未定义或大于当前事务版本,这样可以确保事务读取到的数据在事务开始之前一定未被删除。 INSERT 新增的行保存当前事务的版本号为行版本号。 DELETE 删除的行保存当前事务的版本号为行删除标识。 UPDATE 新增一条记录保存当前事务的版本号为行版本号,同时将当前系统版本号作为原来行的删除标识。 MVCC只在REPEATABLE READ和READ COMMITTED两个隔离级别下工作,READ UNCOMMITTED总是会读取最新的数据行,而不是符合当前事务版本的行,而SERIALIZABLE则会对所有读取行都加锁。 MySQL引擎 可以通过命令来获取当前版本支持的引擎。 mysql> show engines MySQL存储引擎 MySQL创建表,会在数据库子目录下创建一个和表同名的.frm文件,用于保存表的定义。可以通过以下命令查看 mysql>SHOW TABLES STATUS LIKE 表名 InnoDB引擎概览 存储在表空间中,4.1后的版本,每个表的数据和索引存放在单独的文件中。 采用MVCC来支持高并发,支持全部四种隔离级别,默认级别为REPEATABLE READ可重复读,并且通过间隙锁(next key locking)策略防止幻读。 InnoDB表是基于聚簇索引建立的,聚簇索引主键查询性能很高,但二级索引(即非主键索引中)必须包含主键列,因而主键列应尽可能的小。 InnoDB可以支持热备份,无需停止读写。 MySQL5.1以后的默认引擎,支持自动崩溃恢复特性。5.1以前是MyISAM为默认引擎。 MyISAM引擎概览 将表存储在两个文件中,数据文件和索引文件,分别以.MYD和.MYI为扩展名。 支持表级锁,不支持行级锁。读取加共享锁,写入加排他锁。支持并发插入,即在表有读取查询的同时,有插入操作。 支持修复。通过CHECK TABLE mytable来检查表的错误,如果有错误,可以用REPAIR TABLE mytable来进行修复。如果MySQL服务器已经关闭,可以通过myisamchk来检查和修复操作。 索引,MyISAM支持前缀索引,和基于分词创建的全文索引。 支持延迟更新索引键,通过DELAY_KEY_WRITE来设置,只有在清理键缓冲区或者关闭表的时候才会将对应的索引块写入到磁盘。如果数据库宕机,可能会导致索引损坏。 MyISAM压缩表比较适合数据导入后,不再进行修改操作的表。 MyISAM最大的性能问题就是表锁问题。 Archive引擎概览 Archive存储引擎只支持INSERT和SELECT操作,5.1之前也不支持索引。比较适用于日志和数据采集类的应用。 支持行级锁和专用缓冲区,支持一致性读及批量插入对读操作的不可见性。但它不是事务型引擎。 Blackhole引擎概览 该引擎它并没有实现存储机制,会丢弃所有的插入数据,不做保存,但服务器会记录Blackhole表的日志。可以用于复制数据到备库。 CSV引擎概览 CSV引擎可以实现普通CSV文件作为MySQL表来读取和操作,因此可以用于一种数据交换机制。 Federated引擎概览 Federated引擎是访问其他MySQL服务器的一个代理,他创建一个到远程MySQL服务器的客户端连接,并将查询传输到远程服务器执行,提取出需要的数据。 Federated可以在本地数据库中创建一个远端数据表的“软链接”。这样,访问远端数据表就如同访问本地数据表。(远端可以是不同主机上的不同实例) 手动安装命令mysql> install plugin federated soname ‘ha_federated.so’ Memory引擎 数据保存在内存中,无需磁盘的io操作,重启后,表结构还在,数据清空。 比MyISAM引擎快一个数量级。 支持的场景多样用于查找或者映射表用于缓存周期性聚合结果用于保存数据分析产生的中间数据。 支持Hash索引,查询速度非常快。 表级锁,行固定不支持VARCHAR,不支持BLOB或TEXT。 Merge引擎 MyISAM的一个变种 多个MyISAM表合并而来的虚拟表 之前用于数据仓库或日志类应用。引入分区功能后,已废弃。 NDB集群引擎 MySQL服务器,NDB集群存储引擎,NDB数据库组成MySql集群。 第三方引擎 OLTP类引擎 XtraDB引擎 基于InnoDB引擎的改进版,包含在Percona Server和Maria DB中 OLTP类引擎 在性能,可测量性和灵活性上有所提升 PBXT引擎 OLTP类引擎 支持ACID事务和MVCC 支持引擎级别的复制,外键约束 支持固态存储SSD,优化了大值类型,如BLOB。 TokuDB引擎 分形树的索引数据结构,与缓存无关,大小超过内存性能也不会下降。 大数据存储引擎,高压缩比,可创建大量索引。 RethinkDB 为固态存储SSD而设计的。 采用了一种只能追加的写时复制B树作为索引的数据结构 面向列的存储引擎 infobright 为数据分析和数据仓库设计的面向列的存储引擎。 不支持索引,块结构排序 社区存储版本 Aria/Groonga/OQGragh/Q4M/SphinxSE/Spider/VPForMySQL 小结 MySQL分层架构,上层是服务器层的服务和查询执行引擎,下层是存储引擎。优先建议用InnoDB引擎,如果用全文索引可以用InnoDB和Sphinx组合,而不用MyISAM。MyISAM比较适合日志类应用。选取MySql引擎的考虑因素,备份/事务/崩溃恢复/特定的特性。日志类应用 MyISAM/Archive引擎比较合适订单类应用 InnoDB 第三章 服务器性能剖析 查询响应时间是数据库服务器性能度量的标准。 性能剖析有两种: 基于执行时间的分析 基于等待的分析 常用的性能剖析命令: Show Profile / Show Profiles Show Status 某些数据库活动的频繁程度 Show Global Status Show Processlist Strace 工具调查系统调用情况。 strace -cfp $pid of mysqld 小结 响应时间是定义性能最有效的方法。 测量的最佳起点是应用程序,而非数据库。 完整的测量需要用剖析器来分析大量的待分析数据。 第四章 schema与数据类型优化 数据类型的选择 更小的通常更好,一般情况下,更小的数据类型更快,占用更少的磁盘,cpu和内存,处理时需要的cpu周期也更少。 简单就好,简单的数据类型会占用更少的cpu周期。如存储时间应该用date,time或datetime而非字符串,存储ip应该用整型。 尽量避免NULL,NULL值很难优化,会占用更多的存储空间。对于要索引的列,尽量避免为null。 数据类型的别名不会有性能损耗,但是肯能会令人不解。 整数类型 有符号和无符号类型使用相同的存储空间,并具有相同的性能。 MySQL可以为整数类型指定宽度,大多数情况是没有意义的。只是用来显示字符个数,对于存储和计算而言,INT(1)和INT(20)是相同的。 实数类型 带有小数部分的数字。也可以存储DECIMAL,比BIGINT还大的整数。 MySQL既支持精确类型,也支持不精确类型。 MySQL使用DOUBLE做内部浮点计算的类型,使用DECIMAL来做存储类型。 可以通过乘以响应的倍数来用BIGINT代替DECIMAL。 字符串类型 VARCHAR VARCHAR类型可变长字符,如果MySQL表ROW_FORMAT=FIXED,定长存储。 VARCHAR适合的场景,字符串列的最大长度比平均长度大很多,列更新很少,碎片少。使用了UTF-8这样的可变长字符集。 InnoDB会把VARCHAR存储为BLOB。 CHAR CHAR类型定长。 不易产生碎片。 最好的策略是只分配真正需要的空间。 BOLB和TEXT类型 MySQL对TXET或者BLOB的排序与其他类型不同,它只对每个列的最前max_sort_length字节进行排序。如果只需要排序前面一小部分字符,则可以减少max_sort_length的配置,或者使用ORDER BY SUBSTRING(column,length)。 MySQL不能将BOLB和TEXT列全部长度的字符来进行索引。 MySQL在查询时如果使用了BLOB或者TEXT列会使用磁盘临时表,这会导致严重的性能开销。最好的解决方案是尽量避免使用这两种类型。如果不得不用,可以考虑用SUBSTRING(column,length)的方式取足够短的字符,来保证使用的临时表大小,小于max_heap_table_size或tmp_table_size,从而不会由内存临时表转为磁盘临时表。 如果EXPLAIN执行计划的Extra列包含了Using temporary,则说明这个查询使用了隐式临时表。 枚举类 MySQL会把枚举值存储为整数,枚举的字符串列表是固定的,如果添加或删除字符串,需要ALTER TABLE。可以考虑用枚举替代字符串。 DATETIME和TIMESTAMP DATETIME 范围1001-9999年,精度为秒 存储格式为YYYYMMDDHHMMSS,与时区无关 存储占8个字节 TIMESTAMP 1970年1月1日午夜-2038年。 占用4个字节 默认列为NOT NULL。 MariaDB支持微秒级别的时间存储。 位数据类型 BIT 5.0版本之前BIT是TINYINT的同义词。 可以使用BIT列在一列中存储一个或多个true/false值。 BIT列最大长度是64个位 BIT在MYSQL中当做字符类型,而非数字类型。一般不建议用BIT 如果想在BIT中存储一个true或者false,另一个方法是创建一个可以为空的CHAR(0)列。 SET SET数据类型,改变列需要ALTER TABLE,对于大表来说操作比较昂贵。 很难使用索引。 选择标识符 一般标识列可能与其他值进行比较,或者寻找其他列,或者作为外键使用。因而选择标识符数据类型时,需要考虑存储类型,以及计算和比较的成本。最好的存储类型是整数类型。 对于标识列来说,ENUM和SET通常都是个糟糕的选择。他们更适合存储固定的信息。 尽量避免字符串作为标识列,因为占空间和速度较慢。MYISAM引擎会对字符串默认做压缩索引的处理,造成查询更慢。 完全随机字符串也会有影响,如MD5(),SHA1()或者UUID()产生的字符串,这些函数产生的新值会任意分布在很大的空间内,导致一些插入和查询语句很慢。IO代价比较高。 如果存储uuid值,最好移除掉-符号,更好的方式是用UNHEX()函数将其转换为16字节的数字存储在BINARY(16)列中。检索时可以通过HEX()函数来格式化为16进制格式。 #####MySQL Schema设计中的陷阱 太多的列 MySQL存储引擎API工作时需要在服务层和存储引擎层之间通过缓冲格式拷贝数据,然后在服务层将缓冲内容编码成各个列。 太多的关联 MySQL每个关联操作最多只能有61张表。如果希望查询执行的快且并发性好,单个查询最好在12个表以内。 防止过度的使用枚举 不要过度拒绝NULL #####范式和反范式 范式化的数据库中,每个事实数据会出现并且只出现一次。相反,在反范式化的数据库中,事实数据可能会出现多次,通俗来讲就是我们所说的字段冗余。 范式化的好处 范式化的更新操作通常比反范式化要快。 需要修改的数据更少。 范式化的表通常更小,可以更好的放在内存,操作速度更快。 多余的数据少,意味着检索列表数据时更少需要distinct或者group by语句。 范式化的缺点 通常需要关联,会造成查询上的代价变高。 反范式的好处 schema所有数据都在一张表中,可以很好的避免关联。 避免了随机IO。 更有效的使用索引。 最常见的反范式的方法是缓存或者复制。 MySQL本身并不支持物化视图。需要第三方支持。 #####如何加快ALTER TABLE的速度 常见方式: 现在一台不提供服务的机器上执行ALTER TABLE操作,然后和提供服务的主库进行切换。 影子拷贝的方式。用要求的表结构创建一张和源表无关的新表,然后通过重命名和删表操作交换两张表。可以用一些工具来处理 online schema change或openark。如果改变列可以用ALTER COLUMN的方式来处理。它只会修改.frm文件,而不涉及到表数据,因此非常快。具体地 创建一张有相同结构的空表做修改。 执行FLUSH TABLES WITH READ LOCK。这将会关闭所有正在使用的表,并且,禁止任何表被打开。 交换.frm文件。 执行UNLOCK TABLES来释放读锁。 DISABLE KEYS只对非唯一索引有效。 #####总结 小而简单总是好的。 尽量避免过度设计。 使用小而简单的合适数据类型,除非真实数据模型中有确切需要,否则尽量避免使用NULL值。 尽量使用相同的数据类型存储相似或相关的值,尤其是要在关联条件中使用的列。 注意可变长字符串,其在临时表和排序时可能导致悲观的按最大长度分配内存。 尽量使用整型定义标识列。 避免使用MYSQL已经遗弃的特性,如指定浮点数的精度或整数的显示宽度等。 小心使用ENUM和SET,不要滥用。 最好避免使用BIT。 Chapter 5 创建高性能索引索引基础 索引可以包含一个或多个列,列顺序有关,MySQL只能高效地使用索引的最左前缀列。 不同存储引擎索引的工作方式并不一样,并非所有的存储引擎都支持所有类型的索引。 B-Tree索引 大多数MySQL引擎支持BTree索引。Archive引擎除外,5.1版本以前不支持任何索引,以后的版本才开始支持单个自增列的索引。 InnoDB用的是B+Tree。 不同的存储引擎以不同的方式使用B-Tree索引,性能各不相同。MyISAM使用前缀压缩技术使得索引占用更少的空间,InnoDB则按照原有格式存储。MyISAM索引通过数据的物理位置引用被索引的行,而InnoDB则根据主键引用索引的行。 B-Tree即平衡二叉树。有序,且每个叶子到根节点距离相同。 BTree树搜索是从索引的根节点开始进行,根节点的槽中存放了指向子节点的指针,存储引擎根据这些指针向下查找。树的深度和表的大小直接相关。 索引对多个值进行排序的依据是CREATE TABLE语句中定义索引时列的顺序。 BTree索引适用于全键值,键值范围或最左前缀查找。 因为索引树中的节点是有序的,因而支持按值查找,也支持Order By操作(按顺序查找) BTree索引限制: 如果不是按照索引的最左列开始查找,则无法使用索引。 不能跳过索引中的列。 如果查询中有某个列的范围查询,则其右边所有列都无法使用索引优化查询。 在优化性能时,可能需要使用相同的列但顺序不同的索引来满足不同类型的查询需求。 哈希索引 哈希索引基于哈希表实现,只有精确匹配索引所有列的查询才有效。MySQL中只有Memory引擎显式的支持hash索引(memory引擎的默认索引类型)。如果多个列的hash值相同,则会用链表的形式存放多个记录到同一个hash条目中。 Hash索引的特点: Hash索引只包含hash值和行指针,而不存储字段值,所以不能使用索引的值来避免读取行。不过,访问内存中的行的速度很快,对性能影响不大。 Hash索引数据并不是按照索引值顺序存储的,也就无法进行排序。 Hash索引不支持部分索引匹配查找,因为Hash索引始终是使用索引列的全部内容来计算Hash值的。如在数据列(A,B)上建立Hash索引,如果查询只有数据列A,则无法使用该索引。 Hash索引只支持等值比较查询,包括=,in,<=>,不支持范围查询。 理论上访问Hash索引速度非常快,除非有很多hash冲突,这时会遍历索引值相同的链表查找。 Hash索引的应用场景:如需要查找很多关联表的场景。InnoDB有个自适应hash索引的功能,他会为访问频繁的索引值加一个hash索引,用于加快查找速度,这个用户无法控制,但可以关闭。 创建自定义Hash值的思路,在BTree基础上创建一个伪Hash索引,它使用Hash值作为二分查找的索引。在where语句中使用hash函数查询。自定义hash值得函数请使用crc32的,不要用sha1或者md5的(太长浪费空间)。但是如果数据量大,crc32会出现大量的hash冲突,这种情况最好自己实现一个64位的hash函数。 R-Tree 空间索引MyISAM支持空间索引,可以用于地理数据存储,数据需要用到Mysql的GIS相关函数。相对不太完善,一般都用PostgreSQL的PostGIS。 全文索引查找文本中的关键词,而非直接比较索引值。类似于搜索引擎。匹配方式也和其他索引不一样。相同的列可以同时创建全文索引和Btree索引。 其他其他三方数据库引擎的索引,如TokuDB的分形树索引 fractal tree index等。 索引的优缺点 大大减少了服务器需要扫描的数据量 可以帮助服务器避免排序和临时表 可以随机I/O变顺序I/O 当且仅当创建索引的代价比索引带来的性能提升小时,这样索引才是有效的。 高效使用索引的注意点 独立的列,索引列不能是表达式的一部分,也不能是函数的参数,否则引擎无法解析。 前缀索引和索引选择性,前缀索引可以索引开始的部分字符,这样大大节省了索引空间,提高索引效率,但会降低索引选择性。索引选择性是指,不重复的索引值(即基数)和数据表的记录总数的比值。索引选择性越高,查询效率越高。即可辨识度越高,性能越好,唯一索引选择性是1,性能也是最好的。 对于BLOB,TEXT和很长的VARCHAR列,必须使用前缀索引。MYSQL不允许索引这些列的完整长度。目标是长度尽可能短的情况下,保证选择性尽可能的高,可辨识度高。注意MYSQL无法使用前缀索引做ORDER BY 或GROUP BY,也无法使用前缀索引做覆盖扫描。 多列索引问题 常见的误区:为每个列创建索引,或者按照错误的顺序创建多个索引。 5.0以前的老的MySQL对于where条件中A or B的条件进行全表扫描,除非改成A条件 union B条件。5.0以后查询能够同时使用这两个单列索引进行扫描,再将结果合并。Or条件取并集,And条件取交集,组合前两种情况的联合和相交。 索引合并策略是Mysql5.0版本以后的一个新特性,但是这也意味着表上索引建的不好。当有多个AND条件,通常意味着需要一个包含这多个条件的多列索引。 选择索引列的基本法则当不需要考虑排序和分组时,将选择性最高的列放在前面。这时索引只优化WHERE条件的查找。如WHERE条件中有A和B,Where A and B,选择A在前还是B在前时,可以先跑一些查询看看两个条件下那个条件查询出来的结果更少,就把哪个条件放在前。如 select sum(A),sum(b) from table。但这种情况有可能比较片面。 因此最好使用select count(distinct 字段A)/count(1) , count(distinct 字段B)/count(1) from table来看那个字段对应的值可辨识度更高,来作为前面的字段。 where子句中排序,分组和范围条件等其他因素,对查询性能造成的影响可能更大。 聚簇索引聚簇索引并非单独的索引类型,而是一种数据存储方式。InnoDB的聚簇索引实际上在同一个结构中保存了B-Tree索引和数据行。聚簇表示数据行和相邻键值紧凑的存在一起。因为无法把数据行存放在两个不同的地方,因此一个表理论上只能有一个聚簇索引,不过覆盖索引可以模拟多个聚簇索引的情况。 以InnoDB举例,InnoDB一般通过主键聚集数据,如果未定义主键,InnoDB会选择一个唯一的非空索引代替。如果没有这样的索引,InnoDB会隐式定义一个主键来作为聚簇索引。 聚簇索引本身是个双刃剑,特别是做引擎迁移时尤其要注意性能。 优点: 将数据保存在一起,减少磁盘IO。 数据和索引一起放在BTREE上,查询速度更快。 使用覆盖索引扫描的查询可以直接使用页节点中的主键值。 缺点: 聚簇索引最大限度的提高了IO密集型应用的性能,对于数据全部在内存中的,没有优势。 插入速度严重依赖于插入顺序。按照主键顺序插入是加载数据到InnoDB表的最快方式。如果不是按主键顺序,最后加载完成后,使用Optimize table命令重新组织一下表。 更新索引的代价很昂贵。 基于聚簇索引的表再插入新行,或者主键被更新导致移位时,可能会造成页分裂。 聚簇索引可能导致全表扫面变慢。尤其是行比较稀疏,或者页分裂导致数据存储不连续时。 二级索引(非聚簇索引)可能比想象的更大,因为在二级索引的叶子节点包含了应用行的主键列。 二级索引访问需要两次索引查找,而不是一次。 InnoDB和MyISAM保存索引的差异 顺序主键可能的问题,高并发插入的热点问题,锁争用严重。 覆盖索引如果一个索引包含或者说覆盖所有需要查询的字段值,就称之为覆盖索引。 索引条目通常远小于数据行大小,可以极大的减小数据访问量,如果只读取索引的情况下。 因为索引是按照列值顺序存储的,至少单页如此,因而对I/O密集型的范围查询会比从随机从磁盘读取每一行数据的I/O要少的多。 一些存储引擎如MyISAM在内存中只缓存索引,数据则依赖操作系统来缓存,因此要访问数据需要一次系统调用,可能会导致性能问题。 对于InnoDB如果它的二级主键能够覆盖查询,则可以避免对主键索引的二次查询。 不是所有类型的索引都可以称为覆盖索引,覆盖索引必须要存储索引列的值,而hash索引,空间索引和全文索引等都不存储存储列的值。MySQL只能使用B-Tree索引做覆盖索引。另外不是索引的引擎都支持覆盖索引,如memory引擎目前是不支持的。 执行计划的extra中如果是using index则表明当前查询使用了覆盖索引查询。如: SELECT A,B from TABLE S 正好有A,B字段的索引就会走覆盖索引。 但是如果SELECT的时候用了*,而非具体的字段,就无法走覆盖索引啦。大多数存储引擎的覆盖索引,都只能覆盖那些只访问索引中部分列的查询。但也可以根据实际情况做一些优化,以利用的更多。如InnoDB的二级主键。 使用索引扫描来排序MySQL有两种方式生成顺序结果,排序方式或者根据索引顺序扫描。 如果执行计划中的type列值为index,则表示MySQL使用了索引扫描来做排序。 MySQL可以使用同一个索引即满足排序,又用于行查找。这种是最好的,最好的情况是索引记录就覆盖了查找的全部列,而不需要二次从索引再去查询列信息。 当索引的列顺序和Order by子句的顺序完全一致,且排序方向都一样时,Mysql才用索引对结果做排序。 如果查询关联多张表,则只有当Order By子句引用的字段全部为第一个表时,才能使用索引排序。 考虑索引需要考虑CPU密集型和IO密集型的差别,如MyISAM的压缩索引,对于CPU密集型的随机查找,压缩索引就会很慢。 重复索引和冗余索引一般情况下,增加新索引会导致INSERT,UPDATE,DELETE等操作速度变慢,特别是当新增索引后导致达到了内存瓶颈的时候。 Perconapt-index-usage 查询未使用的索引。 碎片化行碎片,行间碎片,剩余空间碎片 MyISAM都会有,InnoDB不会有行碎片,它会移动短小的行并重写到一个片段中。 总结选择编写索引的原则 单行访问总是很慢的。最好读取快中能包含尽可能多的所需要行,使用索引可以创建位置引用以提升效率。 按顺序访问范围数据是很快的。1.顺序IO不需要多次磁盘寻道,所以比随机IO快很多。2.如果服务器能够按需要顺序读取数据,那么就不再需要额外的排序操作,并且GROUP BY查询也无需在做排序和将行按组进行聚合计算了。 能用覆盖索引尽量用覆盖索引查询所需字段。 查询性能优化 查询优化,库表结构优化,索引优化都要考虑和调整。 大部分性能低下的查询都可以通过减少访问数据量的方式来优化。通常用两个步骤来分析: 确认应用程序是否检索了大量超过需要的数据,这意味着访问了太多的行,但有时间也可能是访问了太多的列。 查询了不需要的记录 多表关联返回全部列 SELECT总是查询全部列 重复查询相同的数据 确认Mysql服务器层是否在分析大量超过需要的数据行。 分析查询开销的三个指标:响应时间,扫描行数,返回行数。 响应时间是服务时间和排队时间之和。 explain中的Type列反应了访问类型。如果查询没有办法找到合适的访问类型,最好的办法就是加一个合适的索引。 MySql能够使用如下三种方式应用Where条件 在索引中使用where条件来过滤不匹配的记录。存储引擎层完成。 使用索引覆盖扫描来返回记录,直接从索引中过滤不需要的记录并返回命中的结果。服务器层完成。 数据表中返回数据,然后过滤不满足条件的记录。服务器层完成。 如果发现查询扫描了大量的数据,只返回了少数的行,则可以从以下几点考虑优化 使用覆盖索引,查询的列放在索引中,无需再次回表查询。 改变库表结构。 重写这个查询。 1.Mysql运行多个小查询已不是问题。其他条件都相同的情况下,尽可能少的查询当然是更好的。 2.将大查询分解为小查询。 3.分解关联查询的好处: a. 让缓存的效率更高。 b.将查询分解后,执行单个查询可以较少锁竞争。 c.在应用层做关联,可以更容易对数据库进行拆分,更容易做到高性能扩展。 d.查询本身效率也可能会有所提升。 e.可以减少冗余记录的查询。 MySQL 查询Mysql查询的基本路径 MySQL客户端和服务器之间采用的是半双工通信,任何一个时刻都是要么服务器发送数据给客户端,要么客户端发送数据给服务器,不能同时发生。客户端用一个单独的数据包将查询传给服务器,这也是为什么查询语句很长时,参数max_allowed_packet很重要。相反的,服务端返回给客户端的数据包会分为很多个,客户端必须完整的接受整个返回结果,这也是为什么要加limit的原因。 查询状态用SHOW FULL PROCESSLIST命令查询所有的状态。 Sleep — 线程正在等待客户端发送新的请求。 Query — 线程正在执行查询或者正在将结果发送给客户端。 Locked — MySQL服务层,该线程正在等待表锁。存储引擎级别实现的锁,如InnoDB的行锁,并不会体现在线程状态中。 Analyzing and statistics — 线程正在收集存储引擎的统计信息,并生成查询的执行计划。 Copying to tmp table [on disk] — 线程正在执行查询,并将结果复制到临时表,一般要么是GROUP BY , FILE SORT,或者UNION等。如果包含on disk标识,表示MySQL正在将一个内存临时表刷入磁盘。 Sorting result — 线程正在对结果集进行排序。 Sending data — 线程可能在多个状态之间传输数据 或者 在生成结果集 或者在向客户端返回数据 Mysql 查询优化时选择错误的执行计划的原因 统计信息不准确 执行计划中的成本估计不等同于执行成本。 MySQL认为的最优和实际用户的目标并不一定一致。 MySQL从不考虑并发执行的场景。 MySQL也并不是任何时候都是基于成本的优化,有时也会基于一定的规则。 MySQL不考虑不受其控制的操作成本,如执行存储过程或者用户自定义函数等成本。 优化器也无法估算所有的执行计划。 MySQL的优化分为静态优化和动态优化,静态优化可以认为是编译时优化,动态优化可以认为是执行时优化。 MySQL可以处理的优化,有以下几种, 重新定义表关联顺序 将外连接转换为内连接 使用等价变化简化和规范表达式 优化count(),min()和max() 预计并转化为常数表达式 覆盖索引查询 子查询优化,减少子查询对数据的访问 提前终止查询 等值传播 列表IN()的比较 MySQL的执行计划总是一颗左侧深度优先的树。 ","raw":null,"content":null,"categories":[],"tags":[{"name":"MySQL,数据库设计","slug":"MySQL-数据库设计","permalink":"http://yoursite.com/tags/MySQL-数据库设计/"}]},{"title":"正则表达式Tips","slug":"regextips","date":"2017-08-23T01:09:07.000Z","updated":"2017-08-30T09:12:46.000Z","comments":true,"path":"2017/08/23/regextips/","link":"","permalink":"http://yoursite.com/2017/08/23/regextips/","excerpt":"","text":"正则表达式引擎正则表达式的正则引擎主要分为两大类:DFA和NFA,NFA又细分为传统型NFA,POSIX NFA。 DFA(确定型有穷机) 以文本主导的匹配。匹配规则是从匹配文本入手,从左至右,每个字符的匹配次数不会超过两次。 多条子表达式会在扫描文本时,同时进行匹配 不支持捕获组,各种引用,贪心修饰符等。 时间复杂度是多项式级别的。 可以确保匹配最长的可能的字符串。 使用DFA引擎 awk,MySQL,egrep等 NFA (非确定性有穷自动机) 以正则表达式主导的匹配。 时间复杂度最好是多项式级别的,最差是指数级别的。 支持捕获组和各种引用等。 采用贪婪匹配回溯的方式匹配。 如果是POSIX NFA,则会在可以确保已经找到可能的最长的匹配之前,将继续回溯。因而POSIX NFA的效率低于传统的NFA效率。 使用传统型NFA的引擎 GUN Emacs,Java,.NET,Ruby,Perl,PHP,Python,Sed,vi等。 使用POSIX NFA的引擎 使用DFA/NFA混合的引擎 GNU awk,GNU grep/egrep等。 注意DFA会在编译阶段对表达式进行分析,生成映射关系,因而编译阶段NFA效率高于DFA。 匹配过程如Hello terrence,正则表达式为 /te(brence|rrence|rrencm)/ NFA 匹配 先在字符串中查找t然后匹配其后是否为e ,如果是e则继续,查找其后是否为b,如果不是则匹配其后是否为 r (此时淘汰brence选择项)。然后继续看其后是否依次为r,e,n,c,接着测试是否为 e,是e则匹配成功,不是则测试是否为m。 DFA匹配 从 Hello 中 H 开始依次查找 t,定位到 t ,已知其后为 e ,则查看表达式是否有 e ,此处正好有 e 。然后字符串 e 后为 r ,DFA依次测试表达式,此时 brence 不符合要求淘汰。rrence 和 rrencm 符合要求,然后DFA依次检查字符串,检测到rence 中的 e 时只有rence 分支符合。 NFA的匹配效率Alternation Can Be ExpensiveAlternation可能会导致更多的回溯。 举例如下:具有Alternation的正则:u|v|w|x|y|z固定的正则:[uvwxyz] 具有Alternation的正则每次匹配失败都需要回朔,固定的正则将一次匹配所有的。如果失败,只匹配一次。即极限情况下,具有Alternation的正则需要回溯的次数是固定正则回溯次数的六倍。 尽可能的明确^或$标识尽量确定行首或行尾的特征,使用^或者$来标识开始或结束,特别是表达式中包含贪婪匹配的元素时。 举例:假如用non-capturing group (?:) 匹配 http://baidu.com正则表达式如下(http?|ftp)://([^/\\r\\n]+)(/[^\\r\\n])?如果我并不关心protocol,可以加入non-capturing group(?:) ,改为(?:http?|ftp)://([^/\\r\\n]+)(/[^\\r\\n])?这样匹配时,会忽略掉protocol,提升效率。 提取重复内容h(?:as|at) vs. (?:has|hat) 重复功能可以提取出来,当作一个函数,从而减少回朔的次数。这里前面的正则表达式效率高于后面的。 将最有可能的匹配放在最前面假如我想匹配各种后缀(com net edu等)结尾的url,正则表达式如下 (?:com|edu|org|net|…)实际上每个后缀的域名数量是不同的,假如 .net .org 的域名很多,为了提高效率我们可以将域名按照数量从高到低排序,以减少回溯次数。原正则表达式则变为 (?:net|org|com|edu|…)。 用lookAround剔除掉部分无效的匹配","raw":null,"content":null,"categories":[],"tags":[{"name":"regex,DFA,NFA","slug":"regex,DFA,NFA","permalink":"http://yoursite.com/tags/regex,DFA,NFA/"}]},{"title":"Java Memory Model","slug":"jmm","date":"2017-05-13T03:57:31.000Z","updated":"2017-05-14T04:27:49.000Z","comments":true,"path":"2017/05/13/jmm/","link":"","permalink":"http://yoursite.com/2017/05/13/jmm/","excerpt":"","text":"##Java Memory Model线程之间的通信机制有两种:共享内存和消息传递。 线程之间的公共状态,在共享内存的并发模型里,是通过写-读内存中的公共状态来隐式进行通信。在消息传递的并发模型里,是通过明确的发送消息来显式进行通信。 线程之间的状态同步,在共享内存并发模型里,必须显式地指定某个方法或某段代码需要在线程之间互斥执行。在消息传递的并发模型里,由于消息的发送必须在消息的接收之前,因此同步是隐式的。 Java并发采用的是共享内存模型,线程之间的内存公共状态的读写通信是透明的,状态同步是显式的,保证同步操作时线程之间是互斥的。 JVM内存模型中,程序计数器,虚拟机栈,本地方法栈都是线程私有的,他们存储的信息不存在可见性问题,是线程安全的。堆和方法区是线程共享的,存在可见性问题。 线程之间的通信是由JMM决定的,它决定了一个线程对共享变量的写入何时对另一个线程可见。相当于每个线程都有一个主内存中的变量副本,用于自身的操作。 JMM规范中有两条规定: 线程对共享变量的所有操作都必须在自己的工作内存中进行,不能直接从主内存中读写。 不同线程之间无法直接访问其他线程工作内存中的变量,线程间变量值的传递需要通过主内存来完成。 happen-before原则一个线程中的每个操作,happens-before 于该线程中的任意后续操作。一个监视器解锁,happens-before 于随后对这个监视器锁的加锁。一个volatile域的写,happens-before 于任意后续对这个volatile域的读。如果A happens-before B,且B happens-before C,那么A happens-before C。 两个操作之间具有happens-before关系,并不意味着前一个操作必须要在后一个操作之前执行。happens-before仅仅要求前一个操作(执行的结果)对后一个操作可见,且前一个操作按顺序排在第二个操作之前。 指令重排序happen-before规则是编译器重排序和处理器重排序的基础。程序执行时,编译器和处理器会根据执行效率对程序进行指令重排序。编译器重排序,编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。(编译器级别)指令级重排序,现代处理器采用了指令级并行技术(Instruction-Level Parallelism, ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。(cpu级别)内存系统重排序。由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。(CPU级别) 编译器重排序由JMM的编译器重排序规则来禁止,处理器重排序可以通过内存屏障来禁止重排序。每个处理器processor的写缓冲区仅对自身可见。常见的处理器都允许store-load的重排序。 JMM的内存屏障分为以下四类:JMM内存屏障分为四类 LoadLoadBarries如:Load1–>LoadLoad–>Load2 确保load1数据装载一定在load2及所有后续装载指令1前装载。 StoreStoreBarries如:Store1–>StroreStore–>Store2 确保Store1数据对其他处理器可见一定在Store2及所有后续存储指令存储的前面。 LoadStoreBarries如:Load1–>LoadStore–>Store2 StoreLoadBarries如: Store1–>StoreLoad–>Load2 as-if-serial语义编译器和处理器不会改变存在数据依赖关系(仅针对单个处理器中执行的指令序列和单个线程中执行的操作,不包括不同处理器之间和不同线程之间的)的两个操作的执行顺序。因此重排序并不会给单线程带来内存可见性问题。这个就是as-if-serial语义。 as-if-serial:无论如何重排序,程序执行的结果应该与代码顺序执行的结果一致(Java编译器,运行时和处理器都会保证java在单线程下遵循as-if-serial语义)。 原子性JMM对正确同步的多线程程序的内存一致性做了如下保证:如果程序是正确同步的,程序的执行将具有顺序一致性(sequentially consistent)即程序的执行结果与该程序在顺序一致性内存模型中的执行结果相同. 对64位(long,double)变量的读写可能不是原子操作,java内存模型允许JVM将没有被volatile修饰的64位数据类型的读写操作划分为两次32位的读写操作来完成。一般商用的JVM都对这部分做了原子性操作的保证,不用特意处理。导致问题:可能会出现读取到”半个变量”的情况解决办法:加volatile关键字。 加volatile关键字volatile能够保证volatile变量的可见性,不能保证volatile变量复合操作的原子性volatile如何实现内存可见性,深入来说通过加入内存屏障和禁止重排序优化来实现的。 对volatie变量执行写操作时,会在写操作后加入一条store屏障指令。 会把CPU写缓冲区的缓存强制刷新到主内存中。 防止处理器把volatile变量前面的操作重排序到valatile写操作之后。 对volatie变量执行读操作时,会在读操作前加入一条load屏障指令。 CPU缓冲区失效,从主内存读。 防止重排序。 volatile禁止重排序的规则 当第二个操作是volatile写时,不管第一个操作是什么,都不能重排序。这个规则确保volatile写之前的操作不会被编译器重排序到volatile写之后。 当第一个操作是volatile读时,不管第二个操作是什么,都不能重排序。这个规则确保volatile读之后的操作不会被编译器重排序到volatile读之前。 当第一个操作是volatile写,第二个操作是volatile读时,不能重排序。 volatile内存屏障的规则 在每个volatile写操作的前面插入一个StoreStore屏障。 在每个volatile写操作的后面插入一个StoreLoad屏障。 在每个volatile读操作的后面插入一个LoadLoad屏障。 在每个volatile读操作的后面插入一个LoadStore屏障。 锁和CAS锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息。JVM中concurrent包中的锁主要依赖的是AbstractQueuedSynchronizer框架,即AQS实现的。AQS会维护一个volatile的Long型同步状态。 java的CAS同时具有 volatile 读和volatile写的内存语义,即编译器不能对CAS与CAS前面和后面的任意内存操作重排序。CAS会使用现代处理器上提供的高效机器级别原子指令,这些原子指令以原子方式对内存执行读-改-写操作,这是在多处理器中实现同步的关键。volatile变量的读/写和CAS可以实现线程之间的通信,如下 A线程写volatile变量,随后B线程读这个volatile变量。 A线程写volatile变量,随后B线程用CAS更新这个volatile变量。 A线程用CAS更新一个volatile变量,随后B线程用CAS更新这个volatile变量。 A线程用CAS更新一个volatile变量,随后B线程读这个volatile变量。 conocurrent包的通用实现模式 声明共享状态为volatile 使用CAS的原子条件更新来实现线程之间的同步 配合以volatile和CAS所具有的volatile读/写的内存语义,来实现线程之间的通信。 final对于final域,只要对象是正确构造的(被构造对象的引用在构造函数中没有“逸出”),那么不需要使用同步(指lock和volatile的使用),就可以保证任意线程都能看到这个final域在构造函数中被初始化之后的值。JSR133对写final域的重排序规则会要求译编器在final域的写之后,构造函数return之前,插入一个StoreStore障屏。读final域的重排序规则要求编译器在读final域的操作前面插入一个LoadLoad屏障。 JMM在不同的处理器中需要插入的内存屏障的数量和种类也不相同。 对于不计写读操作的处理器,JMM需要插入的内存屏障为StoreLoad Barries 对于不计写读操作和写写操作的处理器, JMM需要插入的内存屏障为StoreLoad Barries,StoreStore Barries. 对于不计写读,写写,读写和读读操作的处理器,JMM需要插入的内存屏障为StoreLoad Barries,StoreStore Barries,LoadStore Barries,LoadLoad Barries. JSR-133对JDK5之前的旧内存模型的修补主要有两方面: 增强volatile的内存语义。旧内存模型允许volatile变量与普通变量重排序。JSR-133严格限制volatile变量与普通变量的重排序,使volatile的写-读和锁的释放-获取具有相同的内存语义。 增强final的内存语义。在旧内存模型中,多次读取同一个final变量的值可能会不相同。JSR-133为final增加了两个重排序规则。保证了final初始化的安全性。即如果一个对象的引用在构造阶段不允许逸出(escape),那么一旦构造函数完成,并且线程发布了对另一个对象的引用,那么在不使用同步的条件下,这个对象的final域字段就能保证对所有其他线程是可见的、正确的并且是不变的。","raw":null,"content":null,"categories":[],"tags":[{"name":"JMM","slug":"JMM","permalink":"http://yoursite.com/tags/JMM/"}]},{"title":"javaio","slug":"javaio","date":"2017-04-11T15:03:54.000Z","updated":"2017-04-11T15:03:54.000Z","comments":true,"path":"2017/04/11/javaio/","link":"","permalink":"http://yoursite.com/2017/04/11/javaio/","excerpt":"","text":"","raw":null,"content":null,"categories":[],"tags":[]},{"title":"Java Class文件","slug":"javaclass","date":"2017-04-10T15:50:29.000Z","updated":"2017-04-10T16:00:30.000Z","comments":true,"path":"2017/04/10/javaclass/","link":"","permalink":"http://yoursite.com/2017/04/10/javaclass/","excerpt":"","text":"Java Class文件Java Class文件是Java支持跨平台的基础,它是由不同位数的字节组成的,也称为字节码文件。其组织结构如下","raw":null,"content":null,"categories":[],"tags":[{"name":"JDK,Class文件","slug":"JDK-Class文件","permalink":"http://yoursite.com/tags/JDK-Class文件/"}]},{"title":"Java类编译,加载,执行","slug":"javacompile","date":"2017-03-20T15:16:36.000Z","updated":"2017-07-22T14:54:23.000Z","comments":true,"path":"2017/03/20/javacompile/","link":"","permalink":"http://yoursite.com/2017/03/20/javacompile/","excerpt":"","text":"Java类编译,加载和执行Java类的编译,加载和执行,常常被人们忽略。现在我们来看看这块具体的相关知识。 1.Java类编译Javac编译类的步骤如下: a.调用com.sun.tools.javac.parser.Scanner对java类做词法分析,将代码字符串转变为token序列。b.调用com.sun.tools.javac.parser.Parser对token序列做语法分析,将token序列生成抽象语法树。c.调用com.sun.tools.javac.comp.Enter将符号输入到符号表,通常包括确定类的超类型和接口,类构造器及符号等。d.如果当前类包含用户自定义的注解,则会进行注解处理,生成附加代码或进行特殊检查,并再次进入a-c的步骤来进行词法和语法分析并输入到符号表。e.基于抽象语法树进行语义分析,将语法树中的名字,表达式等元素与变量,方法,类型作关联,检查变量使用前是否已声明,推导泛型参数类型,检查类型匹配,检查变量赋值,检查语句可达性,去掉语法糖等。d.语义分析完成后,调用com.sun.tools.javac.jvm.gen来生成class文件。生成的大致步骤如下:将实例成员初始化器收集到构造器中,将静态成员初始化器收集到()中d.语义分析完成后,调用com.sun.tools.javac.jvm.gen来生成class文件。生成的大致步骤如下:将实例成员初始化器收集到构造器中,将静态成员初始化器收集到()中。后续遍历抽象语法树,进行少量的代码转换,生成字节码,最后从符号表生成class文件。e.class文件不仅包含了字节码,还包含了JVM用来执行class的附加信息。概括来说:包括class文件格式版本号及各部分的数量与大小信息。元数据-类、继承的超类、实现的接口声明,域与方法声明信息和常量池。方法信息-字节码,异常处理表,求值栈与局部变量区大小,求值栈的类型记录,调式用符号信息。 2.Java类加载Java JVM将类加载分为三个阶段:装载,链接和初始化。链接又细分为验证,准备,解析三个阶段。其中装载,验证,准备和初始化发生的顺序是确定的,但之间却没有严格的顺序完成的要求,通常都是各个阶段间交替混合进行的。而解析阶段没有这种顺序的要求,可能再初始化之后,也可能在其之前。 .class文件装载和链接过程完成后,即将二进制的字节码转换为Class对象,初始化过程不是加载类时必须触发的,但最迟必须在第一次主动使用对象前执行(给静态变量赋值,调用()等)。而一个类的声明周期除了上面三个阶段外,还包括使用,卸载两步。如下所示: a.装载load(查找并加载类的二机制数据)装载主要涉及的类为java.lang.ClassLoader,装载Class的二进制字节码并加载到JVM中,通过类的全限定名及类加载器完成类加载。具体的加载过程如下:(1)通过类的全限定名来获取该类的二进制字节流,来源可能是一个本地class文件,网络下载的class文件,动态生成的class文件,jar或者zip中的class文件,或是专有数据库中提取的class文件等。(2)将该字节流所代表的静态存储结构存储到方法区,转换为方法区的数据结构。(3)在堆内存中生成一个当前类的java.lang.Class对象,作为方法区数据结构的访问入口。JVM比较两个类是否相同,不仅比较类的全名,还比较加载此类的类加载器。只有两者都相同的时候,认为两个是相同的类。这也是代理模式的设计动机,即为了保证Java核心库的类型安全。所有Java应用都至少需要引用java.lang.Object类,通过代理模式,对于Java核心库的类的加载工作由引导类加载器来统一完成,保证了Java应用所使用的都是同一个版本的Java核心库的类,是互相兼容的。真正完成类的加载工作是通过调用defineClass来实现的,如果调用失败会报java.lang.NoClassDefFoundError;而类启动的加载过程是通过调用loadClass来实现的,如果失败会报java.lang.ClassNotFoundException。 b.链接(确保被加载的类的正确性)链接主要是对二进制class文件进行格式校验,初始化装载类中的静态变量及当前类中引用的接口和其他类。链接可以分为三个阶段验证,准备,解析。验证阶段,如果class文件格式验证有问题,会抛出java.lang.VerifyError的错误。校验过程如果需要加载其他的引用接口和类,也会进行加载,如果找不到,则会报java.lang.NoClassDefFoundError。验证分为四个方面的检查:(1)文件格式验证:验证字节流是否符合class文件格式的要求,如是否以OXCAFEBABE开头,版本号是否支持等。(2)原数据验证:对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言规范的要求,如这个类是否有父类。(3)字节码验证:通过数据流和控制流分析,确定程序语义是合法的。(4)符号引用验证:确保解析动作能正确执行。 准备阶段,为类的静态变量分配内存,并将其初始化为默认值,是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配。对于该阶段有以下几点需要注意:1、这时候进行内存分配的仅包括类变量(static),而不包括实例变量,实例变量会在对象实例化时随着对象一块分配在Java堆中。2、这里所设置的初始值通常情况下是数据类型默认的零值(如0、0L、null、false等),而不是被在Java代码中被显式地赋予的值。假设一个类变量的定义为:public static int value = 100;那么变量value在准备阶段过后的初始值为0,而不是100,因为这时候尚未开始执行任何Java方法,而把value赋值为100的putstatic指令是在程序编译后,存放于类构造器()方法之中的,所以把value赋值为100的动作将在初始化阶段才会执行。· 这里还需要注意如下几点:· 对基本数据类型来说,对于类变量(static)和全局变量,如果不显式地对其赋值而直接使用,则系统会为其赋予默认的零值,而对于局部变量来说,在使用前必须显式地为其赋值,否则编译时不通过。· 对于同时被static和final修饰的常量,必须在声明的时候就为其显式地赋值,否则编译时不通过;而只被final修饰的常量则既可以在声明时显式地为其赋值,也可以在类初始化时显式地为其赋值,总之,在使用前必须为其显式地赋值,系统不会为其赋予默认零值。· 对于引用数据类型reference来说,如数组引用、对象引用等,如果没有对其进行显式地赋值而直接使用,系统都会为其赋予默认的零值,即null。· 如果在数组初始化时没有对数组中的各元素赋值,那么其中的元素将根据对应的数据类型而被赋予默认的零值。 3、如果类字段的字段同时被final和static修饰,那么在准备阶段变量value就会被初始化为所指定的值。假设上面的类变量value被定义为: public static final int value = 200;在准备阶段虚拟机就会将value赋值为200. 解析阶段,是虚拟机将常量池内的符号引用替换为直接引用的过程。对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行解析。如果这个阶段失败,会产生NoSuchMethodError,NoSuchFieldError等错误。JVM实现的解析策略可能互不同。一种是在链接的时候,就把所有依赖的引用都解析出来。而另外一种则是只有在这个引用真正要用到的时候,才进行解析。 c.初始化初始化过程主要操作的静态初始化代码,构造器代码以及静态属性的初始化。初始化执行前,必须完成链接的校验和准备阶段,解析没有特别要求。触发java接口和类初始化的情况有以下几种:1.创建实例2.子类初始化3.JVM启动过程中指定的初始化类4.反射调用了类中的方法5.调用类的静态方法6.访问某个类或者接口的静态变量,或者对该静态变量赋值 d.JVM结束生命周期1.执行了system.exit()方法2.程序正常执行结束3.程序执行过程中遇到了异常或错误而异常中止4.操作系统错误导致的JVM进程挂掉 3.Java类执行 a.解释执行解释执行主要是JVM基于字节码进行的。具体的JVM采用了invokestatic,invokevirtual,invokeinterface和invokespecial来执行不同的方法调用。invokestatic对应的是static方法的调用。invokevirtual对应的是对象实例方法的调用。invokeinterface对应的是接口方法的调用。invokespecial对应的是private方法和方法的调用。b.编译执行JDK提供将字节码编译为机器码的支持,编译在执行时执行频率较高的代码。JDK在编译上提供了两种模式:clent compiler(-client)和server compiler(-server)JDK主要是根据栈的结构来执行字节码的,具体如下图: SUN JDK对Java类执行的一些优化:1.操作数栈顶的数据直接缓存在寄存器中,对于大部分只需要一个值的操作,无需再将数据放入操作数栈,可直接在寄存器计算。2.栈帧共享,后面调用的方法将前一个方法的操作数栈作为当前方法的局部变量。减少数据copy的性能消耗。3.编译执行(JIT编译器)对执行频繁的代码采用编译执行,他包含两种模式:client compiler(-client)即c1和server compiler(-server)即c2。 c1的优化client compiler(c1)主要用于桌面交互的应用,在寄存器分配策略上,JDK6以后采用的为线性扫描寄存器分配算法。还有就是方法内联,去虚拟化,冗余削除等。-XX:+PrintInlining来查看方法内联的信息。去虚拟化是在装载class文件后,进行类层次的分析,如果类中的方法只提供一个实现类,那么对于调用了此方法的代码,也可进行方法内联,从而提升执行的性能。冗余削除是指在编译时,根据运行状况进行代码折叠或削除。 c2的优化c2在寄存器分配策略上,采用传统的图着色寄存器分配算法。C2的优化主要机遇逃逸分析。逃逸分析指的是JVM根据运行状况来判断当前方法中的变量是否被外部引用,如果外部不引用,则认为是逃逸的。基于逃逸分析,主要的优化点有:标量替换,栈上分配和同步削除等。 默认情况在,JDK根据机器配置来选择client或server模式,32位windows机器始终选择的是client模式,当机器配置超过2核且内存>2G默认为server模式。可以通过启动参数-client或-server强制指定。 JDK未选择启动时即编译成机器码的原因1.静态编译后无法根据运行情况做优化。2.解释执行比编译执行更节省内存。3.启动时的解释执行速度比编译后再启动更快。 JDK用于权衡编译和解释代码的转换,主要基于两个维度的统计值。 1.调用计数器,即方法被调用的次数。CompileThreshold该值是指当方法被调用多少次后,就会被编译为机器码。在client模式下默认为1500次,在server模式下默认为10 000次,可通过在启动时添加-XX:CompileThreshold=10000来设置该值。 2.回边计数器,即循环体内代码被执行的次数。OnStackReplacePercentage该值为用于计算是否触发OSR编译的阈值,默认情况下client模式时为933,server模式下为140,该值可通过在启动时添加-XX: OnStackReplacePercentage=140来设置,在client模式时,计算规则为CompileThreshold (OnStackReplacePercentage/100),在server模式时,计算规则为(CompileThreshold (OnStackReplacePercentage - InterpreterProfilePercentage))/100。InterpreterProfilePercentage默认值为33。","raw":null,"content":null,"categories":[],"tags":[{"name":"Java原理","slug":"Java原理","permalink":"http://yoursite.com/tags/Java原理/"}]},{"title":"JAVA IO -- File","slug":"file","date":"2017-02-10T03:54:15.000Z","updated":"2017-02-10T03:55:19.000Z","comments":true,"path":"2017/02/10/file/","link":"","permalink":"http://yoursite.com/2017/02/10/file/","excerpt":"","text":"","raw":null,"content":null,"categories":[],"tags":[{"name":"JAVA IO, JDK , File","slug":"JAVA-IO-JDK-File","permalink":"http://yoursite.com/tags/JAVA-IO-JDK-File/"}]},{"title":"zab 协议","slug":"zab","date":"2016-12-29T01:27:47.000Z","updated":"2017-02-03T10:03:33.000Z","comments":true,"path":"2016/12/29/zab/","link":"","permalink":"http://yoursite.com/2016/12/29/zab/","excerpt":"","text":"Zab 协议ZAB协议是专门为ZooKeeper设计的崩溃可恢复的原子消息广播算法。ZooKeeper的分布式一致性主要由ZAB协议来实现。它通过一个单一的主进程来接收并处理客户端的所有事务请求,并采用ZAB的原子广播协议,将服务器的状态变更以事务Proposal的形式广播到所有的副本进程上去。协议的核心是所有事务请求必须由一个全局唯一的服务器来协调处理,即leader,而余下的其他服务器则为follower。leader服务器负责将一个客户端事务请求转换为一个事务Proposal提议,并将该Proposal分发给集群中所有的Follower服务器。如果Leader接收到了超过半数的Follower服务器进行了正确的反馈,leader就会再次向Follower发送Commit消息,要求其将前一个Proposal提交。 ZAB协议包含两个重要部分:崩溃恢复和消息广播。 一个由3台机器组成的ZAB服务,通常由一个leader,2个follower服务器组成。某个时间加入一个Follower服务器挂了,整个ZAB集群是不会中断服务的,因为leader服务器仍然可以获得过半的机器包括自身的支持。","raw":null,"content":null,"categories":[],"tags":[{"name":"zab, ZooKeeper","slug":"zab-ZooKeeper","permalink":"http://yoursite.com/tags/zab-ZooKeeper/"}]},{"title":"zookeeper简介","slug":"zookeeper","date":"2016-12-25T07:34:57.000Z","updated":"2016-12-29T11:07:45.000Z","comments":true,"path":"2016/12/25/zookeeper/","link":"","permalink":"http://yoursite.com/2016/12/25/zookeeper/","excerpt":"","text":"zookeeper简介zookeeper为分布式应用提供了高效可靠的基础服务,它并没有直接实用paxos算法作为分布式一致性上的解决方法,而是采用了ZAB协议(ZooKeeper Atomic Broadcast)。它提供了如下特性:1.顺序一致性,即从同一个客户端发起的事务请求,最终将会严格地按照其发送顺序被应用到ZooKeeper中。2.原子性,所有事务请求最终处理的结果在整个集群是一致的,不存在一部分执行了该事务,一部分没有的情况。3.单一视图,无论客户端连接的是哪台zookeeper服务器,其看到的服务端数据模型都是一致的。4.可靠性,一旦服务端成功应用了一条事务,并完成对客户端的响应,那么该事务所引起的服务端状态的变更会一直保持下去,直到一个新的事务对其做了改变。5.实时性,zookeeper能够保证在一定的时间段内,客户端最终一定能从服务端读到最新状态的数据。 zookeeper的设计目标:1.简单数据模型: zookeeper的树型命名空间由一系列ZNode结点组成,有点类似于文件系统的层级结构。2.方便构建集群: 3-5台机器即可组成一个集群。每台机器都会在内存中维护当前的服务器状态,且每台服务器之间保持互相通信。3.支持顺序访问。4.高性能。全部数据存储在内存中,尤其适用于读操作的场景。 zookeeper集群它并非传统的master/slave式的集群模式,而是采用了leader,follower和observer三种角色。zookeeper中包含一台选举出来的leader,它提供客户端的读写服务。其他机器为Follower和observer,他们只提供读服务。且二者之间的区别在于,follower除了提供读服务外,还参与leader的选举,以及写操作的”过半写成功”策略。 session会话客户端于服务器建立第一次连接开始,客户端的会话生命周期也同事开始。通过这个长连接,客户端可以通过心跳机制来于服务端保持会话的有效性。如果客户端因为服务器压力过大,或网络故障等等造成的断开,只要在sessionTimeOut之前重新于集群建立连接,都认为会话仍有效。 数据节点ZNode即为数据模型中的数据单元,zookeeper的所有数据都存储在一个树型结构的内存模型中。比如/path/foo1就是一个znode,每个znode不仅存储数据,也存储一系列属性信息。ZNode有永久结点和临时结点的区分。临时结点与客户端会话相关。znode的版本,zookeeper会为每个znode维护一个stat,包含version znode的当前版本,cversion znode的子节点版本和aversion znode的ACL版本 watcher监听器zookeeper允许用户在指定节点注册时间监听器。zookeeper会在事件触发后发送给感兴趣的用户。 ACL权限控制1.CREATE 创建子节点的权限。2.READ 获取节点数据和子节点列表的权限。3.WRITE 更新节点数据的权限4.DELETE 删除子节点的权限5.ADMIN 设置节点ACL的权限","raw":null,"content":null,"categories":[],"tags":[{"name":"分布式一致性,zookeeper,ZAB","slug":"分布式一致性-zookeeper-ZAB","permalink":"http://yoursite.com/tags/分布式一致性-zookeeper-ZAB/"}]},{"title":"paxos在chubby的应用","slug":"paxosinchubby","date":"2016-12-12T11:49:32.000Z","updated":"2016-12-25T07:32:34.000Z","comments":true,"path":"2016/12/12/paxosinchubby/","link":"","permalink":"http://yoursite.com/2016/12/12/paxosinchubby/","excerpt":"","text":"paxos在chubby中的应用 paxos在chubby中主要作为日志层一致性的保证,为上层的分布式锁和数据库提供保障。 paxos算法的作用就在于保证chubby集群内各个副本节点的日志能够保持一致。chubby事务日志中的每个value对应的Paxos算法中的一个instance。在整个chubby运行过程中,会存在多个Paxos instance。chubby会为每个Paxos instance按序分配一个全局唯一的Instance编号,并顺序写入tvirsConfigMapper到事务日志中去。在多模式下,为了提升算法执行的性能,就必须选举出一个副本节点作为paxos算法的主节点,以避免多个Paxos Round并存的情况。具体地:prepare->promise->propose->accept 当前至多存在k个未达成一致的Instance,将这些未决的instance各自最后接受的提案值(若尚未接受任何值,则用null来代替。)作为promise消息返回。 判断N是否大于当前Acceptor的最大提案编号,如果大于该值,则标记所有未决instance和所有未来的instance的最大提案编号为n,这样未决的和所有未来的instance都不能接受提案编号小于n的提案。在master稳定的情况下,只需要使用一个相同的编号来执行每个instance的promise->accept模式,一旦在单个的instance中接收到了多数派的Accept反馈,即可将对应的提案值写入本地事务日志并广播commit消息告诉集群中的其他副本节点,以便于其他副本节点写入。如果某台宕机,可以主动向其他副本节点进行查询。 chubby的数据库层 数据快照+事务日志,定期的数据快照避免日志过大,造成的宕机恢复时间较长。如果磁盘损坏,只能从其他副本节点索取全量的状态数据了。另外如果磁盘未损坏,宕机瞬间丢失的日志数据也可以通过其他副本节点做恢复。","raw":null,"content":null,"categories":[],"tags":[{"name":"chubby paxos","slug":"chubby-paxos","permalink":"http://yoursite.com/tags/chubby-paxos/"}]},{"title":"chubby介绍","slug":"chubby","date":"2016-10-22T05:52:42.000Z","updated":"2017-03-15T15:47:44.000Z","comments":true,"path":"2016/10/22/chubby/","link":"","permalink":"http://yoursite.com/2016/10/22/chubby/","excerpt":"","text":"chubby chubby系统架构 大致如下图所示: 一个由chubby cell组成的chubby server集群,客户端的chubby lib通过Rpc协议与服务端进行通讯,chubby cell之间通过paxos算法选举server中的master。 目录与文件chubby的数据结构可以看作是一个由文件和目录组成的树。/chubby节点的公共前缀/chubby集群的名字/业务的相对路径, Chubby服务器内部可以解析并定位到数据节点。 chubby 包括acl元信息,及四个单调递增的64位编码。实例编码,文件内容编码,锁编码,ACL编码消息乱序或延迟造成锁失效。如一个客户端c1获取到了互斥锁L,并且在锁L的保护下发出的请求R,但请求R迟迟没有到达服务器,这时应用程序会认为该客户端进程已经失败,于是便会为另一个客户端C2分配了锁L,然后再重新发送请求R,并且成功的应用到了服务器上,此时,不幸的事情发生了,客户端C1的请求R在经过一波三折之后也到达了服务端, chubby中任意一个数据节点都可以充当一个读写锁来使用,一种是单个客户端以排他(写)模式持有这个锁,另一种则是任意数目的客户端以共享(读)模式持有这个锁。chubby舍弃了严格的强制锁,客户端可以在没有获取任何锁的情况下访问chubby的文件。 chubby中解决消息延迟和重排序引起的分布式锁问题,采用锁延迟和锁序列器两种策略。chubby 客户端以正常的方式主动释放一个锁,那么chubby服务端将会允许其他客户端能够立即获取到该锁。如果是因为客户端异常而被释放的话,那么chubby服务器会为该锁保留一定的时间,称之为锁延迟。锁序列器,该策略需要chubby的上层应用配合在代码中加入相应的修改逻辑。 chubby中的事件通知机制客户端向服务端注册事件通知,服务端根据事件通知来通知对应的客户端。 chubby客户端的缓存策略chubby客户端缓存一致性问题的处理,通过租期机制来保证缓存一致性。chubby缓存的生命周期和master租期机制紧密相关,master会维护每个客户端的数据缓存情况,并通过向客户端发送过期信息的方式来保证客户端数据的一致性。这样,chubby就能保证客户端要么能从缓存中访问到一致的数据,要么访问出错。每个客户端的缓存都有一个租期,一旦该租期到期,客户端就需要向服务端续订租期以继续维持缓存的有效性,当文件数据或元数据修改时,chubby会首先阻塞该修改操作,由master向所有缓存了该缓存的客户端发送缓存过期信号,以使其缓存过期。master在接收到所有客户端针对该缓存的应答后,再进行修改操作。 chubby服务端处理客户端的保持连接的请求,收到后会先阻塞等待该客户端的租期即将过期时,才会对它的租期进行续租,续租后向客户端返回保持连接请求的应答。chubby的租期会话时间默认为12s,但会根据具体负载进行调节。客户端收到保持连接的响应后,会立即向服务端发一个保持连接的请求,服务端再次阻塞。 chubby master选举后的基本流程1.master选举后,会确定master周期,master周期确定后,如果再收到客户端携带的其他master的编号的请求,则会拒绝。并告知客户端更新master的编号。master只要发生重新选取,即需要重新确定周期。2.选举产生的新的master会立即对寻址的客户端请求进行响应,但不会立即对客户端的会话请求进行处理。3.master会根据本地存储的会话和锁信息,来构建服务器内存状态。4.master处理客户端的keepalive请求。5.master向所有客户端发送一个故障切换的请求,等待所有的客户端接收到侯清空本地缓存,警告上层缓存失效。并应答回master。6.master开始处理会话级的请求。如果客户端调用了一个故障切换前创建的句柄,当前的master会重新创建合格内存对象。","raw":null,"content":null,"categories":[],"tags":[{"name":"分布式锁,GFS,Big Table","slug":"分布式锁-GFS-Big-Table","permalink":"http://yoursite.com/tags/分布式锁-GFS-Big-Table/"}]},{"title":"paxos 算法","slug":"paxos","date":"2016-10-16T17:15:01.000Z","updated":"2016-12-24T10:06:09.000Z","comments":true,"path":"2016/10/17/paxos/","link":"","permalink":"http://yoursite.com/2016/10/17/paxos/","excerpt":"","text":"分布式一致性的背景1.面临的问题通讯异常,网络分区,三态(成功,失败,超时),节点故障2.相关的概念(1) ACID原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)(2) CAP原理指的是 一致性(CONSISTENCY)可用性(AVAILABILITY)分区容忍性(PARTITION TOLERANCE)这三个要素最多只能同时实现两点,不可能三者兼顾。在进行分布式架构设计时,必须做出取舍。而对于分布式数据系统,分区容忍性是基本要求,否则就失去了价值。一般情况下分布式数据系统,就是在一致性和可用性之间取一个平衡。对于大多数WEB应用,其实并不需要强一致性,因此牺牲一致性而换取高可用性,是多数分布式产品的方向。(3) BASE理论BASE理论是最终一致性的理论支撑,它的全称是 Basically Available(基本可用),Soft-state(软状态/柔性事务),Eventually Consistent(最终一致性)。在理论逻辑上它是相反于ACID模型的,它牺牲高一致性,获得可用性和分区容忍性。 一致性模型的类型:(1) Weak 弱一致性:当你写入一个新值后,读操作在数据副本上可能读出来,也可能读不出来。比如:网络游戏其它玩家的数据,VOIP系统等。(2) Eventually 最终一致性:当你写入一个新值后,有可能读不出来,但在某个时间窗口之后保证最终能读出来。比如:DNS,电子邮件、Amazon S3,Google搜索引擎等。(3) Strong 强一致性:新的数据一旦写入,在任意副本任意时刻都能读到新值。比如:文件系统,RDBMS,Azure Table等。 分布式算法1.PC二段提交第一阶段:协调者向所有参与者发送”是否可以提交”–> 参与者开始做执行准备:资源上锁,预留资源,写入undo/redo日志(便于回滚),准备成功则回应”可以提交”,否则回应”拒绝”第二阶段:所有参与者回应“可以提交” –> 协调者向所有的参与者发送“正式提交”命令 –> 参与者完成正式提交,释放资源,回应”完成” –> 协调者收集到所有”提交完成”,结束事务。任意一个参与者回应”拒绝提交” –> 协调者向所有的参与者发送“回滚”命令 –> 参与者释放所有资源,回应”回滚完成”–> 协调者收集到所有”回滚完成”,取消整个事务。 优势:原理实现简单劣势:单点问题–>协调者同步阻塞问题脑裂问题–>协调者区域网络断开,导致双主。过于保守–>所有参与者都要响应(如:参与者超时返回,参与者响应后协调者错误导致参与者所处的状态等)。 2.三阶段提交(1) 阶段1:事务询问协调者发送一个包含事务的can commit请求,询问是否可以提交操作—>参与者向协调者反馈事务询问的响应 (2) 阶段2:precommit执行事务预先提交:协调者向所有参与者发送precommit请求 –> 参与者收到precommit请求后,执行事务操作,写undo,redo信息。–> 所有参与者成功执行事务操作,反馈ACK响应给协调者,并等待下一步指令:commit 或者 abort。 中断事务:任何一个参与者ACK了No或者等待超时后,仍有参与者未反馈响应,则中断事务。协调者向所有参与者发送中断请求 –> 参与者中断事务(参与者无论是否收到协调者的abort请求或等待协调者过程中发生超时,参与者都会中断事务) (3) docommit执行提交协调者向所有参与者发送提交请求 –> 参与者提交事务,释放事务执行期间所占用的资源,完成后向协调者发送ACK消息,反馈事务提交的结果 –> 协调者接收到所有参与者的ack后完成事务。 中断事务协调者收到任一个参与者的No ACK,或者因为网络故障造成的响应超时, 协调者发送中断请求–> 参与者事务回滚,释放执行期间的资源占用,反馈事务的提交结果 –> 协调者收到所有反馈后,中断事务 优缺点:优点:对比2阶段提交,3阶段提交降低了参与者的阻塞范围, 在出现单点故障后继续达成一致。缺点:参与者收到precommit消息后,如果和协调者无法网络通讯,会提交事务,导致数据不一致。 拜占庭将军问题 (Byzantine Generals Problem) 拜占庭将军问题的大致描述如下:两支军队,各有一个将军,他们准备协同攻击同样的一座城市,唯一的通信方式就是派各自的信使来往于山谷。但存在会被抓获的危险。当且仅当两支军队同时进攻这座城市才会成功。这种情形下两支军队需要进行沟通,来确定一个进攻时间。这将导致一个问题:试图通过建立在一个不可靠的连接上的交流,来协同一项行动的隐患和设计上的巨大挑战。 Paxos算法 Paxos算法是基于消息传递,具有高度容错特性的一致性算法,是当前解决分布式一致性问题最有效的算法之一。该算法解决的问题就是如何在一个可能发生诸如机器宕机或网络异常等情况的分布式系统中,快速且正确的在集群内部对某个数据的值达成一致,并且保证不论发生以上任何异常都不会破坏整个系统的一致性。paxos的三个角色Proposer,Acceptor,learner.paxos算法大致分为两部分,提出提案和提案获取.1.提出提案: 分为prepare阶段和accept阶段prepare阶段Proposer选定出一个编号为N的提案,然后向acceptor的某个超过半数的子集成员发送编号为N的prepare请求。此时如果acceptor收到该编号为N的prepare请求,如果编号N大于它所有当前已经响应的请求编号,那么它就会将编号为N的提案作为响应反馈给Proposer,并且承诺不会再批准任何小于编号N的提案。accept阶段如果Proposer收到来自半数以上的Acceptor反馈的,编号N的Prepare请求响应,那么它就会发送一个针对N的值为Vn的Accept请求给Acceptor,这个Vn就是提案编号N的value值。如果acceptor收到这个针对N编号值为Vn的提案,且它尚未对大于编号N的Prepare请求作出响应,它就可以通过该提案。注意:为保证防止死循环,proposer选取一个主proposer。2.提案的获取:acceptor通过后通知learner集合,learner集合再负责通知其余的learner。","raw":null,"content":null,"categories":[],"tags":[{"name":"paxos,分布式","slug":"paxos-分布式","permalink":"http://yoursite.com/tags/paxos-分布式/"}]}]}