• 关于 复制两个字段数据 的搜索结果

问题

SQL 两张表相差一个字段怎么复制数据

吴孟桥 2019-12-01 19:54:53 889 浏览量 回答数 1

回答

高内聚和低耦合 一个逻辑和物理模型由哪些记录和字段组成,应该遵循最基本的软件设计方法论中的高内聚和低耦合原则。主要从数据业务特性和访问特性两个角度来考虑:将业务相近或者相关的数据、粒度相同数据设计为一个逻辑或者物理模型;将高概率同时访问的数据放一起,将低概率同时访问的数据分开存储。 核心模型与扩展模型分离 建立核心模型与扩展模型体系,核心模型包括的字段支持常用核心的业务,扩展模型包括的字段支持个性化或是少量应用的需要。在必须让核心模型与扩展模型做关联时,不能让扩展字段过度侵入核心模型,以免破坏了核心模型的架构简洁性与可维护性。 公共处理逻辑下沉及单一 底层公用的处理逻辑应该在数据调度依赖的底层进行封装与实现,不要让公用的处理逻辑暴露给应用层实现,不要让公共逻辑在多处同时存在。 成本与性能平衡 适当的数据冗余可换取查询和刷新性能,不宜过度冗余与数据复制。 数据可回滚 处理逻辑不变,在不同时间多次运行数据的结果需确定不变。 一致性 相同的字段在不同表中的字段名必须相同。 命名清晰可理解 表命名规范需清晰、一致,表命名需易于下游的理解和使用。

LiuWH 2020-03-19 23:13:23 0 浏览量 回答数 0

回答

代码看着很蛋疼,编辑一下,使用工具栏的 # 插入代码,才能有语法高亮 ######确实很蛋疼,整理了一下。这样好看多了。。。######不知道我有没有理解楼主的意思 引用“取个数据存个数据都得复制张贴字段好辛苦” 意思是getXXX setXXX 这样比较轻松吗, 那写javabean不辛苦吗,有人说可以自动生成 get set,还有人说可以自动生成Bean。 那同样JFinal的Model你也可以改造一下实现同样的功能。 需要复制张贴字段: 1.DB设计的不是很合理,名字长,并且生涩难记,才会出现这种情况 2.开发不用心,不熟悉表结构######回复 @LICHUAN :我们公司很多表都是在30个字段以上,然后其中还有某些表预留字段就有几十个,但是常用的就那么几个。######呵呵,在开发初期在db设计的表结构,可能你记忆真的好,一下子就记住了字段名。还有,现在一个人从设计表结构到所有都做完成的一个项目真心很小,只能代表jfinal适合一个人玩小项目,数据库什么的不复杂。######之前用Hibernate就非常讨厌写Bean和给Bean加注解######软件里面的东西只要数量级变大都会变得很麻烦,不可能每个表都是45个字段,并且每个字段都有用吧,当出现几十个字段的时候是不是应该从模型的角度也斟酌一下?###### 虽然公司项目基本都是jfinal,但是有个遗留的ssh项目,现在每次加个字段都被hibernater搞的我想死。。。各种忘记加,忘记配置。 ######回复 @LICHUAN : javaBean是依赖数据库设计的。注解比xml优秀,编写代码无需来回关注xml。但是jfinal自动创建javabean/orm的方式,比注解优秀。jfinal自动创建,创建的不仅仅是orm,还有setter/getter。维护更改数据库字段时候,双击的时间远比你找注解改注解的时间要快。当然,你也可以试试实现orm和自动生成getter/setter。哈哈哈哈哈######回复 @LICHUAN : 当你能不用写注解与setter getter以后,你会发现注解也很不爽,试用一下 jfinal activerecord两三天时间,将彻底改变你现在的想法######。。。相对来说,你还没玩过hibernate的hbm。xml时代呵,个人觉得注解形式javabean映射是最简单高效的orm框架设计了,没有之一。######回复 @Jieven : 抱歉,我说的是4-5个字段,没有那么夸张到40-50个字段的表结构。 话说我真不敢苟同你的意见,一个javabean配置一个表结构,使用getset方法访问的好处是在写代码的时候直接调用getset函数就能够方便的操作表字段,而不需要记住表结构字段名称。 而这个好处在刚开始写项目的速度优势是显而易见的。还有一个好处是可以随时调整表结构,而不需要写了一般发现某些字段得调整,而当这个表结构使用jfina的set(“key","value")分散在各处的时候,你会发现,悲剧了,每个都得改。 ###### 回楼上的楼上,我现在使用jfinal的Model就是这样改造来用的,将就着用吧: public String getName() { return get("name"); } public void setName(String name) { set("name", name); }###### 不知道他们大公司实际是怎么做的 你可以咨询下@绝望的八皮 或者 @JFinal  个人是参照的 jfinal blog 里面的,用起来也很爽! ###### 引用来自“孤独的3”的答案 不知道他们大公司实际是怎么做的 你可以咨询下@绝望的八皮 或者 @JFinal  个人是参照的 jfinal blog 里面的,用起来也很爽! 其他用起来还是蛮爽的,就是对这个Model组件挺纠结的。赶脚这种去对象化的DB设计对于多人合作或者数据表复杂点就难以使用了。 ######大公司,不一定牛B,技术也不一定先进,我们公司500人不知道算什么公司,还在用HashVO 来获取数据,如果这个到时候改表结构,不知道有多少人要跳楼,但是实际上几乎没改过表结构######     用 jfinal model 一到两天就知道好处了,对于web项目来说数据是非常关键的,所以对数据库表结构要非常地清楚,至于字段记忆的问题,可以通过 desc tableName得到表结构,然后放在model最前面的注释里面,随时可以查看。     用过 hibernate 两三年,jfinal 比传统 Java ORM 好用多了,不信就试试,保证你会将传统java ORM彻底扔进垃圾桶, @绝望的八皮 就是其中之一 ######支持Record ORM,支持JFinal######别的不想多说,时间会证明一切,看看是屌丝多,还是高帅富多######springrain @WhereSQL 动态查询条件的注解……###### 各位只是说jfinal record orm好啊,真的好啊,非常好啊。但是几乎都没有说到怎么个好法,怎么达到快速敏捷开发的目的。是jfinal的脑残粉么? 我也用过不少ORM框架,大概也不过xml配置,注解式,还有jfinal的sql映射方式。我就想问两种方式, 1,只写一个带注解的bean,使用getset方法访问数据。 2,还是需要写个sql或建立好表再写个空bean把相关字段放到各处使用。 两种方式哪个更方便,更便于维护? ######jfinal没有完全使用注解实现orm,没耐心看源码怪谁?我一个学jfianl2天的人,不敢说我说的都准确。但是我告诉你一点,jfinal可以自动实现orm/setter/getter/基本的jdbc。维护数据表不需要重复关注javabean的注解,不需要重复关注xml。我们有更多时间写业务。另外jfinal的mvc传参比springmvc不会出现忘记数据类型/数据字段名称等的400错误。

kun坤 2020-06-10 09:36:07 0 浏览量 回答数 0

海外云虚拟主机包年25元/月起

海外独享虚拟主机全面上线,助力构建海外网站,提升公司国际形象;全球有效覆盖,超高性价比;建站入门首选,助力出口,适合跨境贸易企业。

问题

关于postgresql在实际场景下所碰到的问题

jaywu 2019-12-01 19:44:32 1369 浏览量 回答数 1

回答

简介 ES是一个基于RESTful web接口并且构建在Apache Lucene之上的开源分布式搜索引擎。 同时ES还是一个分布式文档数据库,其中每个字段均可被索引,而且每个字段的数据均可被搜索,能够横向扩展至数以百计的服务器存储以及处理PB级的数据。 可以在极短的时间内存储、搜索和分析大量的数据。通常作为具有复杂搜索场景情况下的核心发动机。 ES就是为高可用和可扩展而生的。一方面可以通过升级硬件来完成系统扩展,称为垂直或向上扩展(Vertical Scale/Scaling Up)。 另一方面,增加更多的服务器来完成系统扩展,称为水平扩展或者向外扩展(Horizontal Scale/Scaling Out)。尽管ES能够利用更强劲的硬件,但是垂直扩展毕竟还是有它的极限。真正的可扩展性来自于水平扩展,通过向集群中添加更多的节点来分担负载,增加可靠性。ES天生就是分布式的,它知道如何管理多个节点来完成扩展和实现高可用性。意味应用不需要做任何的改动。 Gateway,代表ES索引的持久化存储方式。在Gateway中,ES默认先把索引存储在内存中,然后当内存满的时候,再持久化到Gateway里。当ES集群关闭或重启的时候,它就会从Gateway里去读取索引数据。比如LocalFileSystem和HDFS、AS3等。 DistributedLucene Directory,它是Lucene里的一些列索引文件组成的目录。它负责管理这些索引文件。包括数据的读取、写入,以及索引的添加和合并等。 River,代表是数据源。是以插件的形式存在于ES中。  Mapping,映射的意思,非常类似于静态语言中的数据类型。比如我们声明一个int类型的变量,那以后这个变量只能存储int类型的数据。比如我们声明一个double类型的mapping字段,则只能存储double类型的数据。 Mapping不仅是告诉ES,哪个字段是哪种类型。还能告诉ES如何来索引数据,以及数据是否被索引到等。 Search Moudle,搜索模块,支持搜索的一些常用操作 Index Moudle,索引模块,支持索引的一些常用操作 Disvcovery,主要是负责集群的master节点发现。比如某个节点突然离开或进来的情况,进行一个分片重新分片等。这里有个发现机制。 发现机制默认的实现方式是单播和多播的形式,即Zen,同时也支持点对点的实现。另外一种是以插件的形式,即EC2。 Scripting,即脚本语言。包括很多,这里不多赘述。如mvel、js、python等。    Transport,代表ES内部节点,代表跟集群的客户端交互。包括 Thrift、Memcached、Http等协议 RESTful Style API,通过RESTful方式来实现API编程。 3rd plugins,代表第三方插件。 Java(Netty),是开发框架。 JMX,是监控。 使用案例 1、将ES作为网站的主要后端系统 比如现在搭建一个博客系统,对于博客帖子的数据可以直接在ES上存储,并且使用ES来进行检索,统计。ES提供了持久化的存储、统计和很多其他数据存储的特性。 注意:但是像其他的NOSQL数据存储一样,ES是不支持事务的,如果要事务机制,还是考虑使用其他的数据库做真实库。 2、将ES添加到现有系统 有些时候不需要ES提供所有数据的存储功能,只是想在一个数据存储的基础之上使用ES。比如已经有一个复杂的系统在运行,但是现在想加一个搜索的功能,就可以使用该方案。 3、将ES作为现有解决方案的后端部分 因为ES是开源的系统,提供了直接的HTTP接口,并且现在有一个大型的生态系统在支持他。比如现在我们想部署大规模的日志框架、用于存储、搜索和分析海量的事件,考虑到现有的工具可以写入和读取ES,可以不需要进行任何开发,配置这些工具就可以去运作。 设计结构 1、逻辑设计 文档 文档是可以被索引的信息的基本单位,它包含几个重要的属性: 是自我包含的。一篇文档同时包含字段和他们的取值。 是层次型的。文档中还可以包含新的文档,一个字段的取值可以是简单的,例如location字段的取值可以是字符串,还可以包含其他字段和取值,比如可以同时包含城市和街道地址。 拥有灵活的结构。文档不依赖于预先定义的模式。也就是说并非所有的文档都需要拥有相同的字段,并不受限于同一个模式 {   "name":"meeting",   "location":"office",   "organizer":"yanping" } {   "name":"meeting",   "location":{     "name":"sheshouzuo",        "date":"2019-6-28"   },   "memebers":["leio","shiyi"] } 类型 类型是文档的逻辑容器,类似于表格是行的容器。在不同的类型中,最好放入不同的结构的文档。 字段 ES中,每个文档,其实是以json形式存储的。而一个文档可以被视为多个字段的集合。 映射 每个类型中字段的定义称为映射。例如,name字段映射为String。 索引 索引是映射类型的容器一个ES的索引非常像关系型世界中的数据库,是独立的大量文档集合。   关系型数据库与ES的结构上的对比 2、物理设计 节点 一个节点是一个ES的实例,在服务器上启动ES之后,就拥有了一个节点,如果在另一个服务器上启动ES,这就是另一个节点。甚至可以在一台服务器上启动多个ES进程,在一台服务器上拥有多个节点。多个节点可以加入同一个集群。 当ElasticSearch的节点启动后,它会利用多播(multicast)(或者单播,如果用户更改了配置)寻找集群中的其它节点,并与之建立连接。这个过程如下图所示: 节点主要有3种类型,第一种类型是client_node,主要是起到请求分发的作用,类似路由。第二种类型是master_node,是主的节点,所有的新增,删除,数据分片都是由主节点操作(elasticsearch底层是没有更新数据操作的,上层对外提供的更新实际上是删除了再新增),当然也能承担搜索操作。第三种类型是date_node,该类型的节点只能做搜索操作,具体会分配到哪个date_node,就是由client_node决定,而data_node的数据都是从master_node同步过来的 分片 一个索引可以存储超出单个结点硬件限制的大量数据。比如,一个具有10亿文档的索引占据1TB的磁盘空间,而任一节点都没有这样大的磁盘空间;或者单个节点处理搜索请求,响应太慢。   为了解决这个问题,ES提供了将索引划分成多份的能力,这些份就叫做分片。当你创建一个索引的时候,你可以指定你想要的分片的数量。每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上。 分片之所以重要,主要有两方面的原因:   1、允许你水平分割/扩展你的内容容量 允许你在分片(潜在地,位于多个节点上)之上进行分布式的、并行的操作,进而提高性能/吞吐量 至于一个分片怎样分布,它的文档怎样聚合回搜索请求,是完全由ES管理的,对于作为用户的你来说,这些都是透明的。   2、在一个网络/云的环境里,失败随时都可能发生,在某个分片/节点不知怎么的就处于离线状态,或者由于任何原因消失了。这种情况下,有一个故障转移机制是非常有用并且是强烈推荐的。为此目的,ES允许你创建分片的一份或多份拷贝,这些拷贝叫做复制分片,或者直接叫复制。 复制之所以重要,主要有两方面的原因: (1)在分片/节点失败的情况下,提供了高可用性。因为这个原因,注意到复制分片从不与原/主要(original/primary)分片置于同一节点上是非常重要的。 (2)扩展你的搜索量/吞吐量,因为搜索可以在所有的复制上并行运行 总之,每个索引可以被分成多个分片。一个索引也可以被复制0次(意思是没有复制)或多次。一旦复制了,每个索引就有了主分片(作为复制源的原来的分片)和复制分片(主分片的拷贝)之别。分片和复制的数量可以在索引创建的时候指定。在索引创建之后,你可以在任何时候动态地改变复制数量,但是不能改变分片的数量。   默认情况下,ES中的每个索引被分片5个主分片和1个复制,这意味着,如果你的集群中至少有两个节点,你的索引将会有5个主分片和另外5个复制分片(1个完全拷贝),这样的话每个索引总共就有10个分片。一个索引的多个分片可以存放在集群中的一台主机上,也可以存放在多台主机上,这取决于你的集群机器数量。主分片和复制分片的具体位置是由ES内在的策略所决定的。 3、插件HEAD elasticsearch-head是一个界面化的集群操作和管理工具 ● node:即一个 Elasticsearch 的运行实例,使用多播或单播方式发现 cluster 并加入。 ● cluster:包含一个或多个拥有相同集群名称的 node,其中包含一个master node。 ● index:类比关系型数据库里的DB,是一个逻辑命名空间。 ● alias:可以给 index 添加零个或多个alias,通过 alias 使用index 和根据index name 访问index一样,但是,alias给我们提供了一种切换index的能力,比如重建了index,取名● customer_online_v2,这时,有了alias,我要访问新 index,只需要把 alias 添加到新 index 即可,并把alias从旧的 index 删除。不用修改代码。 ● type:类比关系数据库里的Table。其中,一个index可以定义多个type,但一般使用习惯仅配一个type。 ● mapping:类比关系型数据库中的 schema 概念,mapping 定义了 index 中的 type。mapping 可以显示的定义,也可以在 document 被索引时自动生成,如果有新的 field,Elasticsearch 会自动推测出 field 的type并加到mapping中。 ● document:类比关系数据库里的一行记录(record),document 是 Elasticsearch 里的一个 JSON 对象,包括零个或多个field。 ● field:类比关系数据库里的field,每个field 都有自己的字段类型。 ● shard:是一个Lucene 实例。Elasticsearch 基于 Lucene,shard 是一个 Lucene 实例,被 Elasticsearch 自动管理。之前提到,index 是一个逻辑命名空间,shard 是具体的物理概念,建索引、查询等都是具体的shard在工作。shard 包括primary shard 和 replica shard,写数据时,先写到primary shard,然后,同步到replica shard,查询时,primary 和 replica 充当相同的作用。replica shard 可以有多份,也可以没有,replica shard的存在有两个作用,一是容灾,如果primary shard 挂了,数据也不会丢失,集群仍然能正常工作;二是提高性能,因为replica 和 primary shard 都能处理查询。另外,如上图右侧红框所示,shard数和replica数都可以设置,但是,shard 数只能在建立index 时设置,后期不能更改,但是,replica 数可以随时更改。但是,由于 Elasticsearch 很友好的封装了这部分,在使用Elasticsearch 的过程中,我们一般仅需要关注 index 即可,不需关注shard。   shard、node、cluster 在物理上构成了 Elasticsearch 集群,field、type、index 在逻辑上构成一个index的基本概念,在使用 Elasticsearch 过程中,我们一般关注到逻辑概念就好,就像我们在使用MySQL 时,我们一般就关注DB Name、Table和schema即可,而不会关注DBA维护了几个MySQL实例、master 和 slave 等怎么部署的一样。 ES中的索引原理 (1)传统的关系型数据库 二叉树查找效率是logN,同时插入新的节点不必移动全部节点,所以用树型结构存储索引,能同时兼顾插入和查询的性能。因此在这个基础上,再结合磁盘的读取特性(顺序读/随机读),传统关系型数据库采用了B-Tree/B+Tree这样的数据结构做索引 (2)ES 采用倒排索引 那么,倒排索引是个什么样子呢? 首先,来搞清楚几个概念,为此,举个例子: 假设有个user索引,它有四个字段:分别是name,gender,age,address。画出来的话,大概是下面这个样子,跟关系型数据库一样 Term(单词):一段文本经过分析器分析以后就会输出一串单词,这一个一个的就叫做Term Term Dictionary(单词字典):顾名思义,它里面维护的是Term,可以理解为Term的集合 Term Index(单词索引):为了更快的找到某个单词,我们为单词建立索引 Posting List(倒排列表):倒排列表记录了出现过某个单词的所有文档的文档列表及单词在该文档中出现的位置信息,每条记录称为一个倒排项(Posting)。根据倒排列表,即可获知哪些文档包含某个单词。(PS:实际的倒排列表中并不只是存了文档ID这么简单,还有一些其它的信息,比如:词频(Term出现的次数)、偏移量(offset)等,可以想象成是Python中的元组,或者Java中的对象) (PS:如果类比现代汉语词典的话,那么Term就相当于词语,Term Dictionary相当于汉语词典本身,Term Index相当于词典的目录索引) 我们知道,每个文档都有一个ID,如果插入的时候没有指定的话,Elasticsearch会自动生成一个,因此ID字段就不多说了 上面的例子,Elasticsearch建立的索引大致如下: name字段: age字段: gender字段: address字段: Elasticsearch分别为每个字段都建立了一个倒排索引。比如,在上面“张三”、“北京市”、22 这些都是Term,而[1,3]就是Posting List。Posting list就是一个数组,存储了所有符合某个Term的文档ID。 只要知道文档ID,就能快速找到文档。可是,要怎样通过我们给定的关键词快速找到这个Term呢? 当然是建索引了,为Terms建立索引,最好的就是B-Tree索引(MySQL就是B树索引最好的例子)。 我们查找Term的过程跟在MyISAM中记录ID的过程大致是一样的 MyISAM中,索引和数据是分开,通过索引可以找到记录的地址,进而可以找到这条记录 在倒排索引中,通过Term索引可以找到Term在Term Dictionary中的位置,进而找到Posting List,有了倒排列表就可以根据ID找到文档了 (PS:可以这样理解,类比MyISAM的话,Term Index相当于索引文件,Term Dictionary相当于数据文件) (PS:其实,前面我们分了三步,我们可以把Term Index和Term Dictionary看成一步,就是找Term。因此,可以这样理解倒排索引:通过单词找到对应的倒排列表,根据倒排列表中的倒排项进而可以找到文档记录) 为了更进一步理解,用两张图来具现化这一过程: (至于里面涉及的更加高深的数据压缩技巧,以及多个field联合查询利用跳表的数据结构快速做运算来查询,这些大家有兴趣可以自己去了解)

问问小秘 2020-04-29 15:40:48 0 浏览量 回答数 0

回答

最大的不同就是1、MongoDB是非关系型NoSQL数据库的代表,排名第一,移动互联网公司比较多。2、MySQL是关系型数据库SQL的代表,互联网公司比较多。3、最大的不同在于Schema灵活性,MongoDB不固定表结构,MySQL需要固定设计字段,约束性强。4、如果说还有其他不同,MySQL体系更强大,支持存储引擎、语言、存储过程、包括事务、触发器等复制机制,重量级路线。5、MongoDB走的是轻量级路线,追求高性能,高并发,易于扩展伸缩。6、目前来看两个数据库有一部分是对方的功能,MongoDB也支持事务,MySQL开始支持JSON格式。7、MongoDB对于并发高,并且不确定数据结构,经常变换的项目,比如微博、微信等经常修改需求的数据模型非常适合。8、MySQL传统的数据存储,比较成熟的数据结构可以使用,目前来说还是使用非常多。2个可以结合使用,不冲突。

徐雷frank 2019-12-02 01:41:20 0 浏览量 回答数 0

回答

先说一下fork,fork会生成一个和当前进程相同的副本,称为子进程。原进程的所有资源都以适当的方式复制到子进程,因此该系统调用之后,原来的进程就有了两个独立的实例。这两个实例的联系包括:同一组打开文件、同样的工作目录、内存中同样的数据(两个进程各有一份副本)。当然Linux使用了copy on write,也就是说只有新的进程对内存页执行write操作的时候才会复制内存页面。具体如何完成:首先要了解一下task_struct这个数据结构。Linux内核很多涉及进程的部分都围绕这个数据结构(数据结构定义在include/sched.h中,有兴趣去看一下)。数据结构里面的成员非常多,下面会按照几个部分介绍一下。状态和执行信息,如待决信号、使用的二进制格式(和其他系统二进制格式的任何仿真信息)、进程ID号(pid)、到父进程及其他有关进程的指针、优先级和程序执行有关的时间信息(例如CPU时间)。有关已经分配的虚拟内存的信息。进程身份凭据,如用户ID、组ID以及权限等。可使用系统调用查询(或修改)这些数据。使用的文件包含程序代码的二进制文件,以及进程所处理的所有文件的文件系统信息,这些都必须保存下来。线程信息记录该进程特定于CPU的运行时间数据(该结构的其余字段与所使用的硬件无关)。在与其他应用程序协作时所需的进程间通信有关的信息。该进程所用的信号处理程序,用于响应到来的信号。fork之后,操作系统会copy当前进程的task_struct机构体,除了id号不一样之外,其余完全一样。fork之后如果没有调用exec(),那么仅仅只是生成多个当前的进程,提升并发的能力,比如说nginx。nginx的进程都是master进程fork出来的,所以他们有相同的监听句柄。至于是哪个worker进程去响应,nginx有自己的竞争方式。最后关于子进程fork自己会发生什么。请看下图:

a123456678 2019-12-02 02:39:00 0 浏览量 回答数 0

回答

先说一下fork,fork会生成一个和当前进程相同的副本,称为子进程。原进程的所有资源都以适当的方式复制到子进程,因此该系统调用之后,原来的进程就有了两个独立的实例。这两个实例的联系包括:同一组打开文件、同样的工作目录、内存中同样的数据(两个进程各有一份副本)。当然Linux使用了copy on write,也就是说只有新的进程对内存页执行write操作的时候才会复制内存页面。具体如何完成:首先要了解一下task_struct这个数据结构。Linux内核很多涉及进程的部分都围绕这个数据结构(数据结构定义在include/sched.h中,有兴趣去看一下)。数据结构里面的成员非常多,下面会按照几个部分介绍一下。状态和执行信息,如待决信号、使用的二进制格式(和其他系统二进制格式的任何仿真信息)、进程ID号(pid)、到父进程及其他有关进程的指针、优先级和程序执行有关的时间信息(例如CPU时间)。有关已经分配的虚拟内存的信息。进程身份凭据,如用户ID、组ID以及权限等。可使用系统调用查询(或修改)这些数据。使用的文件包含程序代码的二进制文件,以及进程所处理的所有文件的文件系统信息,这些都必须保存下来。线程信息记录该进程特定于CPU的运行时间数据(该结构的其余字段与所使用的硬件无关)。在与其他应用程序协作时所需的进程间通信有关的信息。该进程所用的信号处理程序,用于响应到来的信号。fork之后,操作系统会copy当前进程的task_struct机构体,除了id号不一样之外,其余完全一样。fork之后如果没有调用exec(),那么仅仅只是生成多个当前的进程,提升并发的能力,比如说nginx。nginx的进程都是master进程fork出来的,所以他们有相同的监听句柄。至于是哪个worker进程去响应,nginx有自己的竞争方式。最后关于子进程fork自己会发生什么。请看下图:linux启动时候只有一个init进程,剩下的题主自行理解。

a123456678 2019-12-02 02:54:52 0 浏览量 回答数 0

回答

克隆是原始副本的精确副本。在 java 中,它实质上意味着能够创建与原始对象具有类似状态的对象。克隆() 方法提供此功能。 浅副本尽可能少地重复。默认情况下,java 克隆是浅拷贝或"按字段复制字段",即 Object 类不知道将调用克隆()方法的类的结构。因此,JVM当要求克隆时,做以下的事情: 1) 如果类只有基元数据类型成员,则将创建对象的全新副本,并返回对新对象副本的引用。 2) 如果类包含任何类类型的成员,则仅复制对这些成员的对象引用,因此原始对象和克隆对象中的成员引用引用同一对象。 深层副本复制了所有内容。集合的深层副本是两个集合,原始集合中的所有元素都重复。在这里,我们希望克隆独立于原始克隆,并且对克隆进行更改不应影响原始克隆。 深度克隆要求满足以下规则。 无需单独复制基元。 原始类中的所有成员类都应支持克隆,上下文中原始类的克隆方法应在所有成员类上调用 super.clone()。 如果任何成员类不支持克隆,那么在克隆方法中,必须创建该成员类的新实例,并逐个将其所有属性复制到新成员类对象。此新成员类对象将在克隆对象中设置。

YDYK 2020-04-25 21:08:05 0 浏览量 回答数 0

回答

深入剖析IPv4和IPv6 深入剖析IP协议,大部分时间就是深入剖析IP头部协议,随着现在的IPv6马上的普及,我们今天就来详细分析一下IPv4和IPv6的头部。 IPv4 1.版本号:占四位,就是IP协议的版本,通信双方的IP协议必须要达到一致,IPv4的版本就是4. 2.首部长度:占四位,因为长度为四比特,所以首部长度的最大值为1111,15,又因为首部长度代表的单位长度为32个字(也就是4个字节),所以首部长度的最小值就是0101,当然,也确实如此,大部分的ip头部中首部字节都是0101.也就是5*4=20个字节,如果是最大值15的话,ip首部的最大值就是60个字节,所以记好了,ipv4首部长度的最大值就是60,当然当中我们又能发现,IPv4的首段长度一定是4字节的整数倍,要是不是怎么办呢?别急,后面的填充字段会自动填充补齐到4字节的整数倍的。 3.区分服务:这个没有什么用处,也没有什么好讲的了,只要自动这玩意占八位,一个字节就可以了。 4.总长度:占16位,这个的意思就是ip数据报中首部和数据的总和的长度,因为占16位,所以很好理解,总长度的最大值就是2的16次方减一,65535,这玩意也对应着还有一个很简单的概念,最大传输单元mtu,意味着一个IP数据报的最大长度就只能装下65535个字节,要是传输的长度超过这个怎么办,很简单,分片。 对于最大传输单元,我们可以调用netstat -in来进行查看:  对于分片我们放在片偏移里面进行详细分析。 5.标识:占16位,标识这玩意很好理解,IP在存储器中维持一个计数器,每产生一个 数据报,计数器就加1,并将此值赋给标识字段。但这个标识并不是平常的序号,因为IP是 无连接服务,数据报不存在按序接收的问题。当数据报由于长度超过网络的MTU而必须分 片时,这个标识字段的值就被复制到所有的数据报片的标识字段中,等到重组的时候,相同标识符的值的数据报就会被重新组装成一个数据报。 6.标志:占三位,一般有用的是前两位, 最低位叫做MF,MF=1表示后面还有若干个数据报,MF=0表示这已经是最后一个数据报了。 中间位叫做DF,DF表示不能进行分片,DF=0才可以进行分片操作。 7.片偏移:占13位,片偏移就是,在原来的数据报分片以后,该片在原分组中的相对位置,片偏移中的基本单位是8字节,所以,也就是说,只要是分片,每个分片的长度都是8字节的整数倍,最后一个分片不够八字节的一样是填充。 8.生存时间ttl:占8位,(time to live),表明数据报在网络中的寿命,这个值被设定成跳数,顾名思义,就是这个数据报可以经过多少个路由器的数量,每经过一个路由器,该值就减一,减到为零的时候就被抛弃,显而易见,这个跳数的最大值就是2的8次方减一,255. 9.协议:就是用来指明数据报携带了哪种协议,占8位。 10.首部效验和:占16位,这个字段用来效验数据报首段,下面给出简单的计算方法:  首先在发送端的时候,将效验和全部置为0,然后把数据报首段数据全部进行反码相加,得到的值为效验和,放入首段效验和里面,然后接收端将数据报首段数据和效验和一起全部反码相加,最后若是得到零,则保留,若是不为零,则说明数据报在传输的过程中发生了改变,则丢弃该数据报。 11.IP源地址:占32位,将IP地址看作是32位数值则需要将网络字节顺序转化位主机字节顺序。转化的方法是:将每4个字节首尾互换,将2、3字节互换。 12.目的地址:也占32位,转换方法和来源IP地址一样。 13.到了可变部分IPv4的头部基本上就已经讲完了,增加头部的可变选项实际上就是增加了数据报的功能,可变选项在实际上是很少用到的。 在IP协议中,IP协议是面向非连接的,所谓的非连接就是在数据的传递过程中,不需要检测网络是否连通,所以是不可靠的数据报协议。IP协议主要用于在主机之间的寻址和选择数据包路由。 IPv6 与IPv4相比,IPv6的头部做了如下修改: 1.取消了首部长度,因为IPv6的首部长度是固定40个字节。 2.取消了服务类型,因为流标号和优先级结合起来实现了服务类型的功能。 3.取消了总长度字段,改用为有效载荷长度,有效载荷就是后面的扩展首部加上数据报中的数据。 4.取消了标识,标志和片偏移,因为这些功能都包含在了扩展首部里面。 5.取消了协议字段,改用为下一个首部,功能不变,这样更容易理解。 6.取消了生存时间ttl,改用为跳数限制,功能不变,这样更容易理解,更形象了。 7.取消了首部效验和,这样加快了路由器对数据报的处理速度,在数据链路层中,当我们发现有差错的帧就会抛弃,在运输层中,在udp中,当发现有差错就会抛弃,在tcp中,当发现有差错就会重传,直到传送到目的进程为止。因此在网路层的检测就可以精简掉。 8,取消了选项字段,功能归并在了扩展首部上。

问问小秘 2020-04-29 15:55:51 0 浏览量 回答数 0

回答

深入剖析IPv4和IPv6 深入剖析IP协议,大部分时间就是深入剖析IP头部协议,随着现在的IPv6马上的普及,我们今天就来详细分析一下IPv4和IPv6的头部。 IPv4 1.版本号:占四位,就是IP协议的版本,通信双方的IP协议必须要达到一致,IPv4的版本就是4. 2.首部长度:占四位,因为长度为四比特,所以首部长度的最大值为1111,15,又因为首部长度代表的单位长度为32个字(也就是4个字节),所以首部长度的最小值就是0101,当然,也确实如此,大部分的ip头部中首部字节都是0101.也就是5*4=20个字节,如果是最大值15的话,ip首部的最大值就是60个字节,所以记好了,ipv4首部长度的最大值就是60,当然当中我们又能发现,IPv4的首段长度一定是4字节的整数倍,要是不是怎么办呢?别急,后面的填充字段会自动填充补齐到4字节的整数倍的。 3.区分服务:这个没有什么用处,也没有什么好讲的了,只要自动这玩意占八位,一个字节就可以了。 4.总长度:占16位,这个的意思就是ip数据报中首部和数据的总和的长度,因为占16位,所以很好理解,总长度的最大值就是2的16次方减一,65535,这玩意也对应着还有一个很简单的概念,最大传输单元mtu,意味着一个IP数据报的最大长度就只能装下65535个字节,要是传输的长度超过这个怎么办,很简单,分片。 对于最大传输单元,我们可以调用netstat -in来进行查看:  对于分片我们放在片偏移里面进行详细分析。 5.标识:占16位,标识这玩意很好理解,IP在存储器中维持一个计数器,每产生一个 数据报,计数器就加1,并将此值赋给标识字段。但这个标识并不是平常的序号,因为IP是 无连接服务,数据报不存在按序接收的问题。当数据报由于长度超过网络的MTU而必须分 片时,这个标识字段的值就被复制到所有的数据报片的标识字段中,等到重组的时候,相同标识符的值的数据报就会被重新组装成一个数据报。 6.标志:占三位,一般有用的是前两位, 最低位叫做MF,MF=1表示后面还有若干个数据报,MF=0表示这已经是最后一个数据报了。 中间位叫做DF,DF表示不能进行分片,DF=0才可以进行分片操作。 7.片偏移:占13位,片偏移就是,在原来的数据报分片以后,该片在原分组中的相对位置,片偏移中的基本单位是8字节,所以,也就是说,只要是分片,每个分片的长度都是8字节的整数倍,最后一个分片不够八字节的一样是填充。 8.生存时间ttl:占8位,(time to live),表明数据报在网络中的寿命,这个值被设定成跳数,顾名思义,就是这个数据报可以经过多少个路由器的数量,每经过一个路由器,该值就减一,减到为零的时候就被抛弃,显而易见,这个跳数的最大值就是2的8次方减一,255. 9.协议:就是用来指明数据报携带了哪种协议,占8位。 10.首部效验和:占16位,这个字段用来效验数据报首段,下面给出简单的计算方法:  首先在发送端的时候,将效验和全部置为0,然后把数据报首段数据全部进行反码相加,得到的值为效验和,放入首段效验和里面,然后接收端将数据报首段数据和效验和一起全部反码相加,最后若是得到零,则保留,若是不为零,则说明数据报在传输的过程中发生了改变,则丢弃该数据报。 11.IP源地址:占32位,将IP地址看作是32位数值则需要将网络字节顺序转化位主机字节顺序。转化的方法是:将每4个字节首尾互换,将2、3字节互换。 12.目的地址:也占32位,转换方法和来源IP地址一样。 13.到了可变部分IPv4的头部基本上就已经讲完了,增加头部的可变选项实际上就是增加了数据报的功能,可变选项在实际上是很少用到的。 在IP协议中,IP协议是面向非连接的,所谓的非连接就是在数据的传递过程中,不需要检测网络是否连通,所以是不可靠的数据报协议。IP协议主要用于在主机之间的寻址和选择数据包路由。 IPv6 与IPv4相比,IPv6的头部做了如下修改: 1.取消了首部长度,因为IPv6的首部长度是固定40个字节。 2.取消了服务类型,因为流标号和优先级结合起来实现了服务类型的功能。 3.取消了总长度字段,改用为有效载荷长度,有效载荷就是后面的扩展首部加上数据报中的数据。 4.取消了标识,标志和片偏移,因为这些功能都包含在了扩展首部里面。 5.取消了协议字段,改用为下一个首部,功能不变,这样更容易理解。 6.取消了生存时间ttl,改用为跳数限制,功能不变,这样更容易理解,更形象了。 7.取消了首部效验和,这样加快了路由器对数据报的处理速度,在数据链路层中,当我们发现有差错的帧就会抛弃,在运输层中,在udp中,当发现有差错就会抛弃,在tcp中,当发现有差错就会重传,直到传送到目的进程为止。因此在网路层的检测就可以精简掉。 8,取消了选项字段,功能归并在了扩展首部上。

问问小秘 2020-04-29 15:55:26 0 浏览量 回答数 0

回答

深入剖析IPv4和IPv6 深入剖析IP协议,大部分时间就是深入剖析IP头部协议,随着现在的IPv6马上的普及,我们今天就来详细分析一下IPv4和IPv6的头部。 IPv4 1.版本号:占四位,就是IP协议的版本,通信双方的IP协议必须要达到一致,IPv4的版本就是4. 2.首部长度:占四位,因为长度为四比特,所以首部长度的最大值为1111,15,又因为首部长度代表的单位长度为32个字(也就是4个字节),所以首部长度的最小值就是0101,当然,也确实如此,大部分的ip头部中首部字节都是0101.也就是5*4=20个字节,如果是最大值15的话,ip首部的最大值就是60个字节,所以记好了,ipv4首部长度的最大值就是60,当然当中我们又能发现,IPv4的首段长度一定是4字节的整数倍,要是不是怎么办呢?别急,后面的填充字段会自动填充补齐到4字节的整数倍的。 3.区分服务:这个没有什么用处,也没有什么好讲的了,只要自动这玩意占八位,一个字节就可以了。 4.总长度:占16位,这个的意思就是ip数据报中首部和数据的总和的长度,因为占16位,所以很好理解,总长度的最大值就是2的16次方减一,65535,这玩意也对应着还有一个很简单的概念,最大传输单元mtu,意味着一个IP数据报的最大长度就只能装下65535个字节,要是传输的长度超过这个怎么办,很简单,分片。 对于最大传输单元,我们可以调用netstat -in来进行查看:  对于分片我们放在片偏移里面进行详细分析。 5.标识:占16位,标识这玩意很好理解,IP在存储器中维持一个计数器,每产生一个 数据报,计数器就加1,并将此值赋给标识字段。但这个标识并不是平常的序号,因为IP是 无连接服务,数据报不存在按序接收的问题。当数据报由于长度超过网络的MTU而必须分 片时,这个标识字段的值就被复制到所有的数据报片的标识字段中,等到重组的时候,相同标识符的值的数据报就会被重新组装成一个数据报。 6.标志:占三位,一般有用的是前两位, 最低位叫做MF,MF=1表示后面还有若干个数据报,MF=0表示这已经是最后一个数据报了。 中间位叫做DF,DF表示不能进行分片,DF=0才可以进行分片操作。 7.片偏移:占13位,片偏移就是,在原来的数据报分片以后,该片在原分组中的相对位置,片偏移中的基本单位是8字节,所以,也就是说,只要是分片,每个分片的长度都是8字节的整数倍,最后一个分片不够八字节的一样是填充。 8.生存时间ttl:占8位,(time to live),表明数据报在网络中的寿命,这个值被设定成跳数,顾名思义,就是这个数据报可以经过多少个路由器的数量,每经过一个路由器,该值就减一,减到为零的时候就被抛弃,显而易见,这个跳数的最大值就是2的8次方减一,255. 9.协议:就是用来指明数据报携带了哪种协议,占8位。 10.首部效验和:占16位,这个字段用来效验数据报首段,下面给出简单的计算方法:  首先在发送端的时候,将效验和全部置为0,然后把数据报首段数据全部进行反码相加,得到的值为效验和,放入首段效验和里面,然后接收端将数据报首段数据和效验和一起全部反码相加,最后若是得到零,则保留,若是不为零,则说明数据报在传输的过程中发生了改变,则丢弃该数据报。 11.IP源地址:占32位,将IP地址看作是32位数值则需要将网络字节顺序转化位主机字节顺序。转化的方法是:将每4个字节首尾互换,将2、3字节互换。 12.目的地址:也占32位,转换方法和来源IP地址一样。 13.到了可变部分IPv4的头部基本上就已经讲完了,增加头部的可变选项实际上就是增加了数据报的功能,可变选项在实际上是很少用到的。 在IP协议中,IP协议是面向非连接的,所谓的非连接就是在数据的传递过程中,不需要检测网络是否连通,所以是不可靠的数据报协议。IP协议主要用于在主机之间的寻址和选择数据包路由。 IPv6 与IPv4相比,IPv6的头部做了如下修改: 1.取消了首部长度,因为IPv6的首部长度是固定40个字节。 2.取消了服务类型,因为流标号和优先级结合起来实现了服务类型的功能。 3.取消了总长度字段,改用为有效载荷长度,有效载荷就是后面的扩展首部加上数据报中的数据。 4.取消了标识,标志和片偏移,因为这些功能都包含在了扩展首部里面。 5.取消了协议字段,改用为下一个首部,功能不变,这样更容易理解。 6.取消了生存时间ttl,改用为跳数限制,功能不变,这样更容易理解,更形象了。 7.取消了首部效验和,这样加快了路由器对数据报的处理速度,在数据链路层中,当我们发现有差错的帧就会抛弃,在运输层中,在udp中,当发现有差错就会抛弃,在tcp中,当发现有差错就会重传,直到传送到目的进程为止。因此在网路层的检测就可以精简掉。 8,取消了选项字段,功能归并在了扩展首部上。

问问小秘 2020-04-29 15:55:23 0 浏览量 回答数 0

问题

名词解释都有哪些?

猫饭先生 2019-12-01 21:19:24 915 浏览量 回答数 0

回答

一、数据库瓶颈 不管是IO瓶颈,还是CPU瓶颈,最终都会导致数据库的活跃连接数增加,进而逼近甚至达到数据库可承载活跃连接数的阈值。在业务Service来看就是,可用数据库连接少甚至无连接可用。接下来就可以想象了吧(并发量、吞吐量、崩溃)。 1、IO瓶颈 第一种:磁盘读IO瓶颈,热点数据太多,数据库缓存放不下,每次查询时会产生大量的IO,降低查询速度 -> 分库和垂直分表。 第二种:网络IO瓶颈,请求的数据太多,网络带宽不够 -> 分库。 2、CPU瓶颈 第一种:SQL问题,如SQL中包含join,group by,order by,非索引字段条件查询等,增加CPU运算的操作 -> SQL优化,建立合适的索引,在业务Service层进行业务计算。 第二种:单表数据量太大,查询时扫描的行太多,SQL效率低,CPU率先出现瓶颈 -> 水平分表。 二、分库分表 1、水平分库 概念:以字段为依据,按照一定策略(hash、range等),将一个库中的数据拆分到多个库中。 结果: 每个库的结构都一样; 每个库的数据都不一样,没有交集; 所有库的并集是全量数据; 场景:系统绝对并发量上来了,分表难以根本上解决问题,并且还没有明显的业务归属来垂直分库。 分析:库多了,io和cpu的压力自然可以成倍缓解。 2、水平分表 概念:以字段为依据,按照一定策略(hash、range等),将一个表中的数据拆分到多个表中。 结果: 每个表的结构都一样; 每个表的数据都不一样,没有交集; 所有表的并集是全量数据; 场景:系统绝对并发量并没有上来,只是单表的数据量太多,影响了SQL效率,加重了CPU负担,以至于成为瓶颈。推荐:一次SQL查询优化原理分析 分析:表的数据量少了,单次SQL执行效率高,自然减轻了CPU的负担。 3、垂直分库 概念:以表为依据,按照业务归属不同,将不同的表拆分到不同的库中。 结果: 每个库的结构都不一样; 每个库的数据也不一样,没有交集; 所有库的并集是全量数据; 场景:系统绝对并发量上来了,并且可以抽象出单独的业务模块。 分析:到这一步,基本上就可以服务化了。例如,随着业务的发展一些公用的配置表、字典表等越来越多,这时可以将这些表拆到单独的库中,甚至可以服务化。再有,随着业务的发展孵化出了一套业务模式,这时可以将相关的表拆到单独的库中,甚至可以服务化。 4、垂直分表 概念:以字段为依据,按照字段的活跃性,将表中字段拆到不同的表(主表和扩展表)中。 结果: 每个表的结构都不一样; 每个表的数据也不一样,一般来说,每个表的字段至少有一列交集,一般是主键,用于关联数据; 所有表的并集是全量数据; 场景:系统绝对并发量并没有上来,表的记录并不多,但是字段多,并且热点数据和非热点数据在一起,单行数据所需的存储空间较大。以至于数据库缓存的数据行减少,查询时会去读磁盘数据产生大量的随机读IO,产生IO瓶颈。 分析:可以用列表页和详情页来帮助理解。垂直分表的拆分原则是将热点数据(可能会冗余经常一起查询的数据)放在一起作为主表,非热点数据放在一起作为扩展表。这样更多的热点数据就能被缓存下来,进而减少了随机读IO。拆了之后,要想获得全部数据就需要关联两个表来取数据。 但记住,千万别用join,因为join不仅会增加CPU负担并且会讲两个表耦合在一起(必须在一个数据库实例上)。关联数据,应该在业务Service层做文章,分别获取主表和扩展表数据然后用关联字段关联得到全部数据。 三、分库分表工具 sharding-sphere:jar,前身是sharding-jdbc; TDDL:jar,Taobao Distribute Data Layer; Mycat:中间件。 注:工具的利弊,请自行调研,官网和社区优先。 四、分库分表步骤 根据容量(当前容量和增长量)评估分库或分表个数 -> 选key(均匀)-> 分表规则(hash或range等)-> 执行(一般双写)-> 扩容问题(尽量减少数据的移动)。 扩展:MySQL:分库分表与分区的区别和思考 五、分库分表问题 1、非partition key的查询问题 基于水平分库分表,拆分策略为常用的hash法。 端上除了partition key只有一个非partition key作为条件查询 映射法 基因法 注:写入时,基因法生成user_id,如图。关于xbit基因,例如要分8张表,23=8,故x取3,即3bit基因。根据user_id查询时可直接取模路由到对应的分库或分表。 根据user_name查询时,先通过user_name_code生成函数生成user_name_code再对其取模路由到对应的分库或分表。id生成常用snowflake算法。 端上除了partition key不止一个非partition key作为条件查询 映射法 冗余法 注:按照order_id或buyer_id查询时路由到db_o_buyer库中,按照seller_id查询时路由到db_o_seller库中。感觉有点本末倒置!有其他好的办法吗?改变技术栈呢? 后台除了partition key还有各种非partition key组合条件查询 NoSQL法 冗余法 2、非partition key跨库跨表分页查询问题 基于水平分库分表,拆分策略为常用的hash法。 注:用NoSQL法解决(ES等)。 3、扩容问题 基于水平分库分表,拆分策略为常用的hash法。 水平扩容库(升级从库法) 注:扩容是成倍的。 水平扩容表(双写迁移法) 第一步:(同步双写)修改应用配置和代码,加上双写,部署; 第二步:(同步双写)将老库中的老数据复制到新库中; 第三步:(同步双写)以老库为准校对新库中的老数据; 第四步:(同步双写)修改应用配置和代码,去掉双写,部署; 注:双写是通用方案。 六、分库分表总结 分库分表,首先得知道瓶颈在哪里,然后才能合理地拆分(分库还是分表?水平还是垂直?分几个?)。且不可为了分库分表而拆分。 选key很重要,既要考虑到拆分均匀,也要考虑到非partition key的查询。 只要能满足需求,拆分规则越简单越好。 七、分库分表示例 示例GitHub地址:https://github.com/littlecharacter4s/study-sharding 来源:cnblogs.com/littlecharacter/p/9342129.html 俩元

AA大大官 2020-03-31 12:45:48 0 浏览量 回答数 0

问题

ES 的分布式架构原理能说一下么(ES 是如何实现分布式的啊)?【Java问答学堂】26期

剑曼红尘 2020-05-26 20:30:13 41 浏览量 回答数 1

回答

一。zval、引用计数、变量分离、写时拷贝我们一步步来理解1、php语言特性PHP是脚本语言,所谓脚本语言,就是说PHP并不是独立运行的,要运行PHP代码需要PHP解析器,用户编写的PHP代码最终都会被PHP解析器解析执行PHP的执行是通过Zend engine(ZE, Zend引擎),ZE是用C编写的用户编写的PHP代码最终都会被翻译成PHP的虚拟机ZE的虚拟指令(OPCODES)来执行也就说最终会被翻译成一条条的指令既然这样,有什么结果和你预想的不一样,查看php源码是最直接最有效的 2、php变量的存储结构在PHP中,所有的变量都是用一个结构zval结构来保存的,在Zend/zend.h中可以看到zval的定义:zval结构包括:① value —— 值,是真正保存数据的关键部分,定义为一个联合体(union)② type —— 用来储存变量的类型 ③ is_ref —— 下面介绍④ refcount —— 下面介绍 声明一个变量$addr="北京";PHP内部都是使用zval来表示变量的,那对于上面的脚本,ZE是如何把addr和内部的zval结构联系起来的呢?变量都是有名字的(本例中变量名为addr)而zval中并没有相应的字段来体现变量名。PHP内部肯定有一个机制,来实现变量名到zval的映射在PHP中,所有的变量都会存储在一个数组中(确切的说是hash table)当你创建一个变量的时候,PHP会为这个变量分配一个zval,填入相应的信息,然后将这个变量的名字和指向这个zval的指针填入一个数组中。当你获取这个变量的时候,PHP会通过查找这个数组,取得对应的zval 注意:数组和对象这类复合类型在生成zval时,会为每个单元生成一个zval3、我们经常说每个变量都有一个内存地址,那这个zval和变量的内存地址,这俩有什么关系吗?定义一个变量会开辟一块内存,这块内存好比一个盒子,盒子里放了zval,zval里保存了变量的相关信息,需要开辟多大的内存,是由zval所占空间大小决定的zval是内存对象,垃圾回收的时候会把zval和内存地址(盒子)分别释放掉 4、引用计数、变量分离、写时拷贝zval中的refcount和is_ref还没有介绍,我们知道PHP是一个长时间运行的服务器端脚本。那么对于它来说,效率和资源占用率是一个很重要的衡量标准,也就是说,PHP必须尽量减少内存占用率。考虑下面这段代码:第一行代码创建了一个字符串变量,申请了一个大小为9字节的内存,保存了字符串“laruence”和一个NULL(0)的结尾第二行定义了一个新的字符串变量,并将变量var的值“复制”给这个新的变量第三行unset了变量var 这样的代码是很常见的,如果PHP对于每一个变量赋值都重新分配内存,copy数据的话,那么上面的这段代码就要申请18个字节的内存空间,为了申请新的内存,还需要cpu执行某些计算,这当然会加重cpu的负载而我们也很容易看出来,上面的代码其实根本没有必要申请两份空间,当第三句执行后,$var被释放了,我们刚才的设想(申请18个字节内存空间)突然变的很滑稽,这次复制显得好多余。如果早知道$var不用了,直接让$var_dup用$var的内存不就行了,还复制干嘛?如果你觉得9个字节没什么,那设想下如果$var是个10M的文件内容,或者20M,是不是我们的计算机资源消耗的有点冤枉呢?呵呵,PHP的开发者也看出来了: 刚才说了,PHP中的变量是用一个存储在symbol_table中的符号名,对应一个zval来实现的,比如对于上面的第一行代码,会在symbol_table中存储一个值“var”,对应的有一个指针指向一个zval结构,变量值“laruence”保存在这个zval中,所以不难想象,对于上面的代码来说,我们完全可以让“var”和“var_dup”对应的指针都指向同一个zval就可以了(额,鸟哥一会说hash table,一会说symbol_table,暂且理解为symbol_table是hash table的子集) PHP也是这样做的,这个时候就需要介绍一下zval结构中的refcount字段了refcount,引用计数,记录了当前的zval被引用的次数(这里的引用并不是真正的 & ,而是有几个变量指向它)比如对于代码:第一行,创建了一个整形变量,变量值是1。 此时保存整形1的这个zval的refcount为1第二行,创建了一个新的整形变量(通过赋值的方式),变量也指向刚才创建的zval,并将这个zval的refcount加1,此时这个zval的refcount为2所以,这个时候(通过值传递的方式赋值给别的变量),并没有产生新的zval,两个变量指向同一zval,通过一个计数器来共用zval及内存地址,以达到节省内存空间的目的当一个变量被第一次创建的时候,它对应的zval结构的refcount的值会被初始化为1,因为只有这一个变量在用它。但是当你把这个变量赋值给别的变量时,refcount属性便会加1变成2,因为现在有两个变量在用这个zval结构了 PHP提供了一个函数可以帮助我们了解这个过程debug_zval_dump输出:long(1) refcount(2)long(1) refcount(3)如果你奇怪 ,var的refcount应该是1啊?我们知道,对于简单变量,PHP是以传值的形式传参数的。也就是说,当执行debug_zval_dump($var)的时候,$var会以传值的方式传递给debug_zval_dump,也就是会导致var的refcount加1,所以只要能看到,当变量赋值给一个变量以后,能导致zval的refcount加1这个结果即可现在我们回头看上面的代码, 当执行了最后一行unset($var)以后,会发生什么呢?unset($var)的时候,它删除符号表里的$var的信息,准备清理它对应的zval及内存空间,这时它发现$var对应的zval结构的refcount值是2,也就是说,还有另外一个变量在一起用着这个zval,所以unset只需把这个zval的refcount减去1就行了上代码:输出:string(8) "laruence" refcount(2) 但是,对于下面的代码呢?很明显在这段代码执行以后,$var_dup的值应该还是“laruence”,那么这又是怎么实现的呢?这就是PHP的copy on write机制(简称COW):PHP在修改一个变量以前,会首先查看这个变量的refcount,如果refcount大于1,PHP就会执行一个分离的过程(在Zend引擎中,分离是破坏一个引用对的过程)对于上面的代码,当执行到第三行的时候,PHP发现$var想要改变,并且它指向的zval的refcount大于1,那么PHP就会复制一个新的zval出来,改变其值,将改变的变量指向新的zval(哪个变量指向新复制的zval其实已经无所谓了),并将原zval的refcount减1,并修改symbol_table里该变量的指针,使得$var和$var_dup分离(Separation)。这个机制就是所谓的copy on write(写时复制,这里的写包括普通变量的修改及数组对象里的增加、删除单元操作)如果了解了is_ref之后,上面说的并不严谨 上代码测试:输出:long(1) refcount(2)string(8) "laruence" refcount(2) 现在我们知道,当使用变量复制的时候 ,PHP内部并不是真正的复制,而是采用指向相同的zval结构来节约开销。那么,对于PHP中的引用,又是如何实现呢?这段代码结束以后,$var也会被间接的修改为1,这个过程称作(change on write:写时改变)那么ZE是怎么知道,这次的复制不需要Separation呢?这个时候就要用到zval中的is_ref字段了:对于上面的代码,当第二行执行以后,$var所代表的zval的refcount变为2,并且设置is_ref为1到第三行的时候,PHP先检查var_ref对应的zval的is_ref字段(is_ref 表示该zval是否被&引用,仅表示真或假,就像开关的开与关一样,zval的初始化情况下为0,即非引用),如果为1,则不分离,直接更改(否则需要执行刚刚提到的zval分离),更改共享的zval实际上也间接更改了$var的值,因为引擎想所有的引用变量都看到这一改变php源码做了这样一个判断,大体逻辑示意如下:如果这个zval中的if_ref为1(即被引用),或者该zval引用计数小于2任何一种方式:都不会进行分离 尽管已经存在写时复制和写时改变,但仍然还存在一些不能通过is_ref和refcount来解决的问题对于如下的代码,又会怎样呢?这里$var、$var_dup、$var_ref三个变量将共用一个zval结构(其实这是不可能的,一个zval不可能既被&,又被指向),有两个属于change-on-write组合($var和$var_ref),有两个属于copy-on-write组合($var和$var_dup),那is_ref和refcount该怎样工作,才能正确的处理好这段复杂的关系呢?答案是不可能!在这种情况下,变量的值必须分离成两份完全独立的存在当执行第二行代码的时候,和前面讲过的一样,$var_dup 和 $var 指向相同的zval, refcount为2当执行第三行的时候,PHP发现要操作的zval的refcount大于1,则PHP会执行Separation(也就是说php将一个zval的is_ref从0设为1 之前,当然此时refcount还没有增加,会看该zval的refcount,如果refcount>1,则会分离), 将$var_dup分离出去,并将$var和$var_ref做change on write关联。也就是,refcount=2, is_ref=1;所以内存会给变量var_dup 分配出一个新的zval,类型与值同 $var和$var_ref指向的zval一样,是新分配出来的,尽管他们拥有同样的值,但是必须通过两个zval来实现。试想一下,如果三者指向同一个zval的话,改边 $var_dup 的值,那么 $var和$var_ref 也会受到影响,这样就乱套了图解:下面的这段代码在内核中同样会产生歧义,所以需要强制复制!也就是说一个zval不会既被引用,又被指向,必须分离 基于这样的分析,我们就可以让debug_zval_dump出refcount为1的结果来:输出:string(8) "laruence" refcount(1) 为什么结果是refcount(1)呢debug_zval_dump()中参数是引用的话,refcount永远为1这两段代码在执行的时候是这样的逻辑:PHP先看变量指向的zval是否被引用,如果是引用,则不再产生新的zval甭管哪个变量引用了它,比如有个变量$a被引用了,$b=&$a,就算自己引用自己$a=&$a,$a所指向的zval都不会被复制,改变其中一个变量的值,另一个值也被改变(写时改变)如果is_ref为0且refcount大于1,改变其中一个变量时,复制新的zval(写时复制) 还有一个知识点需要了解下,就是PHP数组复制的机制复制一个数组,就是把一个数组赋值给一个变量便可。会把数组指针位置一同复制。这里面有两种情况:① 指针位置合法,这时直接复制,无影响② 原数组指针位置非法时(移出界),“新”数组指针会初始化(这里的新为什么要加引号?请看下文),而老的数组指针位置不变,还是false先看例子: 结果:!结果:出现这种情况好像不对?$arr2 难道不是新数组?新数组的数组指针应该重置了啊这里注意了:$arr2 = $arr1 ,在俩变量都没发生写操作时,他们其实引用的是同一个内存地址。在其中一个变量发生写操作后,内存地址会复制一份,发生改变的变量会去引用它,并把数组指针初始化。所以 $arr1 会去引用复制的内存地址,并将指针初始化二。.foreach循环时调用current等函数!结果: 56按照之前说的,foreach先赋值,再移动指针,再执行循环体,第一次结果为2可以理解为什么三次都是2呢?咋就这么2呢?因为current函数是按引用传递的函数 在zval笔记中说了,一个zval不能既被引用,又被指向所以,变量分离,重新拷贝一份数组专门用于current函数 当然,如果数组zval的is_ref为1,则不会拷贝数组了或者:结果:current是引用传参

杨冬芳 2019-12-02 02:26:33 0 浏览量 回答数 0

回答

很高兴看到有人对Lucene情有独钟-因为我对此一无所知。 另一方面,狮身人面像我很了解,所以让我们看看我是否可以帮上忙。 结果相关性排名是默认设置。您可以根据需要设置自己的排序方式,并为特定字段赋予更高的权重。 索引速度非常快,因为它直接与数据库对话。任何缓慢都会来自复杂的SQL查询和未索引的外键以及其他此类问题。我也从来没有发现搜索中有任何缓慢。 我是Rails,所以我不知道使用Django实施有多容易。Sphinx源代码附带了一个Python API。 搜索服务守护程序(searchd)的内存使用率非常低-您可以设置索引器进程也使用多少内存的限制。 可伸缩性是我的知识较粗略的地方-但将索引文件复制到多台计算机并运行多个searchd守护程序很容易。我从其他人那里得到的总体印象是,在高负载下它确实很棒,因此无需在多台计算机上进行扩展。 尽管“其他方法”可以很容易地用其他工具完成,但不支持“您是否想要”等。Sphinx确实通过使用字典来词干,因此,例如,“ driving”和“ drive”在搜索中将被视为相同。 但是,Sphinx不允许对字段数据进行部分索引更新。常用的方法是维护所有最近更改的增量索引,并在每次更改后重新索引(这些新结果在一两秒钟内出现)。由于数据量少,因此可能需要几秒钟的时间。不过,您仍然需要定期重新索引主要数据集(尽管定期性如何取决于数据的波动性-每天还是每小时?)。快速的索引速度使这一切都很轻松。 我不知道这对您的情况有多适用,但是Evan Weaver比较了一些常见的Rails搜索选项(Sphinx,Ferret(Ruby的Lucene移植版)和Solr),并运行了一些基准测试。我猜可能会有用。 我还没有深入探讨MySQL全文搜索的深度,但是我知道它在速度方面和功能方面都无法与Sphinx,Lucene或Solr竞争。来源:stack overflow

保持可爱mmm 2020-05-10 20:04:05 0 浏览量 回答数 0

回答

同步两个SQLServer数据库 如何同步两个sqlserver数据库的内容?程序代码可以有版本管理cvs进行同步管理,可是数据库同步就非常麻烦,只能自己改了一个后再去改另一个,如果忘记了更改另一个经常造成两个数据库的结构或内容上不一致.各位有什么好的方法吗? 一、分发与复制 用强制订阅实现数据库同步操作. 大量和批量的数据可以用数据库的同步机制处理: // 说明: 为方便操作,所有操作均在发布服务器(分发服务器)上操作,并使用推模式 在客户机器使用强制订阅方式。 二、测试通过 1:环境 服务器环境: 机器名称: zehuadb 操作系统:windows 2000 server 数据库版本:sql 2000 server 个人版 客户端 机器名称:zlp 操作系统:windows 2000 server 数据库版本:sql 2000 server 个人版 2:建用户帐号 在服务器端建立域用户帐号 我的电脑管理->本地用户和组->用户->建立 username:zlp userpwd:zlp 3:重新启动服务器mssqlserver 我的电脑->控制面版->管理工具->服务->mssqlserver 服务 (更改为:域用户帐号,我们新建的zlp用户 .\zlp,密码:zlp) 4:安装分发服务器 a:配置分发服务器 工具->复制->配置发布、订阅服务器和分发->下一步->下一步(所有的均采用默认配置) b:配置发布服务器 工具->复制->创建和管理发布->选择要发布的数据库(sz)->下一步->快照发布->下一步->选择要发布的内容->下一步->下一步->下一步->完成 c:强制配置订阅服务器(推模式,拉模式与此雷同) 工具->复制->配置发布、订阅服务器和分发->订阅服务器->新建->sql server数据库->输入客户端服务器名称(zlp)->使用sql server 身份验证(sa,空密码)->确定->应用->确定 d:初始化订阅 复制监视器->发布服务器(zehuadb)->双击订阅->强制新建->下一步->选择启用的订阅服务器->zlp->下一步->下一步->下一步->下一步->完成 5:测试配置是否成功 复制监视器->发布衿?zehuadb)->双击sz:sz->点状态->点立即运行代理程序 查看: 复制监视器->发布服务器(zehuadb)->sz:sz->选择zlp:sz(类型强制)->鼠标右键->启动同步处理 如果没有错误标志(红色叉),恭喜您配置成功 6:测试数据 在服务器执行: 选择一个表,执行如下sql: insert into wq_newsgroup_s select '测试成功',5 复制监视器->发布服务器(zehuadb)->sz:sz->快照->启动代理程序 ->zlp:sz(强制)->启动同步处理 去查看同步的 wq_newsgroup_s 是否插入了一条新的记录 测试完毕,通过。 7:修改数据库的同步时间,一般选择夜晚执行数据库同步处理 (具体操作略) :d /* 注意说明: 服务器一端不能以(local)进行数据的发布与分发,需要先删除注册,然后新建注册本地计算机名称 卸载方式:工具->复制->禁止发布->是在"zehuadb"上静止发布,卸载所有的数据库同步配置服务器 注意:发布服务器、分发服务器中的sqlserveragent服务必须启动 采用推模式: "d:\microsoft sql server\mssql\repldata\unc" 目录文件可以不设置共享 拉模式:则需要共享~! */ 少量数据库同步可以采用触发器实现,同步单表即可。 三、配置过程中可能出现的问题 在sql server 2000里设置和使用数据库复制之前,应先检查相关的几台sql server服务器下面几点是否满足: 1、mssqlserver和sqlserveragent服务是否是以域用户身份启动并运行的(.\administrator用户也是可以的) 如果登录用的是本地系统帐户local,将不具备网络功能,会产生以下错误: 进程未能连接到distributor '@server name' (如果您的服务器已经用了sql server全文检索服务, 请不要修改mssqlserver和sqlserveragent服务的local启动。 会照成全文检索服务不能用。请换另外一台机器来做sql server 2000里复制中的分发服务器。) 修改服务启动的登录用户,需要重新启动mssqlserver和sqlserveragent服务才能生效。 2、检查相关的几台sql server服务器是否改过名称(需要srvid=0的本地机器上srvname和datasource一样) 在查询分析器里执行: use master select srvid,srvname,datasource from sysservers 如果没有srvid=0或者srvid=0(也就是本机器)但srvname和datasource不一样, 需要按如下方法修改: use master go -- 设置两个变量 declare @serverproperty_servername varchar(100), @servername varchar(100) -- 取得windows nt 服务器和与指定的 sql server 实例关联的实例信息 select @serverproperty_servername = convert(varchar(100), serverproperty('servername')) -- 返回运行 microsoft sql server 的本地服务器名称 select @servername = convert(varchar(100), @@servername) -- 显示获取的这两个参数 select @serverproperty_servername,@servername --如果@serverproperty_servername和@servername不同(因为你改过计算机名字),再运行下面的 --删除错误的服务器名 exec sp_dropserver @server=@servername --添加正确的服务器名 exec sp_addserver @server=@serverproperty_servername, @local='local' 修改这项参数,需要重新启动mssqlserver和sqlserveragent服务才能生效。 这样一来就不会在创建复制的过程中出现18482、18483错误了。 3、检查sql server企业管理器里面相关的几台sql server注册名是否和上面第二点里介绍的srvname一样 不能用ip地址的注册名。 (我们可以删掉ip地址的注册,新建以sql server管理员级别的用户注册的服务器名) 这样一来就不会在创建复制的过程中出现14010、20084、18456、18482、18483错误了。 4、检查相关的几台sql server服务器网络是否能够正常访问 如果ping主机ip地址可以,但ping主机名不通的时候,需要在 winnt\system32\drivers\etc\hosts (win2000) windows\system32\drivers\etc\hosts (win2003) 文件里写入数据库服务器ip地址和主机名的对应关系。 例如: 127.0.0.1 localhost 192.168.0.35 oracledb oracledb 192.168.0.65 fengyu02 fengyu02 202.84.10.193 bj_db bj_db 或者在sql server客户端网络实用工具里建立别名,例如: 5、系统需要的扩展存储过程是否存在(如果不存在,需要恢复): sp_addextendedproc 'xp_regenumvalues',@dllname ='xpstar.dll' go sp_addextendedproc 'xp_regdeletevalue',@dllname ='xpstar.dll' go sp_addextendedproc 'xp_regdeletekey',@dllname ='xpstar.dll' go sp_addextendedproc xp_cmdshell ,@dllname ='xplog70.dll' 接下来就可以用sql server企业管理器里[复制]-> 右键选择 ->[配置发布、订阅服务器和分发]的图形界面来配置数据库复制了。 下面是按顺序列出配置复制的步骤: 1、建立发布和分发服务器 [欢迎使用配置发布和分发向导]->[选择分发服务器]->[使"@servername"成为它自己的分发服务器,sql server将创建分发数据库和日志] ->[制定快照文件夹]-> [自定义配置] -> [否,使用下列的默认配置] -> [完成] 上述步骤完成后, 会在当前"@servername" sql server数据库里建立了一个distribion库和 一个distributor_admin管理员级别的用户(我们可以任意修改密码)。 服务器上新增加了四个作业: [ 代理程序历史记录清除: distribution ] [ 分发清除: distribution ] [ 复制代理程序检查 ] [ 重新初始化存在数据验证失败的订阅 ] sql server企业管理器里多了一个复制监视器, 当前的这台机器就可以发布、分发、订阅了。 我们再次在sql server企业管理器里[复制]-> 右键选择 ->[配置发布、订阅服务器和分发] 我们可以在 [发布服务器和分发服务器的属性] 窗口-> [发布服务器] -> [新增] -> [确定] -> [发布数据库] -> [事务]/[合并] -> [确定] -> [订阅服务器] -> [新增] -> [确定] 把网络上的其它sql server服务器添加成为发布或者订阅服务器. 新增一台发布服务器的选项: 我这里新建立的jin001发布服务器是用管理员级别的数据库用户test连接的, 到发布服务器的管理链接要输入密码的可选框, 默认的是选中的, 在新建的jin001发布服务器上建立和分发服务器fengyu/fengyu的链接的时需要输入distributor_admin用户的密码。到发布服务器的管理链接要输入密码的可选框,也可以不选,也就是不需要密码来建立发布到分发服务器的链接(这当然欠缺安全,在测试环境下可以使用)。 2、新建立的网络上另一台发布服务器(例如jin001)选择分发服务器 [欢迎使用配置发布和分发向导]->[选择分发服务器] -> 使用下列服务器(选定的服务器必须已配置为分发服务器) -> 选定服务器 -> [下一步] -> [输入分发服务器(例如fengyu/fengyu)的distributor_admin用户的密码两次] -> [下一步] -> [自定义配置] -> [否,使用下列的默认配置] -> [下一步] -> [完成] -> [确定] 建立一个数据库复制发布的过程: [复制] -> [发布内容] -> 右键选择 -> [新建发布] -> [下一步] -> [选择发布数据库] -> [选中一个待发布的数据库] -> [下一步] -> [选择发布类型] -> [事务发布]/[合并发布] -> [下一步] -> [指定订阅服务器的类型] -> [运行sql server 2000的服务器] -> [下一步] -> [指定项目] -> [在事务发布中只可以发布带主键的表] -> [选中一个有主键的待发布的表] ->[在合并发布中会给表增加唯一性索引和 rowguidcol 属性的唯一标识符字段[rowguid],默认值是newid()] (添加新列将: 导致不带列列表的 insert 语句失败,增加表的大小,增加生成第一个快照所要求的时间) ->[选中一个待发布的表] -> [下一步] -> [选择发布名称和描述] -> -> [下一步] -> [自定义发布的属性] -> [否,根据指定方式创建发布] -> [下一步] -> [完成] -> [关闭] 发布属性里有很多有用的选项:设定订阅到期(例如24小时) 设定发布表的项目属性: 常规窗口可以指定发布目的表的名称,可以跟原来的表名称不一样。 下图是命令和快照窗口的栏目 ( sql server 数据库复制技术实际上是用insert,update,delete操作在订阅服务器上重做发布服务器上的事务操作 看文档资料需要把发布数据库设成完全恢复模式,事务才不会丢失 但我自己在测试中发现发布数据库是简单恢复模式下,每10秒生成一些大事务,10分钟后再收缩数据库日志, 这期间发布和订阅服务器上的作业都暂停,暂停恢复后并没有丢失任何事务更改 ) 发布表可以做数据筛选,例如只选择表里面的部分列: 例如只选择表里某些符合条件的记录, 我们可以手工编写筛选的sql语句: 发布表的订阅选项,并可以建立强制订阅: 成功建立了发布以后,发布服务器上新增加了一个作业: [ 失效订阅清除 ] 分发服务器上新增加了两个作业: [ jin001-dack-dack-5 ] 类型[ repl快照 ] [ jin001-dack-3 ] 类型[ repl日志读取器 ] 上面蓝色字的名称会根据发布服务器名,发布名及第几次发布而使用不同的编号 repl快照作业是sql server复制的前提条件,它会先把发布的表结构,数据,索引,约束等生成到发布服务器的os目录下文件 (当有订阅的时候才会生成, 当订阅请求初始化或者按照某个时间表调度生成) repl日志读取器在事务复制的时候是一直处于运行状态。(在合并复制的时候可以根据调度的时间表来运行) 建立一个数据库复制订阅的过程: [复制] -> [订阅] -> 右键选择 -> [新建请求订阅] -> [下一步] -> [查找发布] -> [查看已注册服务器所做的发布] -> [下一步] -> [选择发布] -> [选中已经建立发布服务器上的数据库发布名] -> [下一步] -> [指定同步代理程序登录] -> [当代理程序连接到代理服务器时:使用sql server身份验证] (输入发布服务器上distributor_admin用户名和密码) -> [下一步] -> [选择目的数据库] -> [选择在其中创建订阅的数据库名]/[也可以新建一个库名] -> [下一步] -> [允许匿名订阅] -> [是,生成匿名订阅] -> [下一步] -> [初始化订阅] -> [是,初始化架构和数据] -> [下一步] -> [快照传送] -> [使用该发布的默认快照文件夹中的快照文件] (订阅服务器要能访问发布服务器的repldata文件夹,如果有问题,可以手工设置网络共享及共享权限) -> [下一步] -> [快照传送] -> [使用该发布的默认快照文件夹中的快照文件] -> [下一步] -> [设置分发代理程序调度] -> [使用下列调度] -> [更改] -> [例如每五分钟调度一次] -> [下一步] -> [启动要求的服务] -> [该订阅要求在发布服务器上运行sqlserveragent服务] -> [下一步] -> [完成] -> [确定] 成功建立了订阅后,订阅服务器上新增加了一个类别是[repl-分发]作业(合并复制的时候类别是[repl-合并]) 它会按照我们给的时间调度表运行数据库同步复制的作业。 3、sql server复制配置好后, 可能出现异常情况的实验日志: 1.发布服务器断网,sql server服务关闭,重启动,关机的时候,对已经设置好的复制没有多大影响 中断期间,分发和订阅都接收到没有复制的事务信息 2.分发服务器断网,sql server服务关闭,重启动,关机的时候,对已经设置好的复制有一些影响 中断期间,发布服务器的事务排队堆积起来 (如果设置了较长时间才删除过期订阅的选项, 繁忙发布数据库的事务日志可能会较快速膨胀), 订阅服务器会因为访问不到发布服务器,反复重试 我们可以设置重试次数和重试的时间间隔(最大的重试次数是9999, 如果每分钟重试一次,可以支持约6.9天不出错) 分发服务器sql server服务启动,网络接通以后,发布服务器上的堆积作业将按时间顺序作用到订阅机器上: 会需要一个比较长的时间(实际上是生成所有事务的insert,update,delete语句,在订阅服务器上去执行) 我们在普通的pc机上实验的58个事务100228个命令执行花了7分28秒. 3.订阅服务器断网,sql server服务关闭,重启动,关机的时候,对已经设置好的复制影响比较大,可能需要重新初试化 我们实验环境(订阅服务器)从18:46分意外停机以, 第二天8:40分重启动后, 已经设好的复制在8:40分以后又开始正常运行了, 发布服务器上的堆积作业将按时间顺序作用到订阅机器上, 但复制管理器里出现快照的错误提示, 快照可能需要重新初试化,复制可能需要重新启动.(我们实验环境的机器并没有进行快照初试化,复制仍然是成功运行的) 4、删除已经建好的发布和定阅可以直接用delete删除按钮 我们最好总是按先删定阅,再删发布,最后禁用发布的顺序来操作。 如果要彻底删去sql server上面的复制设置, 可以这样操作: [复制] -> 右键选择 [禁用发布] -> [欢迎使用禁用发布和分发向导] -> [下一步] -> [禁用发布] -> [要在"@servername"上禁用发布] -> [下一步] -> [完成禁用发布和分发向导] -> [完成] 我们也可以用t-sql命令来完成复制中发布及订阅的创建和删除, 选中已经设好的发布和订阅, 按属标右键可以[生成sql脚本]。(这里就不详细讲了, 后面推荐的网站内有比较详细的内容) 当你试图删除或者变更一个table时,出现以下错误 server: msg 3724, level 16, state 2, line 1 cannot drop the table 'object_name' because it is being used for replication. 比较典型的情况是该table曾经用于复制,但是后来又删除了复制。 处理办法: select * from sysobjects where replinfo >'0' sp_configure 'allow updates', 1 go reconfigure with override go begin transaction update sysobjects set replinfo = '0' where replinfo >'0' commit transaction go rollback transaction go sp_configure 'allow updates', 0 go reconfigure with override go 答案来源于网络

养狐狸的猫 2019-12-02 02:18:58 0 浏览量 回答数 0

回答

MongoDB ACID事务支持 这里要有一定的关系型数据库的事务的概念,不然不一定能理解的了这里说的事务概念。 下面说一说MongoDB的事务支持,这里可能会有疑惑,前面我们在介绍MongoDB时,说MongoDB是一个NoSQL数据库,不支持事务。这里又介绍MongoDB的事务。这里要说明一下MongoDB的事务支持跟关系型数据库的事务支持是两码事,如果你已经非常了解关系型数据库的事务,通过下面一副图对比MongoDB事务跟MySQL事务的不同之处。 MongoDB是如何实现事务的ACID? 1)MongoDB对原子性(Atomicity)的支持 原子性在Mongodb中到底是一个什么概念呢?为什么说支持但又说Mongodb的原子性是单行/文档级原子性,这里提供了一个MongoDB更新语句样例,如下图: MongoDB是如何实现事务的ACID? 更新“username”等于“tj.tang”的文档,更新salary、jobs、hours字段。这里对于这三个字段Mongodb在执行时要么都更新要么都不更新,这个概念在MySQL中可能你没有考虑过,但在MongoDB中由于文档可以嵌套子文档可以很复杂,所以Mongodb的原子性叫单行/文档级原子性。 对于关系型数据库的多行、多文档、多语句原子性目前Mongodb是不支持的,如下情况: MongoDB是如何实现事务的ACID? MongoDB更新条件为工资小于50万的人都把工资调整为50万,这就会牵扯到多文档更新原子性。如果当更新到Frank这个文档时,出现宕机,服务器重启之后是无法像关系型数据库那样做到数据回滚的,也就是说处理这种多文档关系型数据库事务的支持,但MongoDB不支持。那么怎么解决Mongodb这个问题呢?可以通过建模,MongoDB不是范式而是反范式的设计,通过大表和小表可以把相关的数据放到同一个文档中去。然后通过一条语句来执行操作。 2)MongoDB对一致性(consistency)的支持 对于数据一致性来说,传统数据库(单机)跟分布式数据库(MongoDB)对于数据一致性是不太一样的,怎么理解呢?如下图: MongoDB是如何实现事务的ACID? 对于传统型数据库来说,数据一致性主要是在单机上,单机的问题主要是数据进来时的规则检验,数据不能被破坏掉。而在分布式数据库上,因为他们都是多节点分布式的,我们讲的一致性往往就是讲的各个节点之间的数据是否一致。而MongoDB在这点上做的还是不错的,MongoDB支持强一致性或最终一致性(弱一致性),MongoDB的数据一致性也叫可调一致性,什么意思呢?如下图: MongoDB是如何实现事务的ACID? MongoDB的可调一致性,也就是可以自由选择强一致性或最终一致性,如果你的应用场景是前台的方式可以选择强一致性,如果你的应用场景是后台的方式(如报表)可以选择弱一致性。 一致性 上面我们讲到了通过将数据冗余存储到不同的节点来保证数据安全和减轻负载,下面我们来看看这样做引发的一个问题:保证数据在多个节点间的一致性是非常困难的。在实际应用中我们会遇到很多困难,同步节点可能会故障,甚至会无法恢复,网络可能会有延迟或者丢包,网络原因导致集群中的机器被分隔成两个不能互通的子域等等。在NoSQL中,通常有两个层次的一致性:第一种是强一致性,既集群中的所有机器状态同步保持一致。第二种是最终一致性,既可以允许短暂的数据不一致,但数据最终会保持一致。我们先来讲一下,在分布式集群中,为什么最终一致性通常是更合理的选择,然后再来讨论两种一致性的具体实现结节。 关于CAP理论 为什么我们会考虑削弱数据的一致性呢?其实这背后有一个关于分布式系统的理论依据。这个理论最早被Eric Brewer提出,称为CAP理论,尔后Gilbert和Lynch对CAP进行了理论证明。这一理论首先把分布式系统中的三个特性进行了如下归纳: 一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。 可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。 分区容忍性(P):集群中的某些节点在无法联系后,集群整体是否还能继续进行服务。 而CAP理论就是说在分布式存储系统中,最多只能实现上面的两点。而由于当前的网络硬件肯定会出现延迟丢包等问题,所以分区容忍性是我们必须需要实现的。所以我们只能在一致性和可用性之间进行权衡,没有NoSQL系统能同时保证这三点。 要保证数据强一致性,最简单的方法是令写操作在所有数据节点上都执行成功才能返回成功,也就是同步概念。而这时如果某个结点出现故障,那么写操作就成功不了了,需要一直等到这个节点恢复。也就是说,如果要保证强一致性,那么就无法提供7×24的高可用性。 而要保证可用性的话,就意味着节点在响应请求时,不用完全考虑整个集群中的数据是否一致。只需要以自己当前的状态进行请求响应。由于并不保证写操作在所有节点都写成功,这可能会导致各个节点的数据状态不一致。 CAP理论导致了最终一致性和强一致性两种选择。当然,事实上还有其它的选择,比如在Yahoo的PNUTS中,采用的就是松散的一致性和弱可用性结合的方法。但是我们讨论的NoSQL系统没有类似的实现,所以我们在后续不会对其进行讨论。 强一致性 强一致性的保证,要求所有数据节点对同一个key值在同一时刻有同样的value值。虽然实际上可能某些节点存储的值是不一样的,但是作为一个整体,当客户端发起对某个key的数据请求时,整个集群对这个key对应的数据会达成一致。下面就举例说明这种一致性是如何实现的。 假设在我们的集群中,一个数据会被备份到N个结点。这N个节点中的某一个可能会扮演协调器的作用。它会保证每一个数据写操作会在成功同步到W个节点后才向客户端返回成功。而当客户端读取数据时,需要至少R个节点返回同样的数据才能返回读操作成功。而NWR之间必须要满足下面关系:R+W>N 下面举个实在的例子。比如我们设定N=3(数据会备份到A、B、C三个结点)。比如值 employee30:salary 当前的值是20000,我们想将其修改为30000。我们设定W=2,下面我们会对A、B、C三个节点发起写操作(employee30:salary, 30000),当A、B两个节点返回写成功后,协调器就会返回给客户端说写成功了。至于节点C,我们可以假设它从来没有收到这个写请求,他保存的依然是20000那个值。之后,当一个协调器执行一个对employee30:salary的读操作时,他还是会发三个请求给A、B、C三个节点: 如果设定R=1,那么当C节点先返回了20000这个值时,那我们客户端实际得到了一个错误的值。 如果设定R=2,则当协调器收到20000和30000两个值时,它会发现数据不太正确,并且会在收到第三个节点的30000的值后判断20000这个值是错误的。 所以如果要保证强一致性,在上面的应用场景中,我们需要设定R=2,W=2 如果写操作不能收到W个节点的成功返回,或者写操作不能得到R个一致的结果。那么协调器可能会在某个设定的过期时间之后向客户端返回操作失败,或者是等到系统慢慢调整到一致。这可能就导致系统暂时处于不可用状态。 对于R和W的不同设定,会导致系统在进行不同操作时需要不同数量的机器节点可用。比如你设定在所有备份节点上都写入才算写成功,既W=N,那么只要有一个备份节点故障,写操作就失败了。一般设定是R+W = N+1,这是保证强一致性的最小设定了。一些强一致性的系统设定W=N,R=1,这样就根本不用考虑各个节点数据可能不一致的情况了。 HBase是借助其底层的HDFS来实现其数据冗余备份的。HDFS采用的就是强一致性保证。在数据没有完全同步到N个节点前,写操作是不会返回成功的。也就是说它的W=N,而读操作只需要读到一个值即可,也就是说它R=1。为了不至于让写操作太慢,对多个节点的写操作是并发异步进行的。在直到所有的节点都收到了新的数据后,会自动执行一个swap操作将新数据写入。这个操作是原子性和一致性的。保证了数据在所有节点有一致的值。 最终一致性 像Voldemort,Cassandra和Riak这些类Dynamo的系统,通常都允许用户按需要设置N,R,W三个值,即使是设置成W+R<= N也是可以的。也就是说他允许用户在强一致性和最终一致性之间自由选择。而在用户选择了最终一致性,或者是W 3)MongoDB对隔离性(isolation)的支持 在关系型数据库中,SQL2定义了四种隔离级别,分别是READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ和SERIALIZABLE。但是很少有数据库厂商遵循这些标准,比如Oracle数据库就不支持READ UNCOMMITTED和REPEATABLE READ隔离级别。而MySQL支持这全部4种隔离级别。每一种级别都规定了一个事务中所做的修改,哪些在事务内核事务外是可见的,哪些是不可见的。为了尽可能减少事务间的影响,事务隔离级别越高安全性越好但是并发就越差;事务隔离级别越低,事务请求的锁越少,或者保持锁的时间就越短,这也就是为什么绝大多数数据库系统默认的事务隔离级别是RC。 下图展示了几家不同的数据库厂商的不同事物隔离级别。 MongoDB是如何实现事务的ACID? MongoDB在3.2之前使用的是“读未提交”,这种情况下会出现“脏读”。但在MongoDB 3.2开始已经调整为“读已提交”。 下面说说每种隔离级别带来的问题: READ-UNCOMMITTED(读尚未提交的数据) 在这个级别,一个事务的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为“脏读(dirty read)”。这个级别会导致很多问题,从性能上来说,READ UNCOMMITTED不会比其他的级别好太多,但却缺乏其他级别的很多好处,除非真的有非常必要的理由,在实际应用中一般很少使用。 READ-COMMITTED(读已提交的数据) 在这个级别,能满足前面提到的隔离性的简单定义:一个事务开始时,只能“看见”已经提交的事务所做的修改。换句话说,一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。这个级别有时候也叫“不可重复读(non-repeatable read)”,因为两次执行同样的查询,可能会得到不一样的结果。 REPEATABLE-READ(可重复读) 在这个级别,保证了在同一个事务中多次读取统一记录的结果是一致的。MySQL默认使用这个级别。InnoDB和XtraDB存储引擎通过多版本并发控制MVCC(multiversion concurrency control)解决了“幻读”和“不可重复读”的问题。通过前面的学习我们知道RR级别总是读取事务开始那一刻的快照信息,也就是说这些数据数据库当前状态,这在一些对于数据的时效特别敏感的业务中,就很可能会出问题。 SERIALIZABLE(串行化) 在这个级别,它通过强制事务串行执行,避免了前面说的一系列问题。简单来说,SERIALIZABLE会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题。实际应用中也很少在本地事务中使用SERIALIABLE隔离级别,主要应用在InnoDB存储引擎的分布式事务中。 4)MongoDB对持久性(durability)的支持 对于数据持久性来说,在传统数据库中(单机)的表现为服务器任何时候发生宕机都不需要担心数据丢失的问题,因为有方式可以把数据永久保存起来了。一般都是通过日志来保证数据的持久性。通过下图来看一下传统数据库跟MongoDB对于数据持久性各自所使用的方式。 MongoDB是如何实现事务的ACID? 从上图可以看出,MongoDB同样是使用数据进来先写日志(日志刷盘的速度是非常快)然后在写入到数据库中的这种方式来保证数据的持久性,如果出现服务器宕机,当启动服务器时会从日志中读取数据。不同的是传统数据库这种方式叫做“WAL” Write-Ahead Logging(预写日志系统),而MongoDB叫做“journal”。此外MongoDB在数据持久性上这点可能做的更好,MongoDB的复制默认节点就是三节点以上的复制集群,当数据到达主节点之后会马上同步到从节点上去。

景凌凯 2019-12-02 02:05:12 0 浏览量 回答数 0

回答

同步两个SQLServer数据库 如何同步两个sqlserver数据库的内容?程序代码可以有版本管理cvs进行同步管理,可是数据库同步就非常麻烦,只能自己改了一个后再去改另一个,如果忘记了更改另一个经常造成两个数据库的结构或内容上不一致.各位有什么好的方法吗? 一、分发与复制 用强制订阅实现数据库同步操作. 大量和批量的数据可以用数据库的同步机制处理: // 说明: 为方便操作,所有操作均在发布服务器(分发服务器)上操作,并使用推模式 在客户机器使用强制订阅方式。 二、测试通过 1:环境 服务器环境: 机器名称: zehuadb 操作系统:windows 2000 server 数据库版本:sql 2000 server 个人版 客户端 机器名称:zlp 操作系统:windows 2000 server 数据库版本:sql 2000 server 个人版 2:建用户帐号 在服务器端建立域用户帐号 我的电脑管理->本地用户和组->用户->建立 username:zlp userpwd:zlp 3:重新启动服务器mssqlserver 我的电脑->控制面版->管理工具->服务->mssqlserver 服务 (更改为:域用户帐号,我们新建的zlp用户 .\zlp,密码:zlp) 4:安装分发服务器 a:配置分发服务器 工具->复制->配置发布、订阅服务器和分发->下一步->下一步(所有的均采用默认配置) b:配置发布服务器 工具->复制->创建和管理发布->选择要发布的数据库(sz)->下一步->快照发布->下一步->选择要发布的内容->下一步->下一步->下一步->完成 c:强制配置订阅服务器(推模式,拉模式与此雷同) 工具->复制->配置发布、订阅服务器和分发->订阅服务器->新建->sql server数据库->输入客户端服务器名称(zlp)->使用sql server 身份验证(sa,空密码)->确定->应用->确定 d:初始化订阅 复制监视器->发布服务器(zehuadb)->双击订阅->强制新建->下一步->选择启用的订阅服务器->zlp->下一步->下一步->下一步->下一步->完成 5:测试配置是否成功 复制监视器->发布衿?zehuadb)->双击sz:sz->点状态->点立即运行代理程序 查看: 复制监视器->发布服务器(zehuadb)->sz:sz->选择zlp:sz(类型强制)->鼠标右键->启动同步处理 如果没有错误标志(红色叉),恭喜您配置成功 6:测试数据 在服务器执行: 选择一个表,执行如下sql:        insert into wq_newsgroup_s select '测试成功',5 复制监视器->发布服务器(zehuadb)->sz:sz->快照->启动代理程序 ->zlp:sz(强制)->启动同步处理 去查看同步的 wq_newsgroup_s 是否插入了一条新的记录 测试完毕,通过。 7:修改数据库的同步时间,一般选择夜晚执行数据库同步处理 (具体操作略) :d /* 注意说明: 服务器一端不能以(local)进行数据的发布与分发,需要先删除注册,然后新建注册本地计算机名称 卸载方式:工具->复制->禁止发布->是在"zehuadb"上静止发布,卸载所有的数据库同步配置服务器 注意:发布服务器、分发服务器中的sqlserveragent服务必须启动 采用推模式: "d:\microsoft sql server\mssql\repldata\unc" 目录文件可以不设置共享 拉模式:则需要共享~! */ 少量数据库同步可以采用触发器实现,同步单表即可。 三、配置过程中可能出现的问题 在sql server 2000里设置和使用数据库复制之前,应先检查相关的几台sql server服务器下面几点是否满足: 1、mssqlserver和sqlserveragent服务是否是以域用户身份启动并运行的(.\administrator用户也是可以的) 如果登录用的是本地系统帐户local,将不具备网络功能,会产生以下错误: 进程未能连接到distributor '@server name' (如果您的服务器已经用了sql server全文检索服务, 请不要修改mssqlserver和sqlserveragent服务的local启动。 会照成全文检索服务不能用。请换另外一台机器来做sql server 2000里复制中的分发服务器。) 修改服务启动的登录用户,需要重新启动mssqlserver和sqlserveragent服务才能生效。 2、检查相关的几台sql server服务器是否改过名称(需要srvid=0的本地机器上srvname和datasource一样) 在查询分析器里执行: use master select srvid,srvname,datasource from sysservers 如果没有srvid=0或者srvid=0(也就是本机器)但srvname和datasource不一样, 需要按如下方法修改: use master go -- 设置两个变量 declare @serverproperty_servername  varchar(100), @servername    varchar(100) -- 取得windows nt 服务器和与指定的 sql server 实例关联的实例信息 select @serverproperty_servername = convert(varchar(100), serverproperty('servername')) -- 返回运行 microsoft sql server 的本地服务器名称 select @servername = convert(varchar(100), @@servername) -- 显示获取的这两个参数 select @serverproperty_servername,@servername --如果@serverproperty_servername和@servername不同(因为你改过计算机名字),再运行下面的 --删除错误的服务器名 exec sp_dropserver @server=@servername --添加正确的服务器名 exec sp_addserver @server=@serverproperty_servername, @local='local' 修改这项参数,需要重新启动mssqlserver和sqlserveragent服务才能生效。 这样一来就不会在创建复制的过程中出现18482、18483错误了。 3、检查sql server企业管理器里面相关的几台sql server注册名是否和上面第二点里介绍的srvname一样 不能用ip地址的注册名。 (我们可以删掉ip地址的注册,新建以sql server管理员级别的用户注册的服务器名) 这样一来就不会在创建复制的过程中出现14010、20084、18456、18482、18483错误了。 4、检查相关的几台sql server服务器网络是否能够正常访问 如果ping主机ip地址可以,但ping主机名不通的时候,需要在 winnt\system32\drivers\etc\hosts   (win2000) windows\system32\drivers\etc\hosts (win2003) 文件里写入数据库服务器ip地址和主机名的对应关系。 例如: 127.0.0.1       localhost 192.168.0.35    oracledb    oracledb 192.168.0.65    fengyu02    fengyu02 202.84.10.193   bj_db       bj_db 或者在sql server客户端网络实用工具里建立别名,例如: 5、系统需要的扩展存储过程是否存在(如果不存在,需要恢复): sp_addextendedproc 'xp_regenumvalues',@dllname ='xpstar.dll' go sp_addextendedproc 'xp_regdeletevalue',@dllname ='xpstar.dll' go sp_addextendedproc 'xp_regdeletekey',@dllname ='xpstar.dll' go sp_addextendedproc xp_cmdshell ,@dllname ='xplog70.dll'  接下来就可以用sql server企业管理器里[复制]-> 右键选择 ->[配置发布、订阅服务器和分发]的图形界面来配置数据库复制了。 下面是按顺序列出配置复制的步骤: 1、建立发布和分发服务器 [欢迎使用配置发布和分发向导]->[选择分发服务器]->[使"@servername"成为它自己的分发服务器,sql server将创建分发数据库和日志] ->[制定快照文件夹]-> [自定义配置] -> [否,使用下列的默认配置] -> [完成] 上述步骤完成后, 会在当前"@servername" sql server数据库里建立了一个distribion库和 一个distributor_admin管理员级别的用户(我们可以任意修改密码)。 服务器上新增加了四个作业: [ 代理程序历史记录清除: distribution ] [ 分发清除: distribution ] [ 复制代理程序检查 ] [ 重新初始化存在数据验证失败的订阅 ] sql server企业管理器里多了一个复制监视器, 当前的这台机器就可以发布、分发、订阅了。 我们再次在sql server企业管理器里[复制]-> 右键选择 ->[配置发布、订阅服务器和分发] 我们可以在 [发布服务器和分发服务器的属性] 窗口-> [发布服务器] -> [新增]   -> [确定] -> [发布数据库] -> [事务]/[合并] -> [确定]  -> [订阅服务器] -> [新增]  -> [确定] 把网络上的其它sql server服务器添加成为发布或者订阅服务器. 新增一台发布服务器的选项: 我这里新建立的jin001发布服务器是用管理员级别的数据库用户test连接的, 到发布服务器的管理链接要输入密码的可选框, 默认的是选中的, 在新建的jin001发布服务器上建立和分发服务器fengyu/fengyu的链接的时需要输入distributor_admin用户的密码。到发布服务器的管理链接要输入密码的可选框,也可以不选,也就是不需要密码来建立发布到分发服务器的链接(这当然欠缺安全,在测试环境下可以使用)。 2、新建立的网络上另一台发布服务器(例如jin001)选择分发服务器 [欢迎使用配置发布和分发向导]->[选择分发服务器] -> 使用下列服务器(选定的服务器必须已配置为分发服务器) -> [选定服务器](例如fengyu/fengyu) -> [下一步] -> [输入分发服务器(例如fengyu/fengyu)的distributor_admin用户的密码两次] -> [下一步] -> [自定义配置] -> [否,使用下列的默认配置] -> [下一步] -> [完成] -> [确定] 建立一个数据库复制发布的过程: [复制] -> [发布内容] -> 右键选择 -> [新建发布] -> [下一步] -> [选择发布数据库] -> [选中一个待发布的数据库] -> [下一步] -> [选择发布类型] -> [事务发布]/[合并发布] -> [下一步] -> [指定订阅服务器的类型] -> [运行sql server 2000的服务器] -> [下一步] -> [指定项目] -> [在事务发布中只可以发布带主键的表] -> [选中一个有主键的待发布的表] ->[在合并发布中会给表增加唯一性索引和 rowguidcol 属性的唯一标识符字段[rowguid],默认值是newid()] (添加新列将: 导致不带列列表的 insert 语句失败,增加表的大小,增加生成第一个快照所要求的时间) ->[选中一个待发布的表] -> [下一步] -> [选择发布名称和描述] -> -> [下一步] -> [自定义发布的属性] -> [否,根据指定方式创建发布] -> [下一步] -> [完成] -> [关闭] 发布属性里有很多有用的选项:设定订阅到期(例如24小时) 设定发布表的项目属性: 常规窗口可以指定发布目的表的名称,可以跟原来的表名称不一样。 下图是命令和快照窗口的栏目 ( sql server 数据库复制技术实际上是用insert,update,delete操作在订阅服务器上重做发布服务器上的事务操作 看文档资料需要把发布数据库设成完全恢复模式,事务才不会丢失 但我自己在测试中发现发布数据库是简单恢复模式下,每10秒生成一些大事务,10分钟后再收缩数据库日志, 这期间发布和订阅服务器上的作业都暂停,暂停恢复后并没有丢失任何事务更改 ) 发布表可以做数据筛选,例如只选择表里面的部分列: 例如只选择表里某些符合条件的记录, 我们可以手工编写筛选的sql语句: 发布表的订阅选项,并可以建立强制订阅: 成功建立了发布以后,发布服务器上新增加了一个作业: [ 失效订阅清除 ] 分发服务器上新增加了两个作业: [ jin001-dack-dack-5 ] 类型[ repl快照 ] [ jin001-dack-3 ]      类型[ repl日志读取器 ] 上面蓝色字的名称会根据发布服务器名,发布名及第几次发布而使用不同的编号 repl快照作业是sql server复制的前提条件,它会先把发布的表结构,数据,索引,约束等生成到发布服务器的os目录下文件 (当有订阅的时候才会生成, 当订阅请求初始化或者按照某个时间表调度生成) repl日志读取器在事务复制的时候是一直处于运行状态。(在合并复制的时候可以根据调度的时间表来运行) 建立一个数据库复制订阅的过程: [复制] -> [订阅] -> 右键选择 -> [新建请求订阅] -> [下一步] -> [查找发布] -> [查看已注册服务器所做的发布] -> [下一步] -> [选择发布] -> [选中已经建立发布服务器上的数据库发布名] -> [下一步] -> [指定同步代理程序登录] -> [当代理程序连接到代理服务器时:使用sql server身份验证] (输入发布服务器上distributor_admin用户名和密码) -> [下一步] -> [选择目的数据库] -> [选择在其中创建订阅的数据库名]/[也可以新建一个库名] -> [下一步] -> [允许匿名订阅] -> [是,生成匿名订阅] -> [下一步] -> [初始化订阅] -> [是,初始化架构和数据] -> [下一步] -> [快照传送] -> [使用该发布的默认快照文件夹中的快照文件] (订阅服务器要能访问发布服务器的repldata文件夹,如果有问题,可以手工设置网络共享及共享权限) -> [下一步] -> [快照传送] -> [使用该发布的默认快照文件夹中的快照文件] -> [下一步] -> [设置分发代理程序调度] -> [使用下列调度] -> [更改] -> [例如每五分钟调度一次] -> [下一步] -> [启动要求的服务] -> [该订阅要求在发布服务器上运行sqlserveragent服务] -> [下一步] -> [完成] -> [确定] 成功建立了订阅后,订阅服务器上新增加了一个类别是[repl-分发]作业(合并复制的时候类别是[repl-合并]) 它会按照我们给的时间调度表运行数据库同步复制的作业。 3、sql server复制配置好后, 可能出现异常情况的实验日志: 1.发布服务器断网,sql server服务关闭,重启动,关机的时候,对已经设置好的复制没有多大影响 中断期间,分发和订阅都接收到没有复制的事务信息 2.分发服务器断网,sql server服务关闭,重启动,关机的时候,对已经设置好的复制有一些影响 中断期间,发布服务器的事务排队堆积起来 (如果设置了较长时间才删除过期订阅的选项, 繁忙发布数据库的事务日志可能会较快速膨胀), 订阅服务器会因为访问不到发布服务器,反复重试 我们可以设置重试次数和重试的时间间隔(最大的重试次数是9999, 如果每分钟重试一次,可以支持约6.9天不出错) 分发服务器sql server服务启动,网络接通以后,发布服务器上的堆积作业将按时间顺序作用到订阅机器上: 会需要一个比较长的时间(实际上是生成所有事务的insert,update,delete语句,在订阅服务器上去执行) 我们在普通的pc机上实验的58个事务100228个命令执行花了7分28秒. 3.订阅服务器断网,sql server服务关闭,重启动,关机的时候,对已经设置好的复制影响比较大,可能需要重新初试化 我们实验环境(订阅服务器)从18:46分意外停机以, 第二天8:40分重启动后, 已经设好的复制在8:40分以后又开始正常运行了, 发布服务器上的堆积作业将按时间顺序作用到订阅机器上, 但复制管理器里出现快照的错误提示, 快照可能需要重新初试化,复制可能需要重新启动.(我们实验环境的机器并没有进行快照初试化,复制仍然是成功运行的) 4、删除已经建好的发布和定阅可以直接用delete删除按钮 我们最好总是按先删定阅,再删发布,最后禁用发布的顺序来操作。 如果要彻底删去sql server上面的复制设置, 可以这样操作: [复制] -> 右键选择 [禁用发布] -> [欢迎使用禁用发布和分发向导] -> [下一步] -> [禁用发布] -> [要在"@servername"上禁用发布] -> [下一步] -> [完成禁用发布和分发向导] -> [完成] 我们也可以用t-sql命令来完成复制中发布及订阅的创建和删除, 选中已经设好的发布和订阅, 按属标右键可以[生成sql脚本]。(这里就不详细讲了, 后面推荐的网站内有比较详细的内容) 当你试图删除或者变更一个table时,出现以下错误 server: msg 3724, level 16, state 2, line 1 cannot drop the table 'object_name' because it is being used for replication. 比较典型的情况是该table曾经用于复制,但是后来又删除了复制。 处理办法: select * from sysobjects where replinfo >'0' sp_configure 'allow updates', 1 go reconfigure with override go begin transaction update sysobjects set replinfo = '0' where replinfo >'0' commit transaction go rollback transaction go sp_configure 'allow updates', 0 go reconfigure with override go 答案来源网络,供参考,希望对您有帮助

问问小秘 2019-12-02 03:02:42 0 浏览量 回答数 0

问题

为什么要分库分表(设计高并发系统的时候,数据库层面该如何设计)?【Java问答】41期

剑曼红尘 2020-06-19 13:47:21 0 浏览量 回答数 0

回答

如果你在浏览器中打开页面A,如果抓包的时候看到有http请求其他的url(text/html),很可能就是ajax请求,一般用来获取动态数据,在页面中显示。发出ajax请求的时候http请求头部一般有这个字段: X-Requested-With: XMLHttpRequest, 不过不一定都有的哦!所谓python模拟ajax请求,实际上就是用python发起http请求,发送这个http请求所需要的数据可以通过抓包获取,构造一个一样的数据发送过去。Ajax的的全称是异步javascriptand XML,说的是用javascript调用浏览器后台线程来进行http请求,这样做的好处是不会阻塞浏览器主线程,主要表现就是用户可以继续操作页面,比如鼠标复制页面中的文字等。总之不要纠结Ajax,它其实就是在浏览器后台线程进行http请求而已。 =========================== 回到python上,假如浏览器 打开页面A的时候,Ajax请求B页面,并把请求到的数据放到A页面上显示,那么在python中你可以构造两个这样的http请求: 1、先请求A 2、按照之前抓包的看到的数据,构造http请求(一般来说无法就是POST或者GET),可以选择urllib2库来进行http请求 ######如果ajax请求的是原来的页面A,没有表单,只有一个按钮,那么这个请求还可以怎么模拟###### 可以直接post数据吧.可以参考http://www.oschina.net/code/snippet_4362_9802###### @西门飞,感谢你的耐心解答,根据你的解答,我解决了我的问题。但是又出现了一个新的问题: 我还想要抓取个人资料,用查看元素的方法发现想要的数据是XHR产生的: 如果用之前的urllib2.Request()发送请求,就会报错HTTPError:HTTPError405:/profile.do 我想问这个XHR动态产生的数据要怎么得到呢? ######这个数据有可能是之前的某个ajax获取到的或者是js加密生成的,js加密生产的话得去分析js这个比较麻烦需要耐心,不过也有捷径可以调用真实的浏览器来获取,比如selenium,phantomjs

优选2 2020-06-09 10:59:52 0 浏览量 回答数 0

回答

如果你在浏览器中打开页面A ,如果抓包的时候看到有http请求其他的url(text/html) ,很可能就是ajax请求,一般用来获取动态数据,在页面中显示。发出ajax请求的时候 http请求头部一般有这个字段:X-Requested-With:XMLHttpRequest ,不过不一定都有的哦!所谓python模拟ajax请求,实际上就是用python发起http请求,发送这个http请求所需要的数据可以通过抓包获取,构造一个一样的数据发送过去。Ajax的的全称是异步javascript and  XML,说的是用javascript调用浏览器后台线程来进行http请求,这样做的好处是不会阻塞浏览器主线程,主要表现就是用户可以继续操作页面,比如鼠标复制页面中的文字等。总之不要纠结Ajax, 它其实就是在浏览器后台线程进行http请求而已。 =========================== 回到python上, 假如浏览器打开页面A的时候,Ajax请求B页面,并把请求到的数据放到A页面上显示,那么在python中你可以构造两个这样的http请求: 1、先请求A 2、按照之前抓包的看到的数据,构造http请求(一般来说无法就是POST 或者GET),可以选择 urllib2 库 来进行http请求 ######如果ajax请求的是原来的页面A,没有表单,只有一个按钮,那么这个请求还可以怎么模拟###### 可以直接post数据吧.可以参考http://www.oschina.net/code/snippet_4362_9802###### @西门飞 ,感谢你的耐心解答,根据你的解答,我解决了我的问题。但是又出现了一个新的问题: 我还想要抓取个人资料,用查看元素的方法发现想要的数据是 XHR产生的: 如果用之前的urllib2.Request()发送请求,就会报错 HTTPError: HTTP Error 405: /profile.do 我想问这个XHR动态产生的数据要怎么得到呢? ######这个数据有可能是之前的某个ajax获取到的 或者是js加密生成的,js加密生产的话 得去分析js 这个比较麻烦 需要耐心,不过也有捷径 可以调用真实的浏览器来获取,比如selenium,phantomjs

爱吃鱼的程序员 2020-05-30 16:58:55 0 浏览量 回答数 0

回答

Android平台进行数据存储的五大方式,分别如下: 1 使用SharedPreferences存储数据 2 文件存储数据 3 SQLite数据库存储数据 4 使用ContentProvider存储数据 5 网络存储数据 下面详细讲解这五种方式的特点 第一种: 使用SharedPreferences存储数据 适用范围:保存少量的数据,且这些数据的格式非常简单:字符串型、基本类型的值。比如应用程序的各种配置信息(如是否打开音效、是否使用震动效果、小游戏的玩家积分等),解锁口 令密码等 核心原理:保存基于XML文件存储的key-value键值对数据,通常用来存储一些简单的配置信息。通过DDMS的File Explorer面板,展开文件浏览树,很明显SharedPreferences数据总是存储在/data/data/<package name>/shared_prefs目录下。SharedPreferences对象本身只能获取数据而不支持存储和修改,存储修改是通过SharedPreferences.edit()获取的内部接口Editor对象实现。 SharedPreferences本身是一 个接口,程序无法直接创建SharedPreferences实例,只能通过Context提供的getSharedPreferences(String name, int mode)方法来获取SharedPreferences实例,该方法中name表示要操作的xml文件名,第二个参数具体如下: Context.MODE_PRIVATE: 指定该SharedPreferences数据只能被本应用程序读、写。 Context.MODE_WORLD_READABLE: 指定该SharedPreferences数据能被其他应用程序读,但不能写。 Context.MODE_WORLD_WRITEABLE: 指定该SharedPreferences数据能被其他应用程序读,写 Editor有如下主要重要方法: SharedPreferences.Editor clear():清空SharedPreferences里所有数据 SharedPreferences.Editor putXxx(String key , xxx value): 向SharedPreferences存入指定key对应的数据,其中xxx 可以是boolean,float,int等各种基本类型据 SharedPreferences.Editor remove(): 删除SharedPreferences中指定key对应的数据项 boolean commit(): 当Editor编辑完成后,使用该方法提交修改 实际案例:运行界面如下 这里只提供了两个按钮和一个输入文本框,布局简单,故在此不给出界面布局文件了,程序核心代码如下: 、class ViewOcl implements View.OnClickListener{ @Override public void onClick(View v) { switch(v.getId()){ case R.id.btnSet: //步骤1:获取输入值 String code = txtCode.getText().toString().trim(); //步骤2-1:创建一个SharedPreferences.Editor接口对象,lock表示要写入的XML文件名,MODE_WORLD_WRITEABLE写操作 SharedPreferences.Editor editor = getSharedPreferences("lock", MODE_WORLD_WRITEABLE).edit(); //步骤2-2:将获取过来的值放入文件 editor.putString("code", code); //步骤3:提交 editor.commit(); Toast.makeText(getApplicationContext(), "口令设置成功", Toast.LENGTH_LONG).show(); break; case R.id.btnGet: //步骤1:创建一个SharedPreferences接口对象 SharedPreferences read = getSharedPreferences("lock", MODE_WORLD_READABLE); //步骤2:获取文件中的值 String value = read.getString("code", ""); Toast.makeText(getApplicationContext(), "口令为:"+value, Toast.LENGTH_LONG).show(); break; } } } 、读写其他应用的SharedPreferences: 步骤如下 1、在创建SharedPreferences时,指定MODE_WORLD_READABLE模式,表明该SharedPreferences数据可以被其他程序读取 2、创建其他应用程序对应的Context: Context pvCount = createPackageContext("com.tony.app", Context.CONTEXT_IGNORE_SECURITY);这里的com.tony.app就是其他程序的包名 3、使用其他程序的Context获取对应的SharedPreferences SharedPreferences read = pvCount.getSharedPreferences("lock", Context.MODE_WORLD_READABLE); 4、如果是写入数据,使用Editor接口即可,所有其他操作均和前面一致。 SharedPreferences对象与SQLite数据库相比,免去了创建数据库,创建表,写SQL语句等诸多操作,相对而言更加方便,简洁。但是SharedPreferences也有其自身缺陷,比如其职能存储boolean,int,float,long和String五种简单的数据类型,比如其无法进行条件查询等。所以不论SharedPreferences的数据存储操作是如何简单,它也只能是存储方式的一种补充,而无法完全替代如SQLite数据库这样的其他数据存储方式。 第二种: 文件存储数据 核心原理: Context提供了两个方法来打开数据文件里的文件IO流 FileInputStream openFileInput(String name); FileOutputStream(String name , int mode),这两个方法第一个参数 用于指定文件名,第二个参数指定打开文件的模式。具体有以下值可选: MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容,如果想把新写入的内容追加到原文件中。可 以使用Context.MODE_APPEND MODE_APPEND:模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。 MODE_WORLD_READABLE:表示当前文件可以被其他应用读取; MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入。 除此之外,Context还提供了如下几个重要的方法: getDir(String name , int mode):在应用程序的数据文件夹下获取或者创建name对应的子目录 File getFilesDir():获取该应用程序的数据文件夹得绝对路径 String[] fileList():返回该应用数据文件夹的全部文件 public String read() { try { FileInputStream inStream = this.openFileInput("message.txt"); byte[] buffer = new byte[1024]; int hasRead = 0; StringBuilder sb = new StringBuilder(); while ((hasRead = inStream.read(buffer)) != -1) { sb.append(new String(buffer, 0, hasRead)); } inStream.close(); return sb.toString(); } catch (Exception e) { e.printStackTrace(); } return null; } public void write(String msg){ // 步骤1:获取输入值 if(msg == null) return; try { // 步骤2:创建一个FileOutputStream对象,MODE_APPEND追加模式 FileOutputStream fos = openFileOutput("message.txt", MODE_APPEND); // 步骤3:将获取过来的值放入文件 fos.write(msg.getBytes()); // 步骤4:关闭数据流 fos.close(); } catch (Exception e) { e.printStackTrace(); } } openFileOutput()方法的第一参数用于指定文件名称,不能包含路径分隔符“/” ,如果文件不存在,Android 会自动创建它。创建的文件保存在/data/data//files目录,如: /data/data/cn.tony.app/files/message.txt, 下面讲解某些特殊文件读写需要注意的地方: 读写sdcard上的文件 其中读写步骤按如下进行: 1、调用Environment的getExternalStorageState()方法判断手机上是否插了sd卡,且应用程序具有读写SD卡的权限,如下代码将返回true Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) 2、调用Environment.getExternalStorageDirectory()方法来获取外部存储器,也就是SD卡的目录,或者使用"/mnt/sdcard/"目录 3、使用IO流操作SD卡上的文件 注意点:手机应该已插入SD卡,对于模拟器而言,可通过mksdcard命令来创建虚拟存储卡 必须在AndroidManifest.xml上配置读写SD卡的权限 // 文件写操作函数 private void write(String content) { if (Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) { // 如果sdcard存在 File file = new File(Environment.getExternalStorageDirectory() .toString() + File.separator + DIR + File.separator + FILENAME); // 定义File类对象 if (!file.getParentFile().exists()) { // 父文件夹不存在 file.getParentFile().mkdirs(); // 创建文件夹 } PrintStream out = null; // 打印流对象用于输出 try { out = new PrintStream(new FileOutputStream(file, true)); // 追加文件 out.println(content); } catch (Exception e) { e.printStackTrace(); } finally { if (out != null) { out.close(); // 关闭打印流 } } } else { // SDCard不存在,使用Toast提示用户 Toast.makeText(this, "保存失败,SD卡不存在!", Toast.LENGTH_LONG).show(); } } // 文件读操作函数 private String read() { if (Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) { // 如果sdcard存在 File file = new File(Environment.getExternalStorageDirectory() .toString() + File.separator + DIR + File.separator + FILENAME); // 定义File类对象 if (!file.getParentFile().exists()) { // 父文件夹不存在 file.getParentFile().mkdirs(); // 创建文件夹 } Scanner scan = null; // 扫描输入 StringBuilder sb = new StringBuilder(); try { scan = new Scanner(new FileInputStream(file)); // 实例化Scanner while (scan.hasNext()) { // 循环读取 sb.append(scan.next() + "\n"); // 设置文本 } return sb.toString(); } catch (Exception e) { e.printStackTrace(); } finally { if (scan != null) { scan.close(); // 关闭打印流 } } } else { // SDCard不存在,使用Toast提示用户 Toast.makeText(this, "读取失败,SD卡不存在!", Toast.LENGTH_LONG).show(); } return null; } 复制代码 第三种:SQLite存储数据 SQLite是轻量级嵌入式数据库引擎,它支持 SQL 语言,并且只利用很少的内存就有很好的性能。现在的主流移动设备像Android、iPhone等都使用SQLite作为复杂数据的存储引擎,在我们为移动设备开发应用程序时,也许就要使用到SQLite来存储我们大量的数据,所以我们就需要掌握移动设备上的SQLite开发技巧 SQLiteDatabase类为我们提供了很多种方法,上面的代码中基本上囊括了大部分的数据库操作;对于添加、更新和删除来说,我们都可以使用 1 db.executeSQL(String sql); 2 db.executeSQL(String sql, Object[] bindArgs);//sql语句中使用占位符,然后第二个参数是实际的参数集 除了统一的形式之外,他们还有各自的操作方法: 1 db.insert(String table, String nullColumnHack, ContentValues values); 2 db.update(String table, Contentvalues values, String whereClause, String whereArgs); 3 db.delete(String table, String whereClause, String whereArgs);以上三个方法的第一个参数都是表示要操作的表名;insert中的第二个参数表示如果插入的数据每一列都为空的话,需要指定此行中某一列的名称,系统将此列设置为NULL,不至于出现错误;insert中的第三个参数是ContentValues类型的变量,是键值对组成的Map,key代表列名,value代表该列要插入的值;update的第二个参数也很类似,只不过它是更新该字段key为最新的value值,第三个参数whereClause表示WHERE表达式,比如“age > ? and age < ?”等,最后的whereArgs参数是占位符的实际参数值;delete方法的参数也是一样 下面给出demo 数据的添加 1.使用insert方法 复制代码1 ContentValues cv = new ContentValues();//实例化一个ContentValues用来装载待插入的数据2 cv.put("title","you are beautiful");//添加title3 cv.put("weather","sun"); //添加weather4 cv.put("context","xxxx"); //添加context5 String publish = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")6 .format(new Date());7 cv.put("publish ",publish); //添加publish8 db.insert("diary",null,cv);//执行插入操作复制代码2.使用execSQL方式来实现 String sql = "insert into user(username,password) values ('Jack Johnson','iLovePopMuisc');//插入操作的SQL语句db.execSQL(sql);//执行SQL语句数据的删除 同样有2种方式可以实现 String whereClause = "username=?";//删除的条件String[] whereArgs = {"Jack Johnson"};//删除的条件参数db.delete("user",whereClause,whereArgs);//执行删除使用execSQL方式的实现 String sql = "delete from user where username='Jack Johnson'";//删除操作的SQL语句db.execSQL(sql);//执行删除操作数据修改 同上,仍是2种方式 ContentValues cv = new ContentValues();//实例化ContentValuescv.put("password","iHatePopMusic");//添加要更改的字段及内容String whereClause = "username=?";//修改条件String[] whereArgs = {"Jack Johnson"};//修改条件的参数db.update("user",cv,whereClause,whereArgs);//执行修改使用execSQL方式的实现 String sql = "update user set password = 'iHatePopMusic' where username='Jack Johnson'";//修改的SQL语句db.execSQL(sql);//执行修改数据查询 下面来说说查询操作。查询操作相对于上面的几种操作要复杂些,因为我们经常要面对着各种各样的查询条件,所以系统也考虑到这种复杂性,为我们提供了较为丰富的查询形式: 1 db.rawQuery(String sql, String[] selectionArgs); 2 db.query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy); 3 db.query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit); 4 db.query(String distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit); 上面几种都是常用的查询方法,第一种最为简单,将所有的SQL语句都组织到一个字符串中,使用占位符代替实际参数,selectionArgs就是占位符实际参数集; 各参数说明: table:表名称colums:表示要查询的列所有名称集selection:表示WHERE之后的条件语句,可以使用占位符selectionArgs:条件语句的参数数组groupBy:指定分组的列名having:指定分组条件,配合groupBy使用orderBy:y指定排序的列名limit:指定分页参数distinct:指定“true”或“false”表示要不要过滤重复值Cursor:返回值,相当于结果集ResultSet最后,他们同时返回一个Cursor对象,代表数据集的游标,有点类似于JavaSE中的ResultSet。下面是Cursor对象的常用方法: 复制代码 1 c.move(int offset); //以当前位置为参考,移动到指定行 2 c.moveToFirst(); //移动到第一行 3 c.moveToLast(); //移动到最后一行 4 c.moveToPosition(int position); //移动到指定行 5 c.moveToPrevious(); //移动到前一行 6 c.moveToNext(); //移动到下一行 7 c.isFirst(); //是否指向第一条 8 c.isLast(); //是否指向最后一条 9 c.isBeforeFirst(); //是否指向第一条之前 10 c.isAfterLast(); //是否指向最后一条之后 11 c.isNull(int columnIndex); //指定列是否为空(列基数为0) 12 c.isClosed(); //游标是否已关闭 13 c.getCount(); //总数据项数 14 c.getPosition(); //返回当前游标所指向的行数 15 c.getColumnIndex(String columnName);//返回某列名对应的列索引值 16 c.getString(int columnIndex); //返回当前行指定列的值 复制代码实现代码 复制代码String[] params = {12345,123456};Cursor cursor = db.query("user",columns,"ID=?",params,null,null,null);//查询并获得游标if(cursor.moveToFirst()){//判断游标是否为空 for(int i=0;i<cursor.getCount();i++){ cursor.move(i);//移动到指定记录 String username = cursor.getString(cursor.getColumnIndex("username"); String password = cursor.getString(cursor.getColumnIndex("password")); } }复制代码通过rawQuery实现的带参数查询 复制代码Cursor result=db.rawQuery("SELECT ID, name, inventory FROM mytable");//Cursor c = db.rawQuery("s name, inventory FROM mytable where ID=?",new Stirng[]{"123456"}); result.moveToFirst(); while (!result.isAfterLast()) { int id=result.getInt(0); String name=result.getString(1); int inventory=result.getInt(2); // do something useful with these result.moveToNext(); } result.close();复制代码 在上面的代码示例中,已经用到了这几个常用方法中的一些,关于更多的信息,大家可以参考官方文档中的说明。 最后当我们完成了对数据库的操作后,记得调用SQLiteDatabase的close()方法释放数据库连接,否则容易出现SQLiteException。 上面就是SQLite的基本应用,但在实际开发中,为了能够更好的管理和维护数据库,我们会封装一个继承自SQLiteOpenHelper类的数据库操作类,然后以这个类为基础,再封装我们的业务逻辑方法。 这里直接使用案例讲解:下面是案例demo的界面 SQLiteOpenHelper类介绍 SQLiteOpenHelper是SQLiteDatabase的一个帮助类,用来管理数据库的创建和版本的更新。一般是建立一个类继承它,并实现它的onCreate和onUpgrade方法。 方法名 方法描述SQLiteOpenHelper(Context context,String name,SQLiteDatabase.CursorFactory factory,int version) 构造方法,其中 context 程序上下文环境 即:XXXActivity.this; name :数据库名字; factory:游标工厂,默认为null,即为使用默认工厂; version 数据库版本号 onCreate(SQLiteDatabase db) 创建数据库时调用onUpgrade(SQLiteDatabase db,int oldVersion , int newVersion) 版本更新时调用getReadableDatabase() 创建或打开一个只读数据库getWritableDatabase() 创建或打开一个读写数据库首先创建数据库类 复制代码 1 import android.content.Context; 2 import android.database.sqlite.SQLiteDatabase; 3 import android.database.sqlite.SQLiteDatabase.CursorFactory; 4 import android.database.sqlite.SQLiteOpenHelper; 5 6 public class SqliteDBHelper extends SQLiteOpenHelper { 7 8 // 步骤1:设置常数参量 9 private static final String DATABASE_NAME = "diary_db";10 private static final int VERSION = 1;11 private static final String TABLE_NAME = "diary";12 13 // 步骤2:重载构造方法14 public SqliteDBHelper(Context context) {15 super(context, DATABASE_NAME, null, VERSION);16 }17 18 /*19 * 参数介绍:context 程序上下文环境 即:XXXActivity.this 20 * name 数据库名字 21 * factory 接收数据,一般情况为null22 * version 数据库版本号23 */24 public SqliteDBHelper(Context context, String name, CursorFactory factory,25 int version) {26 super(context, name, factory, version);27 }28 //数据库第一次被创建时,onCreate()会被调用29 @Override30 public void onCreate(SQLiteDatabase db) {31 // 步骤3:数据库表的创建32 String strSQL = "create table "33 + TABLE_NAME34 + "(tid integer primary key autoincrement,title varchar(20),weather varchar(10),context text,publish date)";35 //步骤4:使用参数db,创建对象36 db.execSQL(strSQL);37 }38 //数据库版本变化时,会调用onUpgrade()39 @Override40 public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {41 42 }43 }复制代码正如上面所述,数据库第一次创建时onCreate方法会被调用,我们可以执行创建表的语句,当系统发现版本变化之后,会调用onUpgrade方法,我们可以执行修改表结构等语句。 我们需要一个Dao,来封装我们所有的业务方法,代码如下: 复制代码 1 import android.content.Context; 2 import android.database.Cursor; 3 import android.database.sqlite.SQLiteDatabase; 4 5 import com.chinasoft.dbhelper.SqliteDBHelper; 6 7 public class DiaryDao { 8 9 private SqliteDBHelper sqliteDBHelper;10 private SQLiteDatabase db;11 12 // 重写构造方法13 public DiaryDao(Context context) {14 this.sqliteDBHelper = new SqliteDBHelper(context);15 db = sqliteDBHelper.getWritableDatabase();16 }17 18 // 读操作19 public String execQuery(final String strSQL) {20 try {21 System.out.println("strSQL>" + strSQL);22 // Cursor相当于JDBC中的ResultSet23 Cursor cursor = db.rawQuery(strSQL, null);24 // 始终让cursor指向数据库表的第1行记录25 cursor.moveToFirst();26 // 定义一个StringBuffer的对象,用于动态拼接字符串27 StringBuffer sb = new StringBuffer();28 // 循环游标,如果不是最后一项记录29 while (!cursor.isAfterLast()) {30 sb.append(cursor.getInt(0) + "/" + cursor.getString(1) + "/"31 + cursor.getString(2) + "/" + cursor.getString(3) + "/"32 + cursor.getString(4)+"#");33 //cursor游标移动34 cursor.moveToNext();35 }36 db.close();37 return sb.deleteCharAt(sb.length()-1).toString();38 } catch (RuntimeException e) {39 e.printStackTrace();40 return null;41 }42 43 }44 45 // 写操作46 public boolean execOther(final String strSQL) {47 db.beginTransaction(); //开始事务48 try {49 System.out.println("strSQL" + strSQL);50 db.execSQL(strSQL);51 db.setTransactionSuccessful(); //设置事务成功完成 52 db.close();53 return true;54 } catch (RuntimeException e) {55 e.printStackTrace();56 return false;57 }finally { 58 db.endTransaction(); //结束事务 59 } 60 61 }62 }复制代码我们在Dao构造方法中实例化sqliteDBHelper并获取一个SQLiteDatabase对象,作为整个应用的数据库实例;在增删改信息时,我们采用了事务处理,确保数据完整性;最后要注意释放数据库资源db.close(),这一个步骤在我们整个应用关闭时执行,这个环节容易被忘记,所以朋友们要注意。 我们获取数据库实例时使用了getWritableDatabase()方法,也许朋友们会有疑问,在getWritableDatabase()和getReadableDatabase()中,你为什么选择前者作为整个应用的数据库实例呢?在这里我想和大家着重分析一下这一点。 我们来看一下SQLiteOpenHelper中的getReadableDatabase()方法: 复制代码 1 public synchronized SQLiteDatabase getReadableDatabase() { 2 if (mDatabase != null && mDatabase.isOpen()) { 3 // 如果发现mDatabase不为空并且已经打开则直接返回 4 return mDatabase; 5 } 6 7 if (mIsInitializing) { 8 // 如果正在初始化则抛出异常 9 throw new IllegalStateException("getReadableDatabase called recursively"); 10 } 11 12 // 开始实例化数据库mDatabase 13 14 try { 15 // 注意这里是调用了getWritableDatabase()方法 16 return getWritableDatabase(); 17 } catch (SQLiteException e) { 18 if (mName == null) 19 throw e; // Can't open a temp database read-only! 20 Log.e(TAG, "Couldn't open " + mName + " for writing (will try read-only):", e); 21 } 22 23 // 如果无法以可读写模式打开数据库 则以只读方式打开 24 25 SQLiteDatabase db = null; 26 try { 27 mIsInitializing = true; 28 String path = mContext.getDatabasePath(mName).getPath();// 获取数据库路径 29 // 以只读方式打开数据库 30 db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY); 31 if (db.getVersion() != mNewVersion) { 32 throw new SQLiteException("Can't upgrade read-only database from version " + db.getVersion() + " to " 33 + mNewVersion + ": " + path); 34 } 35 36 onOpen(db); 37 Log.w(TAG, "Opened " + mName + " in read-only mode"); 38 mDatabase = db;// 为mDatabase指定新打开的数据库 39 return mDatabase;// 返回打开的数据库 40 } finally { 41 mIsInitializing = false; 42 if (db != null && db != mDatabase) 43 db.close(); 44 } 45 }复制代码在getReadableDatabase()方法中,首先判断是否已存在数据库实例并且是打开状态,如果是,则直接返回该实例,否则试图获取一个可读写模式的数据库实例,如果遇到磁盘空间已满等情况获取失败的话,再以只读模式打开数据库,获取数据库实例并返回,然后为mDatabase赋值为最新打开的数据库实例。既然有可能调用到getWritableDatabase()方法,我们就要看一下了: 复制代码public synchronized SQLiteDatabase getWritableDatabase() { if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) { // 如果mDatabase不为空已打开并且不是只读模式 则返回该实例 return mDatabase; } if (mIsInitializing) { throw new IllegalStateException("getWritableDatabase called recursively"); } // If we have a read-only database open, someone could be using it // (though they shouldn't), which would cause a lock to be held on // the file, and our attempts to open the database read-write would // fail waiting for the file lock. To prevent that, we acquire the // lock on the read-only database, which shuts out other users. boolean success = false; SQLiteDatabase db = null; // 如果mDatabase不为空则加锁 阻止其他的操作 if (mDatabase != null) mDatabase.lock(); try { mIsInitializing = true; if (mName == null) { db = SQLiteDatabase.create(null); } else { // 打开或创建数据库 db = mContext.openOrCreateDatabase(mName, 0, mFactory); } // 获取数据库版本(如果刚创建的数据库,版本为0) int version = db.getVersion(); // 比较版本(我们代码中的版本mNewVersion为1) if (version != mNewVersion) { db.beginTransaction();// 开始事务 try { if (version == 0) { // 执行我们的onCreate方法 onCreate(db); } else { // 如果我们应用升级了mNewVersion为2,而原版本为1则执行onUpgrade方法 onUpgrade(db, version, mNewVersion); } db.setVersion(mNewVersion);// 设置最新版本 db.setTransactionSuccessful();// 设置事务成功 } finally { db.endTransaction();// 结束事务 } } onOpen(db); success = true; return db;// 返回可读写模式的数据库实例 } finally { mIsInitializing = false; if (success) { // 打开成功 if (mDatabase != null) { // 如果mDatabase有值则先关闭 try { mDatabase.close(); } catch (Exception e) { } mDatabase.unlock();// 解锁 } mDatabase = db;// 赋值给mDatabase } else { // 打开失败的情况:解锁、关闭 if (mDatabase != null) mDatabase.unlock(); if (db != null) db.close(); } } }复制代码大家可以看到,几个关键步骤是,首先判断mDatabase如果不为空已打开并不是只读模式则直接返回,否则如果mDatabase不为空则加锁,然后开始打开或创建数据库,比较版本,根据版本号来调用相应的方法,为数据库设置新版本号,最后释放旧的不为空的mDatabase并解锁,把新打开的数据库实例赋予mDatabase,并返回最新实例。 看完上面的过程之后,大家或许就清楚了许多,如果不是在遇到磁盘空间已满等情况,getReadableDatabase()一般都会返回和getWritableDatabase()一样的数据库实例,所以我们在DBManager构造方法中使用getWritableDatabase()获取整个应用所使用的数据库实例是可行的。当然如果你真的担心这种情况会发生,那么你可以先用getWritableDatabase()获取数据实例,如果遇到异常,再试图用getReadableDatabase()获取实例,当然这个时候你获取的实例只能读不能写了 最后,让我们看一下如何使用这些数据操作方法来显示数据,界面核心逻辑代码: 复制代码public class SQLiteActivity extends Activity { public DiaryDao diaryDao; //因为getWritableDatabase内部调用了mContext.openOrCreateDatabase(mName, 0, mFactory); //所以要确保context已初始化,我们可以把实例化Dao的步骤放在Activity的onCreate里 @Override protected void onCreate(Bundle savedInstanceState) { diaryDao = new DiaryDao(SQLiteActivity.this); initDatabase(); } class ViewOcl implements View.OnClickListener { @Override public void onClick(View v) { String strSQL; boolean flag; String message; switch (v.getId()) { case R.id.btnAdd: String title = txtTitle.getText().toString().trim(); String weather = txtWeather.getText().toString().trim();; String context = txtContext.getText().toString().trim();; String publish = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") .format(new Date()); // 动态组件SQL语句 strSQL = "insert into diary values(null,'" + title + "','" + weather + "','" + context + "','" + publish + "')"; flag = diaryDao.execOther(strSQL); //返回信息 message = flag?"添加成功":"添加失败"; Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show(); break; case R.id.btnDelete: strSQL = "delete from diary where tid = 1"; flag = diaryDao.execOther(strSQL); //返回信息 message = flag?"删除成功":"删除失败"; Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show(); break; case R.id.btnQuery: strSQL = "select * from diary order by publish desc"; String data = diaryDao.execQuery(strSQL); Toast.makeText(getApplicationContext(), data, Toast.LENGTH_LONG).show(); break; case R.id.btnUpdate: strSQL = "update diary set title = '测试标题1-1' where tid = 1"; flag = diaryDao.execOther(strSQL); //返回信息 message = flag?"更新成功":"更新失败"; Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show(); break; } } } private void initDatabase() { // 创建数据库对象 SqliteDBHelper sqliteDBHelper = new SqliteDBHelper(SQLiteActivity.this); sqliteDBHelper.getWritableDatabase(); System.out.println("数据库创建成功"); } }复制代码 Android sqlite3数据库管理工具 Android SDK的tools目录下提供了一个sqlite3.exe工具,这是一个简单的sqlite数据库管理工具。开发者可以方便的使用其对sqlite数据库进行命令行的操作。 程序运行生成的.db文件一般位于"/data/data/项目名(包括所处包名)/databases/.db",因此要对数据库文件进行操作需要先找到数据库文件: 1、进入shell 命令 adb shell2、找到数据库文件 cd data/data ls --列出所有项目 cd project_name --进入所需项目名 cd databases ls --列出现寸的数据库文件 3、进入数据库 sqlite3 test_db --进入所需数据库 会出现类似如下字样: SQLite version 3.6.22Enter ".help" for instructionsEnter SQL statements terminated with a ";"sqlite>至此,可对数据库进行sql操作。 4、sqlite常用命令 .databases --产看当前数据库.tables --查看当前数据库中的表.help --sqlite3帮助.schema --各个表的生成语句 原文地址https://www.cnblogs.com/ITtangtang/p/3920916.html

auto_answer 2019-12-02 01:50:21 0 浏览量 回答数 0

回答

触及 multiple inheritance (MI)(多继承)的时候,C++ 社区就会鲜明地分裂为两个基本的阵营。一个阵营认为如果 single inheritance (SI)(单继承)是有好处的,multiple inheritance(多继承)一定更有好处。另一个阵营认为 single inheritance(单继承)有好处,但是多继承引起的麻烦使它得不偿失。在本文中,我们的主要目的是理解在 MI 问题上的这两种看法。   首要的事情之一是要承认当将 MI 引入设计领域时,就有可能从多于一个的 base class(基类)中继承相同的名字(例如,函数,typedef,等等)。这就为歧义性提供了新的时机。例如: class BorrowableItem { // something a library lets you borrowpublic: void checkOut(); // check the item out from the library ..}; class ElectronicGadget {private: bool checkOut() const; // perform self-test, return whether ... // test succeeds}; class MP3Player: // note MI herepublic BorrowableItem, // (some libraries loan MP3 players)public ElectronicGadget{ ... }; // class definition is unimportant MP3Player mp; mp.checkOut(); // ambiguous! which checkOut?    注意这个例子,即使两个函数中只有一个是可访问的,对 checkOut 的调用也是有歧义的。(checkOut 在 BorrowableItem 中是 public(公有)的,但在 ElectronicGadget 中是 private(私有)的。)这与 C++ 解析 overloaded functions(重载函数)调用的规则是一致的:在看到一个函数的是否可访问之前,C++ 首先确定与调用匹配最好的那个函数。只有在确定了 best-match function(最佳匹配函数)之后,才检查可访问性。这目前的情况下,两个 checkOuts 具有相同的匹配程度,所以就不存在最佳匹配。因此永远也不会检查到 ElectronicGadget::checkOut 的可访问性。   为了消除歧义性,你必须指定哪一个 base class(基类)的函数被调用: mp.BorrowableItem::checkOut(); // ah, that checkOut...   当然,你也可以尝试显式调用 ElectronicGadget::checkOut,但这样做会有一个 "you're trying to call a private member function"(你试图调用一个私有成员函数)错误代替歧义性错误。    multiple inheritance(多继承)仅仅意味着从多于一个的 base class(基类)继承,但是在还有 higher-level base classes(更高层次基类)的 hierarchies(继承体系)中出现 MI 也并不罕见。这会导致有时被称为 "deadly MI diamond"(致命的多继承菱形)的后果。 class File { ... };class InputFile: public File { ... };class OutputFile: public File { ... };class IOFile: public InputFile,public OutputFile{ ... };    在一个“在一个 base class(基类)和一个 derived class(派生类)之间有多于一条路径的 inheritance hierarchy(继承体系)”(就像上面在 File 和 IOFile 之间,有通过 InputFile 和 OutputFile 的两条路径)的任何时候,你都必须面对是否需要为每一条路径复制 base class(基类)中的 data members(数据成员)的问题。例如,假设 File class 有一个 data members(数据成员)fileName。IOFile 中应该有这个 field(字段)的多少个拷贝呢?一方面,它从它的每一个 base classes(基类)继承一个拷贝,这就暗示 IOFile 应该有两个 fileName data members(数据成员)。另一方面,简单的逻辑告诉我们一个 IOFile object(对象)应该仅有一个 file name(文件名),所以通过它的两个 base classes(基类)继承来的 fileName field(字段)不应该被复制。   C++ 在这个争议上没有自己的立场。它恰当地支持两种选项,虽然它的缺省方式是执行复制。如果那不是你想要的,你必须让这个 class(类)带有一个 virtual base class(虚拟基类)的数据(也就是 File)。为了做到这一点,你要让从它直接继承的所有的 classes(类)使用 virtual inheritance(虚拟继承): class File { ... };class InputFile: virtual public File { ... };class OutputFile: virtual public File { ... };class IOFile: public InputFile,public OutputFile{ ... };    标准 C++ 库包含一个和此类似的 MI hierarchy(继承体系),只是那个 classes(类)是 class templates(类模板),名字是 basic_ios,basic_istream,basic_ostream 和 basic_iostream,而不是 File,InputFile,OutputFile 和 IOFile。   从正确行为的观点 看,public inheritance(公有继承)应该总是 virtual(虚拟)的。如果这是唯一的观点,规则就变得简单了:你使用 public inheritance(公有继承)的任何时候,都使用 virtual public inheritance(虚拟公有继承)。唉,正确性不是唯一的视角。避免 inherited fields(继承来的字段)复制需要在编译器的一部分做一些 behind-the-scenes legerdemain(幕后的戏法),而结果是从使用 virtual inheritance(虚拟继承)的 classes(类)创建的 objects(对象)通常比不使用 virtual inheritance(虚拟继承)的要大。访问 virtual base classes(虚拟基类)中的 data members(数据成员)也比那些 non-virtual base classes(非虚拟基类)中的要慢。编译器与编译器之间有一些细节不同,但基本的要点很清楚:virtual inheritance costs(虚拟继承要付出成本)。   它也有一些其它方面的成本。支配 initialization of virtual base classes(虚拟基类初始化)的规则比 non-virtual bases(非虚拟基类)的更加复杂而且更不直观。初始化一个 virtual base(虚拟基)的职责由 hierarchy(继承体系)中 most derived class(层次最低的派生类)承担。这个规则中包括的含义:   (1) 从需要 initialization(初始化)的 virtual bases(虚拟基)派生的 classes(类)必须知道它们的 virtual bases(虚拟基),无论它距离那个 bases(基)有多远;   (2) 当一个新的 derived class(派生类)被加入继承体系时,它必须为它的 virtual bases(虚拟基)(包括直接的和间接的)承担 initialization responsibilities(初始化职责)。    我对于 virtual base classes(虚拟基类)(也就是 virtual inheritance(虚拟继承))的建议很简单。首先,除非必需,否则不要使用 virtual bases(虚拟基)。缺省情况下,使用 non-virtual inheritance(非虚拟继承)。第二,如果你必须使用 virtual base classes(虚拟基类),试着避免在其中放置数据。这样你就不必在意它的 initialization(初始化)(以及它的 turns out(清空),assignment(赋值))规则中的一些怪癖。值得一提的是 Java 和 .NET 中的 Interfaces(接口)不允许包含任何数据,它们在很多方面可以和 C++ 中的 virtual base classes(虚拟基类)相比照。   现在我们使用下面的 C++ Interface class(接口类)(参见《C++箴言:最小化文件之间的编译依赖》)来为 persons(人)建模: class IPerson {public: virtual ~IPerson();  virtual std::string name() const = 0; virtual std::string birthDate() const = 0;};    IPerson 的客户只能使用 IPerson 的 pointers(指针)和 references(引用)进行编程,因为 abstract classes(抽象类)不能被实例化。为了创建能被当作 IPerson objects(对象)使用的 objects(对象),IPerson 的客户使用 factory functions(工厂函数)(再次参见 Item 31)instantiate(实例化)从 IPerson 派生的 concrete classes(具体类): // factory function to create a Person object from a unique database ID;// see Item 18 for why the return type isn't a raw pointerstd::tr1::shared_ptr makePerson(DatabaseID personIdentifier); // function to get a database ID from the userDatabaseID askUserForDatabaseID(); DatabaseID id(askUserForDatabaseID());std::tr1::shared_ptr pp(makePerson(id)); // create an object// supporting the// IPerson interface ... // manipulate *pp via// IPerson's member// functions   但是 makePerson 怎样创建它返回的 pointers(指针)所指向的 objects(对象)呢?显然,必须有一些 makePerson 可以实例化的从 IPerson 派生的 concrete class(具体类)。    假设这个 class(类)叫做 CPerson。作为一个 concrete class(具体类),CPerson 必须提供它从 IPerson 继承来的 pure virtual functions(纯虚拟函数)的 implementations(实现)。它可以从头开始写,但利用包含大多数或全部必需品的现有组件更好一些。例如,假设一个老式的 database-specific class(老式的数据库专用类)PersonInfo 提供了 CPerson 所需要的基本要素: class PersonInfo {public: explicit PersonInfo(DatabaseID pid); virtual ~PersonInfo();  virtual const char * theName() const; virtual const char * theBirthDate() const; ... private: virtual const char * valueDelimOpen() const; // see virtual const char * valueDelimClose() const; // below ...};    你可以看出这是一个老式的 class(类),因为 member functions(成员函数)返回 const char*s 而不是 string objects(对象)。尽管如此,如果鞋子合适,为什么不穿呢?这个 class(类)的 member functions(成员函数)的名字暗示结果很可能会非常合适。   你突然发现 PersonInfo 是设计用来帮助以不同的格式打印 database fields(数据库字段)的,每一个字段的值的开始和结尾通过指定的字符串定界。缺省情况下,字段值开始和结尾定界符是方括号,所以字段值 "Ring-tailed Lemur" 很可能被安排成这种格式: [Ring-tailed Lemur]   根据方括号并非满足 PersonInfo 的全体客户的期望的事实,virtual functions(虚拟函数)valueDelimOpen 和 valueDelimClose 允许 derived classes(派生类)指定它们自己的开始和结尾定界字符串。PersonInfo 的 member functions(成员函数)的 implementations(实现)调用这些 virtual functions(虚拟函数)在它们返回的值上加上适当的定界符。作为一个例子使用 PersonInfo::theName,代码如下: const char * PersonInfo::valueDelimOpen() const{ return "["; // default opening delimiter} const char * PersonInfo::valueDelimClose() const{ return "]"; // default closing delimiter} const char * PersonInfo::theName() const{ // reserve buffer for return value; because this is // static, it's automatically initialized to all zeros static char value[Max_Formatted_Field_Value_Length];  // write opening delimiter std::strcpy(value, valueDelimOpen());  append to the string in value this object's name field (being careful to avoid buffer overruns!)  // write closing delimiter std::strcat(value, valueDelimClose());  return value;}    有人可能会质疑 PersonInfo::theName 的陈旧的设计(特别是一个 fixed-size static buffer(固定大小静态缓冲区)的使用,这样的东西发生 overrun(越界)和 threading(线程)问题是比较普遍的——参见《C++箴言:必须返回对象时别返回引用》),但是请把这样的问题放到一边而注意这里:theName 调用 valueDelimOpen 生成它要返回的 string(字符串)的开始定界符,然后它生成名字值本身,然后它调用 valueDelimClose。   因为 valueDelimOpen 和 valueDelimClose 是 virtual functions(虚拟函数),theName 返回的结果不仅依赖于 PersonInfo,也依赖于从 PersonInfo 派生的 classes(类)。    对于 CPerson 的实现者,这是好消息,因为当细读 IPerson documentation(文档)中的 fine print(晦涩的条文)时,你发现 name 和 birthDate 需要返回未经修饰的值,也就是,不允许有定界符。换句话说,如果一个人的名字叫 Homer,对那个人的 name 函数的一次调用应该返回 "Homer",而不是 "[Homer]"。   CPerson 和 PersonInfo 之间的关系是 PersonInfo 碰巧有一些函数使得 CPerson 更容易实现。这就是全部。因而它们的关系就是 is-implemented-in-terms-of,而我们知道有两种方法可以表现这一点:经由 composition(复合)(参见《C++箴言:通过composition模拟“has-a”》)和经由 private inheritance(私有继承)(参见《C++箴言:谨慎使用私有继承》)。《C++箴言:谨慎使用私有继承》 指出 composition(复合)是通常的首选方法,但如果 virtual functions(虚拟函数)要被重定义,inheritance(继承)就是必不可少的。在当前情况下,CPerson 需要重定义 valueDelimOpen 和 valueDelimClose,所以简单的 composition(复合)做不到。最直截了当的解决方案是让 CPerson 从 PersonInfo privately inherit(私有继承),虽然 《C++箴言:谨慎使用私有继承》 说过只要多做一点工作,则 CPerson 也能用 composition(复合)和 inheritance(继承)的组合有效地重定义 PersonInfo 的 virtuals(虚拟函数)。这里,我们用 private inheritance(私有继承)。   但 是 CPerson 还必须实现 IPerson interface(接口),而这被称为 public inheritance(公有继承)。这就引出一个 multiple inheritance(多继承)的合理应用:组合 public inheritance of an interface(一个接口的公有继承)和 private inheritance of an implementation(一个实现的私有继承): class IPerson { // this class specifies thepublic: // interface to be implemented virtual ~IPerson();  virtual std::string name() const = 0; virtual std::string birthDate() const = 0;}; class DatabaseID { ... }; // used below; details are// unimportant class PersonInfo { // this class has functionspublic: // useful in implementing explicit PersonInfo(DatabaseID pid); // the IPerson interface virtual ~PersonInfo();  virtual const char * theName() const; virtual const char * theBirthDate() const;  virtual const char * valueDelimOpen() const; virtual const char * valueDelimClose() const; ...}; class CPerson: public IPerson, private PersonInfo { // note use of MIpublic: explicit CPerson( DatabaseID pid): PersonInfo(pid) {} virtual std::string name() const // implementations { return PersonInfo::theName(); } // of the required // IPerson member virtual std::string birthDate() const // functions { return PersonInfo::theBirthDate(); }private: // redefinitions of const char * valueDelimOpen() const { return ""; } // inherited virtual const char * valueDelimClose() const { return ""; } // delimiter}; // functions   在 UML 中,这个设计看起来像这样:   这个例子证明 MI 既是有用的,也是可理解的。    时至今日,multiple inheritance(多继承)不过是 object-oriented toolbox(面向对象工具箱)里的又一种工具而已,典型情况下,它的使用和理解更加复杂,所以如果你得到一个或多或少等同于一个 MI 设计的 SI 设计,则 SI 设计总是更加可取。如果你能拿出来的仅有的设计包含 MI,你应该更加用心地考虑一下——总会有一些方法使得 SI 也能做到。但同时,MI 有时是最清晰的,最易于维护的,最合理的完成工作的方法。在这种情况下,毫不畏惧地使用它。只是要确保谨慎地使用它。   Things to Remember   ·multiple inheritance(多继承)比 single inheritance(单继承)更复杂。它能导致新的歧义问题和对 virtual inheritance(虚拟继承)的需要。    ·virtual inheritance(虚拟继承)增加了 size(大小)和 speed(速度)成本,以及 initialization(初始化)和 assignment(赋值)的复杂度。当 virtual base classes(虚拟基类)没有数据时它是最适用的。   ·multiple inheritance(多继承)有合理的用途。一种方案涉及组合从一个 Interface class(接口类)的 public inheritance(公有继承)和从一个有助于实现的 class(类)的 private inheritance(私有继承)。 关于虚拟继承的思考虚拟继承在一般的应用中很少用到,所以也往往被忽视,这也主要是因为在C++中,多重继承是不推荐的,而一旦离开了多重继承,虚拟继承就完全失去了存在的必要(因为这样只会降低效率和占用更多的空间,实在是一无是处)。  以下面的一个例子为例:  #include   #include   class CA  {   int k; //为了便于说明后面的内存结构特别添加  public:   void f() {cout << "CA::f" << endl;}  };  class CB : public CA  {  };  class CC : public CA  {  };  class CD : public CB, public CC  {  };  void main()  {   CD d;   d.f();  }  当编译上述代码时,我们会收到如下的错误提示:  error C2385: 'CD::f' is ambiguous  即编译器无法确定你在d.f()中要调用的函数f到底是哪一个。这里可能会让人觉得有些奇怪,命名只定义了一个CA::f,既然大家都派生自CA,那自然就是调用的CA::f,为什么还无法确定呢?  这是因为编译器在进行编译的时候,需要确定子类的函数定义,如CA::f是确定的,那么在编译CB、CC时还需要在编译器的语法树中生成CB::f,CC::f等标识,那么,在编译CD的时候,由于CB、CC都有一个函数f,此时,编译器将试图生成两个CD::f标识,显然这时就要报错了。(当我们不使用CD::f的时候,以上标识都不会生成,所以,如果去掉d.f()一句,程序将顺利通过编译)  要解决这个问题,有两个方法:  1、重载函数f():此时由于我们明确定义了CD::f,编译器检查到CD::f()调用时就无需再像上面一样去逐级生成CD::f标识了;  此时CD的元素结构如下:  --------  |CB(CA)|  |CC(CA)|  --------  故此时的sizeof(CD) = 8;(CB、CC各有一个元素k)  2、使用虚拟继承:虚拟继承又称作共享继承,这种共享其实也是编译期间实现的,当使用虚拟继承时,上面的程序将变成下面的形式:  #include   #include   class CA  {   int k;  public:   void f() {cout << "CA::f" << endl;}  };  class CB : virtual public CA  {  };  class CC : virtual public CA  {  };  class CD : public CB, public CC  {  };  void main()  {   CD d;   d.f();  }  此时,当编译器确定d.f()调用的具体含义时,将生成如下的CD结构:  ----  |CB|  |CC|  |CA|  ----  同时,在CB、CC中都分别包含了一个指向CA的vbptr(virtual base table pointer),其中记录的是从CB、CC的元素到CA的元素之间的偏移量。此时,不会生成各子类的函数f标识,除非子类重载了该函数,从而达到“共享”的目的。  也正因此,此时的sizeof(CD) = 12(两个vbptr + sizoef(int));

a123456678 2019-12-02 01:58:07 0 浏览量 回答数 0

回答

92题 一般来说,建立INDEX有以下益处:提高查询效率;建立唯一索引以保证数据的唯一性;设计INDEX避免排序。 缺点,INDEX的维护有以下开销:叶节点的‘分裂’消耗;INSERT、DELETE和UPDATE操作在INDEX上的维护开销;有存储要求;其他日常维护的消耗:对恢复的影响,重组的影响。 需要建立索引的情况:为了建立分区数据库的PATITION INDEX必须建立; 为了保证数据约束性需要而建立的INDEX必须建立; 为了提高查询效率,则考虑建立(是否建立要考虑相关性能及维护开销); 考虑在使用UNION,DISTINCT,GROUP BY,ORDER BY等字句的列上加索引。 91题 作用:加快查询速度。原则:(1) 如果某属性或属性组经常出现在查询条件中,考虑为该属性或属性组建立索引;(2) 如果某个属性常作为最大值和最小值等聚集函数的参数,考虑为该属性建立索引;(3) 如果某属性经常出现在连接操作的连接条件中,考虑为该属性或属性组建立索引。 90题 快照Snapshot是一个文件系统在特定时间里的镜像,对于在线实时数据备份非常有用。快照对于拥有不能停止的应用或具有常打开文件的文件系统的备份非常重要。对于只能提供一个非常短的备份时间而言,快照能保证系统的完整性。 89题 游标用于定位结果集的行,通过判断全局变量@@FETCH_STATUS可以判断是否到了最后,通常此变量不等于0表示出错或到了最后。 88题 事前触发器运行于触发事件发生之前,而事后触发器运行于触发事件发生之后。通常事前触发器可以获取事件之前和新的字段值。语句级触发器可以在语句执行前或后执行,而行级触发在触发器所影响的每一行触发一次。 87题 MySQL可以使用多个字段同时建立一个索引,叫做联合索引。在联合索引中,如果想要命中索引,需要按照建立索引时的字段顺序挨个使用,否则无法命中索引。具体原因为:MySQL使用索引时需要索引有序,假设现在建立了"name,age,school"的联合索引,那么索引的排序为: 先按照name排序,如果name相同,则按照age排序,如果age的值也相等,则按照school进行排序。因此在建立联合索引的时候应该注意索引列的顺序,一般情况下,将查询需求频繁或者字段选择性高的列放在前面。此外可以根据特例的查询或者表结构进行单独的调整。 86题 建立索引的时候一般要考虑到字段的使用频率,经常作为条件进行查询的字段比较适合。如果需要建立联合索引的话,还需要考虑联合索引中的顺序。此外也要考虑其他方面,比如防止过多的所有对表造成太大的压力。这些都和实际的表结构以及查询方式有关。 85题 存储过程是一组Transact-SQL语句,在一次编译后可以执行多次。因为不必重新编译Transact-SQL语句,所以执行存储过程可以提高性能。触发器是一种特殊类型的存储过程,不由用户直接调用。创建触发器时会对其进行定义,以便在对特定表或列作特定类型的数据修改时执行。 84题 存储过程是用户定义的一系列SQL语句的集合,涉及特定表或其它对象的任务,用户可以调用存储过程,而函数通常是数据库已定义的方法,它接收参数并返回某种类型的值并且不涉及特定用户表。 83题 减少表连接,减少复杂 SQL,拆分成简单SQL。减少排序:非必要不排序,利用索引排序,减少参与排序的记录数。尽量避免 select *。尽量用 join 代替子查询。尽量少使用 or,使用 in 或者 union(union all) 代替。尽量用 union all 代替 union。尽量早的将无用数据过滤:选择更优的索引,先分页再Join…。避免类型转换:索引失效。优先优化高并发的 SQL,而不是执行频率低某些“大”SQL。从全局出发优化,而不是片面调整。尽可能对每一条SQL进行 explain。 82题 如果条件中有or,即使其中有条件带索引也不会使用(要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引)。对于多列索引,不是使用的第一部分,则不会使用索引。like查询是以%开头。如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引。如果mysql估计使用全表扫描要比使用索引快,则不使用索引。例如,使用<>、not in 、not exist,对于这三种情况大多数情况下认为结果集很大,MySQL就有可能不使用索引。 81题 主键不能重复,不能为空,唯一键不能重复,可以为空。建立主键的目的是让外键来引用。一个表最多只有一个主键,但可以有很多唯一键。 80题 空值('')是不占用空间的,判断空字符用=''或者<>''来进行处理。NULL值是未知的,且占用空间,不走索引;判断 NULL 用 IS NULL 或者 is not null ,SQL 语句函数中可以使用 ifnull ()函数来进行处理。无法比较 NULL 和 0;它们是不等价的。无法使用比较运算符来测试 NULL 值,比如 =, <, 或者 <>。NULL 值可以使用 <=> 符号进行比较,该符号与等号作用相似,但对NULL有意义。进行 count ()统计某列的记录数的时候,如果采用的 NULL 值,会被系统自动忽略掉,但是空值是统计到其中。 79题 HEAP表是访问数据速度最快的MySQL表,他使用保存在内存中的散列索引。一旦服务器重启,所有heap表数据丢失。BLOB或TEXT字段是不允许的。只能使用比较运算符=,<,>,=>,= <。HEAP表不支持AUTO_INCREMENT。索引不可为NULL。 78题 如果想输入字符为十六进制数字,可以输入带有单引号的十六进制数字和前缀(X),或者只用(Ox)前缀输入十六进制数字。如果表达式上下文是字符串,则十六进制数字串将自动转换为字符串。 77题 Mysql服务器通过权限表来控制用户对数据库的访问,权限表存放在mysql数据库里,由mysql_install_db脚本初始化。这些权限表分别user,db,table_priv,columns_priv和host。 76题 在缺省模式下,MYSQL是autocommit模式的,所有的数据库更新操作都会即时提交,所以在缺省情况下,mysql是不支持事务的。但是如果你的MYSQL表类型是使用InnoDB Tables 或 BDB tables的话,你的MYSQL就可以使用事务处理,使用SET AUTOCOMMIT=0就可以使MYSQL允许在非autocommit模式,在非autocommit模式下,你必须使用COMMIT来提交你的更改,或者用ROLLBACK来回滚你的更改。 75题 它会停止递增,任何进一步的插入都将产生错误,因为密钥已被使用。 74题 创建索引的时候尽量使用唯一性大的列来创建索引,由于使用b+tree做为索引,以innodb为例,一个树节点的大小由“innodb_page_size”,为了减少树的高度,同时让一个节点能存放更多的值,索引列尽量在整数类型上创建,如果必须使用字符类型,也应该使用长度较少的字符类型。 73题 当MySQL单表记录数过大时,数据库的CRUD性能会明显下降,一些常见的优化措施如下: 限定数据的范围: 务必禁止不带任何限制数据范围条件的查询语句。比如:我们当用户在查询订单历史的时候,我们可以控制在一个月的范围内。读/写分离: 经典的数据库拆分方案,主库负责写,从库负责读。垂直分区: 根据数据库里面数据表的相关性进行拆分。简单来说垂直拆分是指数据表列的拆分,把一张列比较多的表拆分为多张表。水平分区: 保持数据表结构不变,通过某种策略存储数据分片。这样每一片数据分散到不同的表或者库中,达到了分布式的目的。水平拆分可以支撑非常大的数据量。 72题 乐观锁失败后会抛出ObjectOptimisticLockingFailureException,那么我们就针对这块考虑一下重试,自定义一个注解,用于做切面。针对注解进行切面,设置最大重试次数n,然后超过n次后就不再重试。 71题 一致性非锁定读讲的是一条记录被加了X锁其他事务仍然可以读而不被阻塞,是通过innodb的行多版本实现的,行多版本并不是实际存储多个版本记录而是通过undo实现(undo日志用来记录数据修改前的版本,回滚时会用到,用来保证事务的原子性)。一致性锁定读讲的是我可以通过SELECT语句显式地给一条记录加X锁从而保证特定应用场景下的数据一致性。 70题 数据库引擎:尤其是mysql数据库只有是InnoDB引擎的时候事物才能生效。 show engines 查看数据库默认引擎;SHOW TABLE STATUS from 数据库名字 where Name='表名' 如下;SHOW TABLE STATUS from rrz where Name='rrz_cust';修改表的引擎alter table table_name engine=innodb。 69题 如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值;当然了,这个前提是,键值都是唯一的。如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,直到找到相应的数据;如果是范围查询检索,这时候哈希索引就毫无用武之地了,因为原先是有序的键值,经过哈希算法后,有可能变成不连续的了,就没办法再利用索引完成范围查询检索;同理,哈希索引也没办法利用索引完成排序,以及like ‘xxx%’ 这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询);哈希索引也不支持多列联合索引的最左匹配规则;B+树索引的关键字检索效率比较平均,不像B树那样波动幅度大,在有大量重复键值情况下,哈希索引的效率也是极低的,因为存在所谓的哈希碰撞问题。 68题 decimal精度比float高,数据处理比float简单,一般优先考虑,但float存储的数据范围大,所以范围大的数据就只能用它了,但要注意一些处理细节,因为不精确可能会与自己想的不一致,也常有关于float 出错的问题。 67题 datetime、timestamp精确度都是秒,datetime与时区无关,存储的范围广(1001-9999),timestamp与时区有关,存储的范围小(1970-2038)。 66题 Char使用固定长度的空间进行存储,char(4)存储4个字符,根据编码方式的不同占用不同的字节,gbk编码方式,不论是中文还是英文,每个字符占用2个字节的空间,utf8编码方式,每个字符占用3个字节的空间。Varchar保存可变长度的字符串,使用额外的一个或两个字节存储字符串长度,varchar(10),除了需要存储10个字符,还需要1个字节存储长度信息(10),超过255的长度需要2个字节来存储。char和varchar后面如果有空格,char会自动去掉空格后存储,varchar虽然不会去掉空格,但在进行字符串比较时,会去掉空格进行比较。Varbinary保存变长的字符串,后面不会补\0。 65题 首先分析语句,看看是否load了额外的数据,可能是查询了多余的行并且抛弃掉了,可能是加载了许多结果中并不需要的列,对语句进行分析以及重写。分析语句的执行计划,然后获得其使用索引的情况,之后修改语句或者修改索引,使得语句可以尽可能的命中索引。如果对语句的优化已经无法进行,可以考虑表中的数据量是否太大,如果是的话可以进行横向或者纵向的分表。 64题 建立索引的时候一般要考虑到字段的使用频率,经常作为条件进行查询的字段比较适合。如果需要建立联合索引的话,还需要考虑联合索引中的顺序。此外也要考虑其他方面,比如防止过多的所有对表造成太大的压力。这些都和实际的表结构以及查询方式有关。 63题 存储过程是一些预编译的SQL语句。1、更加直白的理解:存储过程可以说是一个记录集,它是由一些T-SQL语句组成的代码块,这些T-SQL语句代码像一个方法一样实现一些功能(对单表或多表的增删改查),然后再给这个代码块取一个名字,在用到这个功能的时候调用他就行了。2、存储过程是一个预编译的代码块,执行效率比较高,一个存储过程替代大量T_SQL语句 ,可以降低网络通信量,提高通信速率,可以一定程度上确保数据安全。 62题 密码散列、盐、用户身份证号等固定长度的字符串应该使用char而不是varchar来存储,这样可以节省空间且提高检索效率。 61题 推荐使用自增ID,不要使用UUID。因为在InnoDB存储引擎中,主键索引是作为聚簇索引存在的,也就是说,主键索引的B+树叶子节点上存储了主键索引以及全部的数据(按照顺序),如果主键索引是自增ID,那么只需要不断向后排列即可,如果是UUID,由于到来的ID与原来的大小不确定,会造成非常多的数据插入,数据移动,然后导致产生很多的内存碎片,进而造成插入性能的下降。总之,在数据量大一些的情况下,用自增主键性能会好一些。 60题 char是一个定长字段,假如申请了char(10)的空间,那么无论实际存储多少内容。该字段都占用10个字符,而varchar是变长的,也就是说申请的只是最大长度,占用的空间为实际字符长度+1,最后一个字符存储使用了多长的空间。在检索效率上来讲,char > varchar,因此在使用中,如果确定某个字段的值的长度,可以使用char,否则应该尽量使用varchar。例如存储用户MD5加密后的密码,则应该使用char。 59题 一. read uncommitted(读取未提交数据) 即便是事务没有commit,但是我们仍然能读到未提交的数据,这是所有隔离级别中最低的一种。 二. read committed(可以读取其他事务提交的数据)---大多数数据库默认的隔离级别 当前会话只能读取到其他事务提交的数据,未提交的数据读不到。 三. repeatable read(可重读)---MySQL默认的隔离级别 当前会话可以重复读,就是每次读取的结果集都相同,而不管其他事务有没有提交。 四. serializable(串行化) 其他会话对该表的写操作将被挂起。可以看到,这是隔离级别中最严格的,但是这样做势必对性能造成影响。所以在实际的选用上,我们要根据当前具体的情况选用合适的。 58题 B+树的高度一般为2-4层,所以查找记录时最多只需要2-4次IO,相对二叉平衡树已经大大降低了。范围查找时,能通过叶子节点的指针获取数据。例如查找大于等于3的数据,当在叶子节点中查到3时,通过3的尾指针便能获取所有数据,而不需要再像二叉树一样再获取到3的父节点。 57题 因为事务在修改页时,要先记 undo,在记 undo 之前要记 undo 的 redo, 然后修改数据页,再记数据页修改的 redo。 Redo(里面包括 undo 的修改) 一定要比数据页先持久化到磁盘。 当事务需要回滚时,因为有 undo,可以把数据页回滚到前镜像的状态,崩溃恢复时,如果 redo log 中事务没有对应的 commit 记录,那么需要用 undo把该事务的修改回滚到事务开始之前。 如果有 commit 记录,就用 redo 前滚到该事务完成时并提交掉。 56题 redo log是物理日志,记录的是"在某个数据页上做了什么修改"。 binlog是逻辑日志,记录的是这个语句的原始逻辑,比如"给ID=2这一行的c字段加1"。 redo log是InnoDB引擎特有的;binlog是MySQL的Server层实现的,所有引擎都可以使用。 redo log是循环写的,空间固定会用完:binlog 是可以追加写入的。"追加写"是指binlog文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。 最开始 MySQL 里并没有 InnoDB 引擎,MySQL 自带的引擎是 MyISAM,但是 MyISAM 没有 crash-safe 的能力,binlog日志只能用于归档。而InnoDB 是另一个公司以插件形式引入 MySQL 的,既然只依靠 binlog 是没有 crash-safe 能力的,所以 InnoDB 使用另外一套日志系统,也就是 redo log 来实现 crash-safe 能力。 55题 重做日志(redo log)      作用:确保事务的持久性,防止在发生故障,脏页未写入磁盘。重启数据库会进行redo log执行重做,达到事务一致性。 回滚日志(undo log)  作用:保证数据的原子性,保存了事务发生之前的数据的一个版本,可以用于回滚,同时可以提供多版本并发控制下的读(MVCC),也即非锁定读。 二进 制日志(binlog)    作用:用于主从复制,实现主从同步;用于数据库的基于时间点的还原。 错误日志(errorlog) 作用:Mysql本身启动,停止,运行期间发生的错误信息。 慢查询日志(slow query log)  作用:记录执行时间过长的sql,时间阈值可以配置,只记录执行成功。 一般查询日志(general log)    作用:记录数据库的操作明细,默认关闭,开启后会降低数据库性能 。 中继日志(relay log) 作用:用于数据库主从同步,将主库发来的bin log保存在本地,然后从库进行回放。 54题 MySQL有三种锁的级别:页级、表级、行级。 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。 死锁: 是指两个或两个以上的进程在执行过程中。因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。 死锁的关键在于:两个(或以上)的Session加锁的顺序不一致。 那么对应的解决死锁问题的关键就是:让不同的session加锁有次序。死锁的解决办法:1.查出的线程杀死。2.设置锁的超时时间。3.指定获取锁的顺序。 53题 当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性(脏读,不可重复读,幻读等),可能产生死锁。 乐观锁:乐观锁不是数据库自带的,需要我们自己去实现。 悲观锁:在进行每次操作时都要通过获取锁才能进行对相同数据的操作。 共享锁:加了共享锁的数据对象可以被其他事务读取,但不能修改。 排他锁:当数据对象被加上排它锁时,一个事务必须得到锁才能对该数据对象进行访问,一直到事务结束锁才被释放。 行锁:就是给某一条记录加上锁。 52题 Mysql是关系型数据库,MongoDB是非关系型数据库,数据存储结构的不同。 51题 关系型数据库优点:1.保持数据的一致性(事务处理)。 2.由于以标准化为前提,数据更新的开销很小。 3. 可以进行Join等复杂查询。 缺点:1、为了维护一致性所付出的巨大代价就是其读写性能比较差。 2、固定的表结构。 3、高并发读写需求。 4、海量数据的高效率读写。 非关系型数据库优点:1、无需经过sql层的解析,读写性能很高。 2、基于键值对,数据没有耦合性,容易扩展。 3、存储数据的格式:nosql的存储格式是key,value形式、文档形式、图片形式等等,文档形式、图片形式等等,而关系型数据库则只支持基础类型。 缺点:1、不提供sql支持,学习和使用成本较高。 2、无事务处理,附加功能bi和报表等支持也不好。 redis与mongoDB的区别: 性能:TPS方面redis要大于mongodb。 可操作性:mongodb支持丰富的数据表达,索引,redis较少的网络IO次数。 可用性:MongoDB优于Redis。 一致性:redis事务支持比较弱,mongoDB不支持事务。 数据分析:mongoDB内置了数据分析的功能(mapreduce)。 应用场景:redis数据量较小的更性能操作和运算上,MongoDB主要解决海量数据的访问效率问题。 50题 如果Redis被当做缓存使用,使用一致性哈希实现动态扩容缩容。如果Redis被当做一个持久化存储使用,必须使用固定的keys-to-nodes映射关系,节点的数量一旦确定不能变化。否则的话(即Redis节点需要动态变化的情况),必须使用可以在运行时进行数据再平衡的一套系统,而当前只有Redis集群可以做到这样。 49题 分区可以让Redis管理更大的内存,Redis将可以使用所有机器的内存。如果没有分区,你最多只能使用一台机器的内存。分区使Redis的计算能力通过简单地增加计算机得到成倍提升,Redis的网络带宽也会随着计算机和网卡的增加而成倍增长。 48题 除了缓存服务器自带的缓存失效策略之外(Redis默认的有6种策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种: 1.定时去清理过期的缓存; 2.当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。 两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!具体用哪种方案,可以根据应用场景来权衡。 47题 Redis提供了两种方式来作消息队列: 一个是使用生产者消费模式模式:会让一个或者多个客户端监听消息队列,一旦消息到达,消费者马上消费,谁先抢到算谁的,如果队列里没有消息,则消费者继续监听 。另一个就是发布订阅者模式:也是一个或多个客户端订阅消息频道,只要发布者发布消息,所有订阅者都能收到消息,订阅者都是平等的。 46题 Redis的数据结构列表(list)可以实现延时队列,可以通过队列和栈来实现。blpop/brpop来替换lpop/rpop,blpop/brpop阻塞读在队列没有数据的时候,会立即进入休眠状态,一旦数据到来,则立刻醒过来。Redis的有序集合(zset)可以用于实现延时队列,消息作为value,时间作为score。Zrem 命令用于移除有序集中的一个或多个成员,不存在的成员将被忽略。当 key 存在但不是有序集类型时,返回一个错误。 45题 1.热点数据缓存:因为Redis 访问速度块、支持的数据类型比较丰富。 2.限时业务:expire 命令设置 key 的生存时间,到时间后自动删除 key。 3.计数器:incrby 命令可以实现原子性的递增。 4.排行榜:借助 SortedSet 进行热点数据的排序。 5.分布式锁:利用 Redis 的 setnx 命令进行。 6.队列机制:有 list push 和 list pop 这样的命令。 44题 一致哈希 是一种特殊的哈希算法。在使用一致哈希算法后,哈希表槽位数(大小)的改变平均只需要对 K/n 个关键字重新映射,其中K是关键字的数量, n是槽位数量。然而在传统的哈希表中,添加或删除一个槽位的几乎需要对所有关键字进行重新映射。 43题 RDB的优点:适合做冷备份;读写服务影响小,reids可以保持高性能;重启和恢复redis进程,更加快速。RDB的缺点:宕机会丢失最近5分钟的数据;文件特别大时可能会暂停数毫秒,或者甚至数秒。 AOF的优点:每个一秒执行fsync操作,最多丢失1秒钟的数据;以append-only模式写入,没有任何磁盘寻址的开销;文件过大时,不会影响客户端读写;适合做灾难性的误删除的紧急恢复。AOF的缺点:AOF日志文件比RDB数据快照文件更大,支持写QPS比RDB支持的写QPS低;比RDB脆弱,容易有bug。 42题 对于Redis而言,命令的原子性指的是:一个操作的不可以再分,操作要么执行,要么不执行。Redis的操作之所以是原子性的,是因为Redis是单线程的。而在程序中执行多个Redis命令并非是原子性的,这也和普通数据库的表现是一样的,可以用incr或者使用Redis的事务,或者使用Redis+Lua的方式实现。对Redis来说,执行get、set以及eval等API,都是一个一个的任务,这些任务都会由Redis的线程去负责执行,任务要么执行成功,要么执行失败,这就是Redis的命令是原子性的原因。 41题 (1)twemproxy,使用方式简单(相对redis只需修改连接端口),对旧项目扩展的首选。(2)codis,目前用的最多的集群方案,基本和twemproxy一致的效果,但它支持在节点数改变情况下,旧节点数据可恢复到新hash节点。(3)redis cluster3.0自带的集群,特点在于他的分布式算法不是一致性hash,而是hash槽的概念,以及自身支持节点设置从节点。(4)在业务代码层实现,起几个毫无关联的redis实例,在代码层,对key进行hash计算,然后去对应的redis实例操作数据。这种方式对hash层代码要求比较高,考虑部分包括,节点失效后的代替算法方案,数据震荡后的自动脚本恢复,实例的监控,等等。 40题 (1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件 (2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次 (3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内 (4) 尽量避免在压力很大的主库上增加从库 (5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3...这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。 39题 比如订单管理,热数据:3个月内的订单数据,查询实时性较高;温数据:3个月 ~ 12个月前的订单数据,查询频率不高;冷数据:1年前的订单数据,几乎不会查询,只有偶尔的查询需求。热数据使用mysql进行存储,需要分库分表;温数据可以存储在ES中,利用搜索引擎的特性基本上也可以做到比较快的查询;冷数据可以存放到Hive中。从存储形式来说,一般情况冷数据存储在磁带、光盘,热数据一般存放在SSD中,存取速度快,而温数据可以存放在7200转的硬盘。 38题 当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。 37题 分层架构设计,有一条准则:站点层、服务层要做到无数据无状态,这样才能任意的加节点水平扩展,数据和状态尽量存储到后端的数据存储服务,例如数据库服务或者缓存服务。显然进程内缓存违背了这一原则。 36题 更新数据的时候,根据数据的唯一标识,将操作路由之后,发送到一个 jvm 内部队列中。读取数据的时候,如果发现数据不在缓存中,那么将重新读取数据+更新缓存的操作,根据唯一标识路由之后,也发送同一个 jvm 内部队列中。一个队列对应一个工作线程,每个工作线程串行拿到对应的操作,然后一条一条的执行。 35题 redis分布式锁加锁过程:通过setnx向特定的key写入一个随机值,并同时设置失效时间,写值成功既加锁成功;redis分布式锁解锁过程:匹配随机值,删除redis上的特点key数据,要保证获取数据、判断一致以及删除数据三个操作是原子的,为保证原子性一般使用lua脚本实现;在此基础上进一步优化的话,考虑使用心跳检测对锁的有效期进行续期,同时基于redis的发布订阅优雅的实现阻塞式加锁。 34题 volatile-lru:当内存不足以容纳写入数据时,从已设置过期时间的数据集中挑选最近最少使用的数据淘汰。 volatile-ttl:当内存不足以容纳写入数据时,从已设置过期时间的数据集中挑选将要过期的数据淘汰。 volatile-random:当内存不足以容纳写入数据时,从已设置过期时间的数据集中任意选择数据淘汰。 allkeys-lru:当内存不足以容纳写入数据时,从数据集中挑选最近最少使用的数据淘汰。 allkeys-random:当内存不足以容纳写入数据时,从数据集中任意选择数据淘汰。 noeviction:禁止驱逐数据,当内存使用达到阈值的时候,所有引起申请内存的命令会报错。 33题 定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。 惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。 定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。 32题 缓存击穿,一个存在的key,在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到DB,造成瞬时DB请求量大、压力骤增。如何避免:在访问key之前,采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。 31题 缓存雪崩,是指在某一个时间段,缓存集中过期失效。大量的key设置了相同的过期时间,导致在缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增,引起雪崩。而缓存服务器某个节点宕机或断网,对数据库服务器造成的压力是不可预知的,很有可能瞬间就把数据库压垮。如何避免:1.redis高可用,搭建redis集群。2.限流降级,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。3.数据预热,在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间。 30题 缓存穿透,是指查询一个数据库一定不存在的数据。正常的使用缓存流程大致是,数据查询先进行缓存查询,如果key不存在或者key已经过期,再对数据库进行查询,并把查询到的对象,放进缓存。如果数据库查询对象为空,则不放进缓存。一些恶意的请求会故意查询不存在的 key,请求量很大,对数据库造成压力,甚至压垮数据库。 如何避免:1:对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该 key 对应的数据 insert 了之后清理缓存。2:对一定不存在的 key 进行过滤。可以把所有的可能存在的 key 放到一个大的 Bitmap 中,查询时通过该 bitmap 过滤。 29题 1.memcached 所有的值均是简单的字符串,redis 作为其替代者,支持更为丰富的数据类型。 2.redis 的速度比 memcached 快很多。 3.redis 可以持久化其数据。 4.Redis支持数据的备份,即master-slave模式的数据备份。 5.Redis采用VM机制。 6.value大小:redis最大可以达到1GB,而memcache只有1MB。 28题 Spring Boot 推荐使用 Java 配置而非 XML 配置,但是 Spring Boot 中也可以使用 XML 配置,通过spring提供的@ImportResource来加载xml配置。例如:@ImportResource({"classpath:some-context.xml","classpath:another-context.xml"}) 27题 Spring像一个大家族,有众多衍生产品例如Spring Boot,Spring Security等等,但他们的基础都是Spring的IOC和AOP,IOC提供了依赖注入的容器,而AOP解决了面向切面的编程,然后在此两者的基础上实现了其他衍生产品的高级功能。Spring MVC是基于Servlet的一个MVC框架,主要解决WEB开发的问题,因为 Spring的配置非常复杂,各种xml,properties处理起来比较繁琐。Spring Boot遵循约定优于配置,极大降低了Spring使用门槛,又有着Spring原本灵活强大的功能。总结:Spring MVC和Spring Boot都属于Spring,Spring MVC是基于Spring的一个MVC框架,而Spring Boot是基于Spring的一套快速开发整合包。 26题 YAML 是 "YAML Ain't a Markup Language"(YAML 不是一种标记语言)的递归缩写。YAML 的配置文件后缀为 .yml,是一种人类可读的数据序列化语言,可以简单表达清单、散列表,标量等数据形态。它通常用于配置文件,与属性文件相比,YAML文件就更加结构化,而且更少混淆。可以看出YAML具有分层配置数据。 25题 Spring Boot有3种热部署方式: 1.使用springloaded配置pom.xml文件,使用mvn spring-boot:run启动。 2.使用springloaded本地加载启动,配置jvm参数-javaagent:<jar包地址> -noverify。 3.使用devtools工具包,操作简单,但是每次需要重新部署。 用

游客ih62co2qqq5ww 2020-03-27 23:56:48 0 浏览量 回答数 0

回答

本文介绍如何在本地文件网关控制台上完成共享设置。 前提条件 已注册阿里云账号,并完成实名认证,详情请参见阿里云账号注册流程。 说明 建议您使用RAM账户登录云存储网关控制台进行相关操作,详情请参见账号访问控制。 已开通云存储网关服务。 首次登录云存储网关控制台时,根据页面提示开通云存储网关服务。 已部署本地文件网关控制台,详情请参见部署本地文件网关控制台。 已创建OSS Bucket,详情请参见创建存储空间。 说明 云存储网关支持标准(Standard)类型、低频访问(IA)类型和归档存储类型的OSS Bucket。 对于归档文件,如果在创建共享时未开启归档支持功能,则在进行读操作时需要先手动解冻文件。 已添加磁盘,详情请参见添加磁盘。 步骤一:添加缓存 文件存储网关的每个共享目录都对应唯一一个缓存盘,创建多个共享目录则需要创建多个缓存盘。您可以将共享目录下的数据通过缓存盘传至阿里云OSS,也可以通过缓存盘将阿里云OSS数据同步到本地。 在浏览器中,输入https://<文件网关IP地址>访问本地文件网关控制台。 输入用户名和密码,单击确认。 选择缓存设置页面,单击创建。 在创建缓存对话框中,完成如下配置。 硬盘:单击选择,选择可用的硬盘。 在部署平台添加磁盘后,此处才有可用的硬盘,详情请参见添加磁盘。 文件系统:可选。勾选此选项,可重用该缓存盘中的数据。如果您误删共享后,可重建共享并使用缓存盘的数据重用功能进行数据恢复。 说明 如果缓存上没有文件系统,勾选重用选项,会创建缓存失败。 单击确认,完成添加。 步骤二:绑定云资源 创建以OSS Bucket为存储后端的共享资源,一个Bucket对应一个共享资源。文件存储网关支持创建多个云资源。 说明 通过客户端写入云存储网关的数据默认实时上传到OSS Bucket,也可在创建共享时设置延时上传,最大延时可支持设置为120s。 在本地文件网关控制台中,选择云资源设置页面,单击绑定。 在 绑定云资源对话框中,完成如下配置。 参数 说明 资源名称 设置云资源名称。 跨域绑定 选择是,可访问与文件网关不同地域的OSS Bucket。 选择否,只能访问与文件网关相同地域的Bucket。 说明 本地文件网关的时区必须与OSS Bucket的时区保持一致。 区域 选择Bucket所在区域。 Bucket名称 选择要绑定的Bucket。 使用SSL 如果选择是,可使用SSL访问OSS Bucket。 单击确认,完成云资源的绑定。 步骤三:创建共享 本地文件网关支持NFS共享和SMB共享,您可以根据需求进行选择。此处以创建NFS共享为例进行说明,如果您要创建SMB共享,详情请参见管理SMB共享。 安装NFS客户端,详情请参见安装NFS客户端。 返回本地文件网关控制台,选择NFS页面,单击创建。 在创建NFS对话框中,完成如下配置,并单击确认。 参数 说明 共享名称 NFS协议的虚拟挂载点。 NFSv4可以通过该名称直接挂载;NFSv3需要通过showmount -e <网关IP地址>获取挂载点。 读写客户端列表 允许读写访问NFS网关的IP地址或网段。 例如192.168.10.10或192.168.0.0/24,允许输入多个IP地址或者网段。 只读客户端列表 允许只读访问NFS网关的IP地址或网段。 例如192.168.10.10或192.168.0.0/24,允许输入多个IP地址或者网段。 用户映射 设置NFS客户端用户与NFS服务器用户之间的映射关系,仅当协议类型选择NFS时需要配置。 none:NFS客户端用户不被映射为NFS服务器的nobody用户。 root_squash:限制root用户,当NFS客户端以root用户身份访问时,映射为NFS服务器的nobody用户。 all_squash:限制所有用户,无论NFS客户端以何种用户身份访问,均映射为NFS服务器的nobody用户。 all_anonymous:限制所有用户,无论NFS客户端以何种用户身份访问,均映射为NFS服务器的匿名用户。 归档支持 选择是,开启归档支持功能。在已归档的文件上发起读操作请求时同步发起解冻请求,请求不会报错,但存在一定的时间延迟。 选择否,关闭归档支持功能。在已归档的文件上发起读操作请求时,需先手动解冻文件,否则请求会报错。 说明 基础型文件网关不支持归档支持功能。 启用 启用NFS共享。 如果您暂时不想使用该NFS共享,您可以选择否,关闭该NFS共享。 模式 包括缓存模式和复制模式。 复制模式:所有数据都会保存两份拷贝,一份保存在本地缓存,另一份保存在OSS。 缓存模式:本地缓存全量元数据和经常访问的用户数据。OSS侧保持全量数据。 反向同步 将OSS上的元数据同步回本地。适用于网关容灾和数据恢复/共享场景。 说明 反向同步会扫描Bucket下的所有对象,如果对象数量较多,会产生OSS API请求费用。具体费用,请参见对象存储 OSS 详细价格信息中的请求费用。 加密类型 包括不加密和服务端加密。 如果选择服务端加密,还需设置密钥ID。您可以在密钥管理服务控制台中创建密钥,详情请参见创建密钥。 开启OSS服务端加密后,允许用户自带密钥,目前支持从密钥管理服务中导入KMS密钥。 开启服务端加密后,通过共享目录上云的文件会在OSS端自动利用KMS密钥进行加密。您可以通过Get Object API验证当前文件是否已经加密,如果返回的Header中x-oss-server-side-encryption字段值为KMS,x-oss-server-side-encryption-key-id字段值为密钥ID,则表示已加密。 说明 白名单用户才能使用此功能。 在密钥管理服务控制台创建密钥时,需选择与OSS Bucket一样的区域。 Bucket名称 选择已创建的Bucket。 子目录 输入Bucket下的子目录。 子目录只支持英文和数字。 说明 从1.0.38版本开始支持将文件系统的根目录对接到OSS Bucket的某个子目录,便于用户做访问隔离。 子目录可以为OSS Bucket中已存在的目录也可以为OSS Bucket中还未创建的目录,创建共享完成后,将以该子目录为根目录,后续的文件和目录都会创建该目录下。 使用元数据盘 使用元数据盘后,将数据盘与元数据盘分离,元数据盘用于存放共享文件夹元数据信息。 选择是,需选择对应的元数据盘和数据盘。 选择否,需选择对应的缓存硬盘。 说明 白名单用户才能使用此功能。 忽略删除 文件删除操作不同步至OSS防止误操作。OSS侧保持全量数据。 同步延迟 设置同步延迟,在关闭文件会延迟一段时间再上传,防止频繁的本地修改操作造成OSS碎片。缺省值为5s,最大值120s。 最大写入速度 允许的最大写入速度为1280MB/s。默认为0,表示不限制速度。 最大上传速度 允许的最大上传速度为1280MB/s。默认为0,表示不限制速度。 说明 在限制速度的情况下,最大上传速度不能小于最大写入速度。 碎片优化 针对某些反复随机小IO读写的应用,启用此配置可提升性能,请根据场景谨慎选择。 上传优化 实时缓存回收,适用于数据纯备份上云场景。 单击确认,完成共享的创建。 创建完成后,您可以通过NFS客户端访问共享目录,详情请参见访问NFS共享目录。

1934890530796658 2020-03-31 11:11:08 0 浏览量 回答数 0

回答

本文介绍如何在本地文件网关控制台上完成共享设置。 前提条件 已注册阿里云账号,并完成实名认证,详情请参见阿里云账号注册流程。 说明 建议您使用RAM账户登录云存储网关控制台进行相关操作,详情请参见账号访问控制。 已开通云存储网关服务。 首次登录云存储网关控制台时,根据页面提示开通云存储网关服务。 已部署本地文件网关控制台,详情请参见部署本地文件网关控制台。 已创建OSS Bucket,详情请参见创建存储空间。 说明 云存储网关支持标准(Standard)类型、低频访问(IA)类型和归档存储类型的OSS Bucket。 对于归档文件,如果在创建共享时未开启归档支持功能,则在进行读操作时需要先手动解冻文件。 已添加磁盘,详情请参见添加磁盘。 步骤一:添加缓存 文件存储网关的每个共享目录都对应唯一一个缓存盘,创建多个共享目录则需要创建多个缓存盘。您可以将共享目录下的数据通过缓存盘传至阿里云OSS,也可以通过缓存盘将阿里云OSS数据同步到本地。 在浏览器中,输入https://<文件网关IP地址>访问本地文件网关控制台。 输入用户名和密码,单击确认。 选择缓存设置页面,单击创建。 在创建缓存对话框中,完成如下配置。 硬盘:单击选择,选择可用的硬盘。 在部署平台添加磁盘后,此处才有可用的硬盘,详情请参见添加磁盘。 文件系统:可选。勾选此选项,可重用该缓存盘中的数据。如果您误删共享后,可重建共享并使用缓存盘的数据重用功能进行数据恢复。 说明 如果缓存上没有文件系统,勾选重用选项,会创建缓存失败。 单击确认,完成添加。 步骤二:绑定云资源 创建以OSS Bucket为存储后端的共享资源,一个Bucket对应一个共享资源。文件存储网关支持创建多个云资源。 说明 通过客户端写入云存储网关的数据默认实时上传到OSS Bucket,也可在创建共享时设置延时上传,最大延时可支持设置为120s。 在本地文件网关控制台中,选择云资源设置页面,单击绑定。 在 绑定云资源对话框中,完成如下配置。 参数 说明 资源名称 设置云资源名称。 跨域绑定 选择是,可访问与文件网关不同地域的OSS Bucket。 选择否,只能访问与文件网关相同地域的Bucket。 说明 本地文件网关的时区必须与OSS Bucket的时区保持一致。 区域 选择Bucket所在区域。 Bucket名称 选择要绑定的Bucket。 使用SSL 如果选择是,可使用SSL访问OSS Bucket。 单击确认,完成云资源的绑定。 步骤三:创建共享 本地文件网关支持NFS共享和SMB共享,您可以根据需求进行选择。此处以创建NFS共享为例进行说明,如果您要创建SMB共享,详情请参见管理SMB共享。 安装NFS客户端,详情请参见安装NFS客户端。 返回本地文件网关控制台,选择NFS页面,单击创建。 在创建NFS对话框中,完成如下配置,并单击确认。 参数 说明 共享名称 NFS协议的虚拟挂载点。 NFSv4可以通过该名称直接挂载;NFSv3需要通过showmount -e <网关IP地址>获取挂载点。 读写客户端列表 允许读写访问NFS网关的IP地址或网段。 例如192.168.10.10或192.168.0.0/24,允许输入多个IP地址或者网段。 只读客户端列表 允许只读访问NFS网关的IP地址或网段。 例如192.168.10.10或192.168.0.0/24,允许输入多个IP地址或者网段。 用户映射 设置NFS客户端用户与NFS服务器用户之间的映射关系,仅当协议类型选择NFS时需要配置。 none:NFS客户端用户不被映射为NFS服务器的nobody用户。 root_squash:限制root用户,当NFS客户端以root用户身份访问时,映射为NFS服务器的nobody用户。 all_squash:限制所有用户,无论NFS客户端以何种用户身份访问,均映射为NFS服务器的nobody用户。 all_anonymous:限制所有用户,无论NFS客户端以何种用户身份访问,均映射为NFS服务器的匿名用户。 归档支持 选择是,开启归档支持功能。在已归档的文件上发起读操作请求时同步发起解冻请求,请求不会报错,但存在一定的时间延迟。 选择否,关闭归档支持功能。在已归档的文件上发起读操作请求时,需先手动解冻文件,否则请求会报错。 说明 基础型文件网关不支持归档支持功能。 启用 启用NFS共享。 如果您暂时不想使用该NFS共享,您可以选择否,关闭该NFS共享。 模式 包括缓存模式和复制模式。 复制模式:所有数据都会保存两份拷贝,一份保存在本地缓存,另一份保存在OSS。 缓存模式:本地缓存全量元数据和经常访问的用户数据。OSS侧保持全量数据。 反向同步 将OSS上的元数据同步回本地。适用于网关容灾和数据恢复/共享场景。 说明 反向同步会扫描Bucket下的所有对象,如果对象数量较多,会产生OSS API请求费用。具体费用,请参见对象存储 OSS 详细价格信息中的请求费用。 加密类型 包括不加密和服务端加密。 如果选择服务端加密,还需设置密钥ID。您可以在密钥管理服务控制台中创建密钥,详情请参见创建密钥。 开启OSS服务端加密后,允许用户自带密钥,目前支持从密钥管理服务中导入KMS密钥。 开启服务端加密后,通过共享目录上云的文件会在OSS端自动利用KMS密钥进行加密。您可以通过Get Object API验证当前文件是否已经加密,如果返回的Header中x-oss-server-side-encryption字段值为KMS,x-oss-server-side-encryption-key-id字段值为密钥ID,则表示已加密。 说明 白名单用户才能使用此功能。 在密钥管理服务控制台创建密钥时,需选择与OSS Bucket一样的区域。 Bucket名称 选择已创建的Bucket。 子目录 输入Bucket下的子目录。 子目录只支持英文和数字。 说明 从1.0.38版本开始支持将文件系统的根目录对接到OSS Bucket的某个子目录,便于用户做访问隔离。 子目录可以为OSS Bucket中已存在的目录也可以为OSS Bucket中还未创建的目录,创建共享完成后,将以该子目录为根目录,后续的文件和目录都会创建该目录下。 使用元数据盘 使用元数据盘后,将数据盘与元数据盘分离,元数据盘用于存放共享文件夹元数据信息。 选择是,需选择对应的元数据盘和数据盘。 选择否,需选择对应的缓存硬盘。 说明 白名单用户才能使用此功能。 忽略删除 文件删除操作不同步至OSS防止误操作。OSS侧保持全量数据。 同步延迟 设置同步延迟,在关闭文件会延迟一段时间再上传,防止频繁的本地修改操作造成OSS碎片。缺省值为5s,最大值120s。 最大写入速度 允许的最大写入速度为1280MB/s。默认为0,表示不限制速度。 最大上传速度 允许的最大上传速度为1280MB/s。默认为0,表示不限制速度。 说明 在限制速度的情况下,最大上传速度不能小于最大写入速度。 碎片优化 针对某些反复随机小IO读写的应用,启用此配置可提升性能,请根据场景谨慎选择。 上传优化 实时缓存回收,适用于数据纯备份上云场景。 单击确认,完成共享的创建。 创建完成后,您可以通过NFS客户端访问共享目录,详情请参见访问NFS共享目录。

1934890530796658 2020-03-31 11:11:37 0 浏览量 回答数 0

问题

ECS Windows系统Apache配置ssl如何加密连接

boxti 2019-12-01 21:31:18 1312 浏览量 回答数 0

问题

如何保证缓存与数据库的双写一致性?【Java问答】38期

剑曼红尘 2020-06-16 12:58:57 36 浏览量 回答数 1
阿里云大学 云服务器ECS com域名 网站域名whois查询 开发者平台 小程序定制 小程序开发 国内短信套餐包 开发者技术与产品 云数据库 图像识别 开发者问答 阿里云建站 阿里云备案 云市场 万网 阿里云帮助文档 免费套餐 开发者工具 云栖号物联网 小程序开发制作 视频内容分析 企业网站制作 视频集锦 代理记账服务 2020阿里巴巴研发效能峰会 企业建站模板 云效成长地图 高端建站 云栖号弹性计算 阿里云云栖号 云栖号案例 云栖号直播