前端技术
HTML
CSS
Javascript
前端框架和UI库
VUE
ReactJS
AngularJS
JQuery
NodeJS
JSON
Element-UI
Bootstrap
Material UI
服务端和客户端
Java
Python
PHP
Golang
Scala
Kotlin
Groovy
Ruby
Lua
.net
c#
c++
后端WEB和工程框架
SpringBoot
SpringCloud
Struts2
MyBatis
Hibernate
Tornado
Beego
Go-Spring
Go Gin
Go Iris
Dubbo
HessianRPC
Maven
Gradle
数据库
MySQL
Oracle
Mongo
中间件与web容器
Redis
MemCache
Etcd
Cassandra
Kafka
RabbitMQ
RocketMQ
ActiveMQ
Nacos
Consul
Tomcat
Nginx
Netty
大数据技术
Hive
Impala
ClickHouse
DorisDB
Greenplum
PostgreSQL
HBase
Kylin
Hadoop
Apache Pig
ZooKeeper
SeaTunnel
Sqoop
Datax
Flink
Spark
Mahout
数据搜索与日志
ElasticSearch
Apache Lucene
Apache Solr
Kibana
Logstash
数据可视化与OLAP
Apache Atlas
Superset
Saiku
Tesseract
系统与容器
Linux
Shell
Docker
Kubernetes
[索引模式 Index Patterns ]的搜索结果
这里是文章列表。热门标签的颜色随机变换,标签颜色没有特殊含义。
点击某个标签可搜索标签相关的文章。
点击某个标签可搜索标签相关的文章。
Kibana
...3.1 第一步:创建索引模式 首先,我们需要确保你的数据已经被正确地存储到Elasticsearch中,并且可以通过Kibana访问。如果还没有创建索引模式,可以按照以下步骤操作: bash 登录Kibana界面 1. 点击左侧菜单栏中的“Management”。 2. 找到“Stack Management”部分,点击“Index Patterns”。 3. 点击“Create index pattern”按钮。 4. 输入你的索引名称(例如 "logstash-"),然后点击“Next step”。 5. 选择时间字段(通常是@timestamp),点击“Create index pattern”完成配置。 > 思考点:这里的关键在于选择合适的索引名称和时间字段。如果你的时间字段命名不规范,后续可能会导致数据无法正确筛选哦! 3.2 第二步:设置索引生命周期策略 接下来,我们要为索引创建生命周期策略。这是Kibana中最核心的部分,直接决定了数据的保留方式。 示例代码: javascript PUT _ilm/policy/my_policy { "policy": { "phases": { "hot": { "actions": { "rollover": { "max_size": "50gb", "max_age": "30d" } } }, "delete": { "min_age": "1y", "actions": { "delete": {} } } } } } 这段代码的意思是: - 热阶段(Hot Phase):当索引大小达到50GB或者超过30天时,触发滚动操作。 - 删除阶段(Delete Phase):超过1年后,自动删除该索引。 > 小贴士:这里的max_size和max_age可以根据你的实际需求调整。比如,如果你的服务器内存较小,可以将max_size调低一点。 3.3 第三步:将策略应用到索引 设置好生命周期策略后,我们需要将其绑定到具体的索引上。具体步骤如下: bash POST /my-index/_settings { "index.lifecycle.name": "my_policy", "index.lifecycle.rollover_alias": "my_index" } 这段代码的作用是将之前创建的my_policy策略应用到名为my-index的索引上。同时,通过rollover_alias指定滚动索引的别名。 --- 4. 实战案例 数据保留策略的实际效果 为了让大家更直观地理解数据保留策略的效果,我特意准备了一个小案例。假设你是一名电商公司的运维工程师,每天都会收到大量的订单日志,格式如下: json { "order_id": "123456789", "status": "success", "timestamp": "2023-09-01T10:00:00Z" } 现在,你想对这些日志进行生命周期管理,具体要求如下: - 最近3个月的数据需要保留。 - 超过3个月的数据自动归档到冷存储。 - 超过1年的数据完全删除。 实现方案: 1. 创建索引模式,命名为orders-。 2. 定义生命周期策略 javascript PUT _ilm/policy/orders_policy { "policy": { "phases": { "hot": { "actions": { "rollover": { "max_size": "10gb", "max_age": "3m" } } }, "warm": { "actions": { "freeze": {} } }, "delete": { "min_age": "1y", "actions": { "delete": {} } } } } } 3. 将策略绑定到索引 bash POST /orders-/_settings { "index.lifecycle.name": "orders_policy", "index.lifecycle.rollover_alias": "orders" } 运行以上代码后,你会发现: - 每隔3个月,新的订单日志会被滚动到一个新的索引中。 - 超过3个月的旧数据会被冻结,存入冷存储。 - 超过1年的数据会被彻底删除,释放存储空间。 --- 5. 总结与展望 通过今天的分享,相信大家对如何在Kibana中设置数据保留策略有了更深的理解。虽然设置过程看似繁琐,但实际上只需要几步就能搞定。而且啊,要是咱们好好用数据保留这招,不仅能让系统跑得更快、更顺畅,还能帮咱们把那些藏在数据里的宝贝疙瘩给挖出来,多好呀! 最后,我想说的是,技术学习是一个不断探索的过程。如果你在实践中遇到问题,不妨多查阅官方文档或者向社区求助。毕竟,我们每个人都是技术路上的探索者,一起努力才能走得更远! 好了,今天的分享就到这里啦!如果你觉得这篇文章有用,记得点赞支持哦~咱们下次再见!
2025-04-30 16:26:33
16
风轻云淡
Kibana
...scover页面创建索引模式时选择任意一个远程集群的索引了。例如: json POST .kibana/_index_template/my_cross_cluster_search_template { "index_patterns": ["cluster_1:index_name", "cluster_2:another_index"], "template": { "settings": {}, "mappings": {} }, "composed_of": [] } 这样,在Discover面板搜索时,就可以同时查询到"cluster_1:index_name"和"cluster_2:another_index"两个不同集群的数据了。 5. 深入思考与探讨 跨集群搜索的功能对于那些拥有大量分布式数据源的企业来说,无疑是一个福音。然而,这并不意味着我们可以无限制地增加集群数量。当我们的集群规模逐渐扩大时,性能消耗和复杂程度也会像体重秤上的数字一样蹭蹭上涨。所以在实际操作中,咱们就得像个精打细算的家庭主妇,根据自家业务的具体需求和资源现状,好好掂量一下,做出最划算、最明智的选择。 此外,虽然Kibana跨集群搜索带来了极大的便利性,但在处理跨集群数据权限、数据同步延迟等问题上仍需谨慎对待。在尽情享受技术带来的种种便利和高效服务时,咱们也别忘了时刻关注并确保数据的安全性以及实时更新的重要性。 总结起来,配置Kibana跨集群搜索不仅是一项技术实践,更是对我们如何在复杂数据环境中优化工作流程,提升数据价值的一次有益探索。每一次尝试和挑战都是我们在数据分析道路上不断进步的动力源泉。
2023-02-02 11:29:07
334
风轻云淡
Kibana
...arch 作为存储和索引日志数据的基础服务,为 Kibana 提供了数据支撑。 索引模式(Index Pattern) , 在 Kibana 中,索引模式是用于定义如何对 Elasticsearch 中的一个或多个索引的数据进行解析和搜索的一种模式结构。它会自动检测索引中的字段类型并建立映射关系,使得用户能够在 Kibana 的发现视图、可视化界面以及仪表板中以结构化的方式查询和分析数据。 可视化组件(Visualization) , 在 Kibana 中,可视化组件是一种图形化的数据展示方式,如柱状图、折线图、热力图等。用户可以根据需求选择合适的可视化类型,并通过配置buckets(分桶)、metrics(度量)等参数,将Elasticsearch中的数据转化为直观易懂的图表形式。例如,文章中创建了一个展示不同HTTP方法请求次数的柱状图可视化组件,以便于数据分析人员快速了解各种HTTP请求方法的分布情况。
2023-08-20 14:56:06
336
岁月静好
Kibana
...sticsearch索引滚动更新策略设置不当,导致Kibana无法获取最新的数据。 - Kibana自身配置中的时间筛选条件或仪表板刷新间隔设置不正确。 - 网络延迟或系统资源瓶颈,影响数据传输和处理速度。 3. 示例与排查步骤 示例1:检查Elasticsearch滚动索引配置 假设你的日志数据是通过Logstash写入Elasticsearch并配置了基于时间的滚动索引策略,而Kibana关联的索引模式未能动态更新至最新索引。 yaml Logstash输出到Elasticsearch的配置段落 output { elasticsearch { hosts => ["localhost:9200"] index => "logstash-%{+YYYY.MM.dd}" 其他相关配置... } } 在Kibana中,你需要确保索引模式包含了滚动创建的所有索引,例如logstash-。 示例2:调整Kibana仪表板刷新频率 Kibana仪表板默认的自动刷新间隔为5分钟,若需要实时更新,可以在仪表板编辑界面调整刷新频率。 markdown 在Kibana仪表板编辑模式下 1. 找到右上角的“自动刷新”图标(通常是一个循环箭头) 2. 点击该图标并选择你期望的刷新频率,比如“每秒” 示例3:检查网络与系统资源状况 如果你已经确认上述配置无误,但依然存在实时更新失效的问题,可以尝试监控网络流量以及Elasticsearch和Kibana所在服务器的系统资源(如CPU、内存和磁盘I/O)。过高的负载可能导致数据处理和传输延迟。 4. 解决策略与实践 面对这个问题,我们需要根据实际情况采取相应的措施。如果问题是出在配置上,那就好比是你的Elasticsearch滚动索引策略或者Kibana刷新频率设置有点小打小闹了,这时候咱们就得把这些参数调整一下,调到最合适的节奏。要是遇到性能瓶颈这块硬骨头,那就得从根儿上找解决方案了,比如优化咱系统的资源配置,让它们更合理地分工协作;再不然,就得考虑给咱的硬件设备升个级,换个更强力的装备,或者琢磨琢磨采用那些更高效、更溜的数据处理策略,让数据跑起来跟飞一样。 5. 总结与思考 在实际运维工作中,我们会遇到各种各样的技术难题,如同Kibana仪表板刷新频率异常一样,它们考验着我们的耐心与智慧。只有你真正钻进去,把系统的工作原理摸得门儿清,像侦探一样抽丝剥茧找出问题的根儿,再结合实际业务需求,拿出些接地气、能解决问题的方案来,才能算是把这些强大的工具玩转起来,让它们乖乖为你服务。每一次我们成功解决一个问题,就像是对知识和技术的一次磨砺和淬炼,同时也像是在大数据的世界里打怪升级,这就是推动我们在这一领域不断向前、持续进步的原动力。 以上仅为一种可能的问题解析与解决方案,实践中还可能存在其他复杂因素。因此,我们要始终保持敏锐的洞察力和求知欲,不断探寻未知,以应对更多的挑战。
2023-10-10 23:10:35
277
梦幻星空
Kibana
...搜索栏、时间过滤器、索引模式以及可视化工具。这些工具凑在一起,就成了个超棒的数据分析神器,让我们可以从各种角度来好好研究数据,简直不要太爽! 2.1 使用搜索栏进行基本数据切片 搜索栏是Kibana中最直接的数据切片工具之一。通过输入关键词,你可以快速筛选出符合特定条件的数据。例如,如果你想查看所有状态为“已完成”的订单,只需在搜索栏中输入status:completed即可。 代码示例: json GET /orders/_search { "query": { "match": { "status": "completed" } } } 2.2 利用时间过滤器进行时间切片 时间过滤器允许我们根据时间范围来筛选数据。这对于分析特定时间段内的趋势非常有用。比如,如果你想要查看过去一周内所有的用户登录记录,你可以设置时间过滤器来限定这个范围。 代码示例: json GET /logs/_search { "query": { "range": { "@timestamp": { "gte": "now-7d/d", "lt": "now/d" } } } } 2.3 使用索引模式进行多角度数据切片 索引模式允许你根据不同的字段来创建视图,从而从不同角度观察数据。比如说,你有个用户信息的大台账,里面记录了各种用户的小秘密,比如他们的位置和年龄啥的。那你可以根据这些小秘密,弄出好几个不同的小窗口来看,这样就能更清楚地知道你的用户都分布在哪儿啦! 代码示例: json PUT /users/_mapping { "properties": { "location": { "type": "geo_point" }, "age": { "type": "integer" } } } 2.4 利用可视化工具进行高级数据切片 Kibana的可视化工具(如图表、仪表板)提供了强大的数据可视化能力,使我们可以直观地看到数据之间的关系。比如说,你可以画个饼图来看看各种产品卖得咋样,比例多大;还可以画个时间序列图,看看每天的销售额是涨了还是跌了。 代码示例: 虽然直接通过API创建可视化对象不是最常见的方式,但你可以通过Kibana的界面来设计你的可视化,并将其导出为JSON格式。下面是一个简单的示例,展示了如何通过API创建一个简单的柱状图: json POST /api/saved_objects/visualization { "attributes": { "title": "Sales by Category", "visState": "{\"title\":\"Sales by Category\",\"type\":\"histogram\",\"params\":{\"addTimeMarker\":false,\"addTooltip\":true,\"addLegend\":true,\"addTimeAxis\":true,\"addDistributionBands\":false,\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"yAxis\":{},\"xAxis\":{},\"grid\":{},\"waterfall\":{} },\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{} },{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"category\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"} }],\"listeners\":{} }", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"sales\",\"filter\":[],\"highlight\":{},\"query\":{\"query_string\":{\"query\":\"\",\"analyze_wildcard\":true} }}" } }, "references": [], "migrationVersion": {}, "updated_at": "2023-09-28T00:00:00.000Z" } 3. 思考与实践 在实际操作中,数据切片并不仅仅是简单的过滤和查询,它还涉及到如何有效地组织和呈现数据。这就得咱们不停地试各种招儿,比如说用聚合函数搞更复杂的统计分析,或者搬出机器学习算法来预测未来的走向。每一次尝试都可能带来新的发现,让数据背后的故事更加生动有趣。 4. 结语 数据切片是数据分析中不可或缺的一部分,它帮助我们在海量数据中寻找有价值的信息。Kibana这家伙可真不赖,简直就是个数据分析神器,有了它,我们实现目标简直易如反掌!希望本文能为你提供一些灵感和思路,让你在数据分析的路上越走越远! --- 以上就是本次关于如何在Kibana中实现数据切片的技术分享,希望能对你有所帮助。如果你有任何疑问或想了解更多内容,请随时留言讨论!
2024-10-28 15:42:51
42
飞鸟与鱼
Apache Lucene
...的全文搜索库,它在搜索引擎领域有着举足轻重的地位。话说在咱们聊Lucene之前,我得先吐槽一下最近在开发中遇到的一个超级烦人的bug——就是那个“javalangNullPointerException: null”。简直让人抓狂啊!这个异常常常会出现在我们的代码中,特别是在处理复杂数据结构时。那么,让我们一边学习如何优雅地使用Lucene,一边看看如何巧妙地避开NullPointerException吧! 二、Lucene的魅力所在 从概念到实践 首先,让我们来了解一下Lucene的基本概念。Lucene可真是个厉害的角色,它是个超级能打的文本搜索小能手,给咱们提供了全套的工具,不管是建索引、搜东西还是让搜索结果更给力,都能搞定!简单来说,Lucene就像是你电脑上的超级搜索引擎,但它的能力远不止于此。 2.1 创建你的第一个索引 在开始之前,你需要确保已经在你的项目中引入了Lucene的相关依赖。接下来,让我们通过一些简单的步骤来创建一个基本的索引: java import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.store.Directory; import org.apache.lucene.store.RAMDirectory; public class SimpleIndexer { public static void main(String[] args) throws Exception { // 创建内存中的目录,用于存储索引 Directory directory = new RAMDirectory(); // 创建索引配置 IndexWriterConfig config = new IndexWriterConfig(new StandardAnalyzer()); // 创建索引写入器 IndexWriter indexWriter = new IndexWriter(directory, config); // 创建文档对象 Document doc = new Document(); doc.add(new Field("content", "Hello Lucene!", Field.Store.YES, Field.Index.ANALYZED)); // 添加文档到索引 indexWriter.addDocument(doc); // 关闭索引写入器 indexWriter.close(); } } 在这个例子中,我们首先创建了一个内存中的目录(RAMDirectory),这是为了方便演示。接着,我们定义了索引配置,并使用StandardAnalyzer对文本进行分析。最后,我们创建了一个文档,并将它添加到了索引中。是不是很简单呢? 2.2 解决NullPointerException:预防胜于治疗 现在,让我们回到那个恼人的NullPointerException问题上。在用Lucene做索引的时候,经常会被空指针异常坑到,特别是当你试图去访问那些还没被初始化的对象或者字段时。为了避免这种情况,我们需要养成良好的编程习惯,比如: - 检查null值:在访问任何对象前,先检查是否为null。 - 初始化变量:确保所有对象在使用前都被正确初始化。 - 使用Optional类:Java 8引入的Optional类可以帮助我们更好地处理可能为空的情况。 例如,假设我们在处理索引文档时遇到了一个可能为空的字段,我们可以这样处理: java // 假设我们有一个可能为空的内容字段 String content = getContent(); // 这里可能会返回null if (content != null) { doc.add(new Field("content", content, Field.Store.YES, Field.Index.ANALYZED)); } else { System.out.println("内容字段为空!"); } 三、深入探索 Lucene的高级特性 3.1 搜索:不仅仅是查找 除了创建索引外,Lucene还提供了强大的搜索功能。让我们来看一个简单的搜索示例: java import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.queryparser.classic.QueryParser; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.TopDocs; import org.apache.lucene.store.Directory; public class SimpleSearcher { public static void main(String[] args) throws Exception { Directory directory = new RAMDirectory(); IndexWriterConfig config = new IndexWriterConfig(new StandardAnalyzer()); IndexWriter indexWriter = new IndexWriter(directory, config); Document doc = new Document(); doc.add(new Field("content", "Hello Lucene!", Field.Store.YES, Field.Index.ANALYZED)); indexWriter.addDocument(doc); indexWriter.close(); DirectoryReader reader = DirectoryReader.open(directory); IndexSearcher searcher = new IndexSearcher(reader); QueryParser parser = new QueryParser("content", new StandardAnalyzer()); Query query = parser.parse("lucene"); TopDocs results = searcher.search(query, 10); for (ScoreDoc scoreDoc : results.scoreDocs) { System.out.println(searcher.doc(scoreDoc.doc).get("content")); } reader.close(); } } 这段代码展示了如何使用QueryParser解析查询字符串,并使用IndexSearcher执行搜索操作。通过这种方式,我们可以轻松地从索引中检索出相关的文档。 3.2 高级搜索技巧:优化你的查询 当你开始构建更复杂的搜索逻辑时,Lucene提供了许多高级功能来帮助你优化搜索结果。比如说,你可以用布尔查询把好几个搜索条件拼在一起,或者用模糊匹配让搜索变得更灵活一点。这样找东西就方便多了! java import org.apache.lucene.index.Term; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.FuzzyQuery; // 构建布尔查询 BooleanQuery booleanQuery = new BooleanQuery(); booleanQuery.add(new TermQuery(new Term("content", "hello")), BooleanClause.Occur.MUST); booleanQuery.add(new FuzzyQuery(new Term("content", "lucen")), BooleanClause.Occur.SHOULD); TopDocs searchResults = searcher.search(booleanQuery, 10); 在这个例子中,我们创建了一个布尔查询,其中包含两个子查询:一个是必须满足的精确匹配查询,另一个是可选的模糊匹配查询。这种组合可以显著提升搜索的准确性和相关性。 四、结语 享受编码的乐趣 通过这篇文章,我们不仅学习了如何使用Apache Lucene来创建和搜索索引,还一起探讨了如何有效地避免NullPointerException。希望这些示例代码和技巧能对你有所帮助。记住,编程不仅仅是一门技术,更是一种艺术。尽情享受编程的乐趣吧,一路探索和学习,你会发现自己的收获多到让人惊喜!如果你有任何问题或想法,欢迎随时与我交流! --- 以上就是关于Apache Lucene与javalangNullPointerException: null的讨论。希望能通过这篇文章点燃你对Lucene的热情,让你在实际开发中游刃有余,玩得更嗨!让我们一起继续探索更多有趣的技术吧!
2024-10-16 15:36:29
88
岁月静好
转载文章
...efined 创建新模式或表时使用的默认字符集,并且没有定义字符集 character-set-server= The default authentication plugin to be used when connecting to the server 连接到服务器时使用的默认身份验证插件 default_authentication_plugin=caching_sha2_password The default storage engine that will be used when create new tables when 当创建新表时将使用的默认存储引擎 default-storage-engine=INNODB Set the SQL mode to strict 将SQL模式设置为strict sql-mode="STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION" General and Slow logging. 一般和缓慢的日志。 log-output=NONE general-log=0 general_log_file="DESKTOP-NF9QETB.log" slow-query-log=0 slow_query_log_file="DESKTOP-NF9QETB-slow.log" long_query_time=10 Binary Logging. 二进制日志。 log-bin Error Logging. 错误日志记录。 log-error="DESKTOP-NF9QETB.err" Server Id. server-id=1 Indicates how table and database names are stored on disk and used in MySQL. 指示表名和数据库名如何存储在磁盘上并在MySQL中使用。 Value = 0: Table and database names are stored on disk using the lettercase specified in the CREATE TABLE or CREATE DATABASE statement. Name comparisons are case sensitive. You should not set this variable to 0 if you are running MySQL on a system that has case-insensitive file names (such as Windows or macOS). Value = 0:表名和数据库名使用CREATE Table或CREATE database语句中指定的lettercase存储在磁盘上。名称比较区分大小写。如果您在一个具有不区分大小写文件名(如Windows或macOS)的系统上运行MySQL,则不应将该变量设置为0。 Value = 1: Table names are stored in lowercase on disk and name comparisons are not case-sensitive. MySQL converts all table names to lowercase on storage and lookup. This behavior also applies to database names and table aliases. 表名以小写存储在磁盘上,并且名称比较不区分大小写。MySQL在存储和查找时将所有表名转换为小写。此行为也适用于数据库名称和表别名。 Value = 3, Table and database names are stored on disk using the lettercase specified in the CREATE TABLE or CREATE DATABASE statement, but MySQL converts them to lowercase on lookup. Name comparisons are not case sensitive. This works only on file systems that are not case-sensitive! InnoDB table names and view names are stored in lowercase, as for Value = 1.表名和数据库名使用CREATE Table或CREATE database语句中指定的lettercase存储在磁盘上,但是MySQL在查找时将它们转换为小写。名称比较不区分大小写。这只适用于不区分大小写的文件系统!InnoDB表名和视图名以小写存储,Value = 1。 NOTE: lower_case_table_names can only be configured when initializing the server. Changing the lower_case_table_names setting after the server is initialized is prohibited. lower_case_table_names=1 Secure File Priv. 权限安全文件 secure-file-priv="C:/ProgramData/MySQL/MySQL Server 8.0/Uploads" The maximum amount of concurrent sessions the MySQL server will allow. One of these connections will be reserved for a user with SUPER privileges to allow the administrator to login even if the connection limit has been reached. MySQL服务器允许的最大并发会话量。这些连接中的一个将保留给具有超级特权的用户,以便允许管理员登录,即使已经达到连接限制。 max_connections=151 The number of open tables for all threads. Increasing this value increases the number of file descriptors that mysqld requires. Therefore you have to make sure to set the amount of open files allowed to at least 4096 in the variable "open-files-limit" in 为所有线程打开的表的数量。增加这个值会增加mysqld需要的文件描述符的数量。因此,您必须确保在[mysqld_safe]节中的变量“open-files-limit”中将允许打开的文件数量至少设置为4096 section [mysqld_safe] table_open_cache=2000 Maximum size for internal (in-memory) temporary tables. If a table grows larger than this value, it is automatically converted to disk based table This limitation is for a single table. There can be many of them. 内部(内存)临时表的最大大小。如果一个表比这个值大,那么它将自动转换为基于磁盘的表。可以有很多。 tmp_table_size=94M How many threads we should keep in a cache for reuse. When a client disconnects, the client's threads are put in the cache if there aren't more than thread_cache_size threads from before. This greatly reduces the amount of thread creations needed if you have a lot of new connections. (Normally this doesn't give a notable performance improvement if you have a good thread implementation.) 我们应该在缓存中保留多少线程以供重用。当客户机断开连接时,如果之前的线程数不超过thread_cache_size,则将客户机的线程放入缓存。如果您有很多新连接,这将大大减少所需的线程创建量(通常,如果您有一个良好的线程实现,这不会带来显著的性能改进)。 thread_cache_size=10 MyISAM Specific options The maximum size of the temporary file MySQL is allowed to use while recreating the index (during REPAIR, ALTER TABLE or LOAD DATA INFILE. If the file-size would be bigger than this, the index will be created through the key cache (which is slower). MySQL允许在重新创建索引时(在修复、修改表或加载数据时)使用临时文件的最大大小。如果文件大小大于这个值,那么索引将通过键缓存创建(这比较慢)。 myisam_max_sort_file_size=100G If the temporary file used for fast index creation would be bigger than using the key cache by the amount specified here, then prefer the key cache method. This is mainly used to force long character keys in large tables to use the slower key cache method to create the index. myisam_sort_buffer_size=179M Size of the Key Buffer, used to cache index blocks for MyISAM tables. Do not set it larger than 30% of your available memory, as some memory is also required by the OS to cache rows. Even if you're not using MyISAM tables, you should still set it to 8-64M as it will also be used for internal temporary disk tables. 如果用于快速创建索引的临时文件比这里指定的使用键缓存的文件大,则首选键缓存方法。这主要用于强制大型表中的长字符键使用较慢的键缓存方法来创建索引。 key_buffer_size=8M Size of the buffer used for doing full table scans of MyISAM tables. Allocated per thread, if a full scan is needed. 用于对MyISAM表执行全表扫描的缓冲区的大小。如果需要完整的扫描,则为每个线程分配。 read_buffer_size=256K read_rnd_buffer_size=512K INNODB Specific options INNODB特定选项 innodb_data_home_dir= Use this option if you have a MySQL server with InnoDB support enabled but you do not plan to use it. This will save memory and disk space and speed up some things. 如果您启用了一个支持InnoDB的MySQL服务器,但是您不打算使用它,那么可以使用这个选项。这将节省内存和磁盘空间,并加快一些事情。skip-innodb skip-innodb If set to 1, InnoDB will flush (fsync) the transaction logs to the disk at each commit, which offers full ACID behavior. If you are willing to compromise this safety, and you are running small transactions, you may set this to 0 or 2 to reduce disk I/O to the logs. Value 0 means that the log is only written to the log file and the log file flushed to disk approximately once per second. Value 2 means the log is written to the log file at each commit, but the log file is only flushed to disk approximately once per second. 如果设置为1,InnoDB将在每次提交时将事务日志刷新(fsync)到磁盘,这将提供完整的ACID行为。如果您愿意牺牲这种安全性,并且正在运行小型事务,您可以将其设置为0或2,以将磁盘I/O减少到日志。值0表示日志仅写入日志文件,日志文件大约每秒刷新一次磁盘。值2表示日志在每次提交时写入日志文件,但是日志文件大约每秒只刷新一次磁盘。 innodb_flush_log_at_trx_commit=1 The size of the buffer InnoDB uses for buffering log data. As soon as it is full, InnoDB will have to flush it to disk. As it is flushed once per second anyway, it does not make sense to have it very large (even with long transactions).InnoDB用于缓冲日志数据的缓冲区大小。一旦它满了,InnoDB就必须将它刷新到磁盘。由于它无论如何每秒刷新一次,所以将它设置为非常大的值是没有意义的(即使是长事务)。 innodb_log_buffer_size=5M InnoDB, unlike MyISAM, uses a buffer pool to cache both indexes and row data. The bigger you set this the less disk I/O is needed to access data in tables. On a dedicated database server you may set this parameter up to 80% of the machine physical memory size. Do not set it too large, though, because competition of the physical memory may cause paging in the operating system. Note that on 32bit systems you might be limited to 2-3.5G of user level memory per process, so do not set it too high. 与MyISAM不同,InnoDB使用缓冲池来缓存索引和行数据。设置的值越大,访问表中的数据所需的磁盘I/O就越少。在专用数据库服务器上,可以将该参数设置为机器物理内存大小的80%。但是,不要将它设置得太大,因为物理内存的竞争可能会导致操作系统中的分页。注意,在32位系统上,每个进程的用户级内存可能被限制在2-3.5G,所以不要设置得太高。 innodb_buffer_pool_size=20M Size of each log file in a log group. You should set the combined size of log files to about 25%-100% of your buffer pool size to avoid unneeded buffer pool flush activity on log file overwrite. However, note that a larger logfile size will increase the time needed for the recovery process. 日志组中每个日志文件的大小。您应该将日志文件的合并大小设置为缓冲池大小的25%-100%,以避免在覆盖日志文件时出现不必要的缓冲池刷新活动。但是,请注意,较大的日志文件大小将增加恢复过程所需的时间。 innodb_log_file_size=48M Number of threads allowed inside the InnoDB kernel. The optimal value depends highly on the application, hardware as well as the OS scheduler properties. A too high value may lead to thread thrashing. InnoDB内核中允许的线程数。最优值在很大程度上取决于应用程序、硬件以及OS调度程序属性。过高的值可能导致线程抖动。 innodb_thread_concurrency=9 The increment size (in MB) for extending the size of an auto-extend InnoDB system tablespace file when it becomes full. 增量大小(以MB为单位),用于在表空间满时扩展自动扩展的InnoDB系统表空间文件的大小。 innodb_autoextend_increment=128 The number of regions that the InnoDB buffer pool is divided into. For systems with buffer pools in the multi-gigabyte range, dividing the buffer pool into separate instances can improve concurrency, by reducing contention as different threads read and write to cached pages. InnoDB缓冲池划分的区域数。对于具有多gb缓冲池的系统,将缓冲池划分为单独的实例可以提高并发性,因为不同的线程对缓存页面的读写会减少争用。 innodb_buffer_pool_instances=8 Determines the number of threads that can enter InnoDB concurrently. 确定可以同时进入InnoDB的线程数 innodb_concurrency_tickets=5000 Specifies how long in milliseconds (ms) a block inserted into the old sublist must stay there after its first access before it can be moved to the new sublist. 指定插入到旧子列表中的块必须在第一次访问之后停留多长时间(毫秒),然后才能移动到新子列表。 innodb_old_blocks_time=1000 It specifies the maximum number of .ibd files that MySQL can keep open at one time. The minimum value is 10. 它指定MySQL一次可以打开的.ibd文件的最大数量。最小值是10。 innodb_open_files=300 When this variable is enabled, InnoDB updates statistics during metadata statements. 当启用此变量时,InnoDB会在元数据语句期间更新统计信息。 innodb_stats_on_metadata=0 When innodb_file_per_table is enabled (the default in 5.6.6 and higher), InnoDB stores the data and indexes for each newly created table in a separate .ibd file, rather than in the system tablespace. 当启用innodb_file_per_table(5.6.6或更高版本的默认值)时,InnoDB将每个新创建的表的数据和索引存储在单独的.ibd文件中,而不是系统表空间中。 innodb_file_per_table=1 Use the following list of values: 0 for crc32, 1 for strict_crc32, 2 for innodb, 3 for strict_innodb, 4 for none, 5 for strict_none. 使用以下值列表:0表示crc32, 1表示strict_crc32, 2表示innodb, 3表示strict_innodb, 4表示none, 5表示strict_none。 innodb_checksum_algorithm=0 The number of outstanding connection requests MySQL can have. This option is useful when the main MySQL thread gets many connection requests in a very short time. It then takes some time (although very little) for the main thread to check the connection and start a new thread. The back_log value indicates how many requests can be stacked during this short time before MySQL momentarily stops answering new requests. You need to increase this only if you expect a large number of connections in a short period of time. MySQL可以有多少未完成连接请求。当MySQL主线程在很短的时间内收到许多连接请求时,这个选项非常有用。然后,主线程需要一些时间(尽管很少)来检查连接并启动一个新线程。back_log值表示在MySQL暂时停止响应新请求之前的短时间内可以堆多少个请求。只有当您预期在短时间内会有大量连接时,才需要增加这个值。 back_log=80 If this is set to a nonzero value, all tables are closed every flush_time seconds to free up resources and synchronize unflushed data to disk. This option is best used only on systems with minimal resources. 如果将该值设置为非零值,则每隔flush_time秒关闭所有表,以释放资源并将未刷新的数据同步到磁盘。这个选项最好只在资源最少的系统上使用。 flush_time=0 The minimum size of the buffer that is used for plain index scans, range index scans, and joins that do not use 用于普通索引扫描、范围索引扫描和不使用索引执行全表扫描的连接的缓冲区的最小大小。 indexes and thus perform full table scans. join_buffer_size=200M The maximum size of one packet or any generated or intermediate string, or any parameter sent by the mysql_stmt_send_long_data() C API function. 由mysql_stmt_send_long_data() C API函数发送的一个包或任何生成的或中间字符串或任何参数的最大大小 max_allowed_packet=500M If more than this many successive connection requests from a host are interrupted without a successful connection, the server blocks that host from performing further connections. 如果在没有成功连接的情况下中断了来自主机的多个连续连接请求,则服务器将阻止主机执行进一步的连接。 max_connect_errors=100 Changes the number of file descriptors available to mysqld. You should try increasing the value of this option if mysqld gives you the error "Too many open files". 更改mysqld可用的文件描述符的数量。如果mysqld给您的错误是“打开的文件太多”,您应该尝试增加这个选项的值。 open_files_limit=4161 If you see many sort_merge_passes per second in SHOW GLOBAL STATUS output, you can consider increasing the sort_buffer_size value to speed up ORDER BY or GROUP BY operations that cannot be improved with query optimization or improved indexing. 如果在SHOW GLOBAL STATUS输出中每秒看到许多sort_merge_passes,可以考虑增加sort_buffer_size值,以加快ORDER BY或GROUP BY操作的速度,这些操作无法通过查询优化或改进索引来改进。 sort_buffer_size=1M The number of table definitions (from .frm files) that can be stored in the definition cache. If you use a large number of tables, you can create a large table definition cache to speed up opening of tables. The table definition cache takes less space and does not use file descriptors, unlike the normal table cache. The minimum and default values are both 400. 可以存储在定义缓存中的表定义的数量(来自.frm文件)。如果使用大量表,可以创建一个大型表定义缓存来加速表的打开。与普通的表缓存不同,表定义缓存占用更少的空间,并且不使用文件描述符。最小值和默认值都是400。 table_definition_cache=1400 Specify the maximum size of a row-based binary log event, in bytes. Rows are grouped into events smaller than this size if possible. The value should be a multiple of 256. 指定基于行的二进制日志事件的最大大小,单位为字节。如果可能,将行分组为小于此大小的事件。这个值应该是256的倍数。 binlog_row_event_max_size=8K If the value of this variable is greater than 0, a replication slave synchronizes its master.info file to disk. (using fdatasync()) after every sync_master_info events. 如果该变量的值大于0,则复制奴隶将其主.info文件同步到磁盘。(在每个sync_master_info事件之后使用fdatasync())。 sync_master_info=10000 If the value of this variable is greater than 0, the MySQL server synchronizes its relay log to disk. (using fdatasync()) after every sync_relay_log writes to the relay log. 如果这个变量的值大于0,MySQL服务器将其中继日志同步到磁盘。(在每个sync_relay_log写入到中继日志之后使用fdatasync())。 sync_relay_log=10000 If the value of this variable is greater than 0, a replication slave synchronizes its relay-log.info file to disk. (using fdatasync()) after every sync_relay_log_info transactions. 如果该变量的值大于0,则复制奴隶将其中继日志.info文件同步到磁盘。(在每个sync_relay_log_info事务之后使用fdatasync())。 sync_relay_log_info=10000 Load mysql plugins at start."plugin_x ; plugin_y". 开始时加载mysql插件。“plugin_x;plugin_y” plugin_load The TCP/IP Port the MySQL Server X Protocol will listen on. MySQL服务器X协议将监听TCP/IP端口。 loose_mysqlx_port=33060 本篇文章为转载内容。原文链接:https://blog.csdn.net/mywpython/article/details/89499852。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-10-08 09:56:02
129
转载
转载文章
...et" href="index.css"><script src="index.js"></script></head><body><div id="pop_star"><div id="targetScore">Target Score : 2000</div><div id="nowScore">Current Score : 0</div><div id="selectScore">0 blocks 0 scores</div></div></body></html> css / 常用页面初始化 /{margin:0;padding:0;}html,body{height: 100%;width: 100%;}pop_star{height: 100%;width: 500px;margin: 0 auto;background: url("./pic/background.png");position: relative; /父元素,为了使之后的子元素都相对于他进行定位,此处设为relative/color:white;background-size: cover; /使背景图片保持比例覆盖整个背景区域/}/ 以下三个元素为现实面板,其样式相同 /targetScore{width: 100%;height: 50px;position: relative;line-height: 50px;text-align: center;font-size: 20px;background-size: cover;}nowScore{width: 100%;height: 50px;position: relative;line-height: 50px;text-align: center;font-size: 20px;background-size: cover;}selectScore{width: 100%;height: 50px;position: relative;line-height: 50px;text-align: center;font-size: 20px;background-size: cover;opacity:0;/不透明度/} js var table; //游戏桌面var squareWidth = 50; //方块宽高var boardWidth = 10; //行列数var squareSet = []; //方块信息集合(二维数组)每个元素保存该方块的全部信息function createSquare(value,row,col){ //创建小方块,传入参数为颜色、行、列,初始化时使用。var temp = document.createElement('div'); //创建div dom对象temp.style.height = squareWidth + "px";temp.style.width = squareWidth + "px";temp.style.position = "absolute"; //相对于背景绝对定位temp.num = value;temp.col = col;temp.row = row;return temp; //返回这个创建出来的对象}function refresh(){ //重绘画板,每次鼠标点击后刷新for(var i = 0 ; i < squareSet.length ; i ++){for(var j = 0 ; j < squareSet[i].length ; j ++){squareSet[i][j].style.backgroundImage = "url(./pic/" + squareSet[i][j].num + ".png)"squareSet[i][j].style.left = squareSet[i][j].col squareWidth + "px"; // 别忘了加"px"squareSet[i][j].style.bottom = squareSet[i][j].row squareWidth + "px";} }}function init(){ // JS调用入口table = document.getElementById('pop_star'); // 获取到最外层的父元素作为桌面for(var i = 0 ; i < boardWidth; i ++){squareSet[i] = new Array(); //二维数组的创建,对每一个元素new Array()创建新数组for(var j = 0 ; j < boardWidth ; j ++){var square = createSquare(Math.floor(Math.random() 5) , i , j);squareSet[i][j] = square; //必须将新创建的方块放回到数组中table.appendChild(square); //需要将创建的新元素添加到桌面上} }refresh(); //每次页面内容发生变化需要重绘页面}window.onload = function(){init();} // window.onload 保证了在页面全部加载完毕后再执行JS代码 效果 第二阶段:鼠标选中后,闪烁 只有JavaScript需要修改 var table; //游戏桌面var squareWidth = 50; //方块宽高var boardWidth = 10; //行列数var squareSet = []; //方块信息集合(二维数组)每个元素保存该方块的全部信息var baseScore = 5; //第一块的分数var stepScore = 10; //每多一块的累加分数var totalScore = 0; //当前总分var targetScore = 1500; //目标分var choose = []; //选中的连通小方块var timer = null; //闪烁定时器var flag = true; //锁,防止点击事件中响应其他点击或移入时间var tempSquare = null; //临时方块function refresh(){for (var i = 0; i < squareSet.length; i++) {for (var j = 0; j < squareSet[i].length; j++) {squareSet[i][j].style.background="url(pic/"+squareSet[i][j].num+".png)"squareSet[i][j].style.left=squareSet[i][j].colsquareWidth+"px";squareSet[i][j].style.bottom=squareSet[i][j].rowsquareWidth+"px";} }}function createSquare(value,row,col){ //创建小方块,传入参数为颜色、行、列,初始化时使用。var temp = document.createElement('div'); //创建div dom对象temp.style.height = squareWidth + "px";temp.style.width = squareWidth + "px";temp.style.position = "absolute"; //相对于背景绝对定位temp.num = value;temp.col = col;temp.row = row;return temp; //返回这个创建出来的对象}function goBack(){ //还原样式if(timer != null){ //清空计时器clearInterval(timer);}for(var i = 0 ; i < squareSet.length ; i ++){for(var j = 0 ; j < squareSet[i].length ; j ++){squareSet[i][j].style.border = "0px solid white";squareSet[i][j].style.transform = "scale(0.95)";} }}function checkLinked(square , arr){ // 递归连通图算法arr.push(square); // 将当前方块放入选中数组中// check leftif( square.col > 0 && //未到边界squareSet[square.row][square.col - 1].num == square.num && //颜色相同arr.indexOf(squareSet[square.row][square.col - 1]) == -1) { //不在choose中,避免循环判断checkLinked(squareSet[square.row][square.col - 1] , arr);}// check rightif( square.col < boardWidth - 1 &&squareSet[square.row][square.col + 1].num == square.num &&arr.indexOf(squareSet[square.row][square.col + 1]) == -1) {checkLinked(squareSet[square.row][square.col + 1] , arr);}// check upif( square.row < boardWidth - 1 &&squareSet[square.row + 1][square.col].num == square.num &&arr.indexOf(squareSet[square.row + 1][square.col]) == -1) {checkLinked(squareSet[square.row + 1][square.col] , arr);}// check downif( square.row > 0 &&squareSet[square.row - 1][square.col].num == square.num &&arr.indexOf(squareSet[square.row - 1][square.col]) == -1) {checkLinked(squareSet[square.row - 1][square.col] , arr);} }function flicker(arr){ // 选中连通的小方块可以闪烁var num = 0;timer = setInterval(function(){for(var i = 0 ; i < arr.length ; i ++){arr[i].style.border = "3px solid BFEFFF";//有个框arr[i].style.transform = "scale(" + (0.9 + (0.05 Math.pow(-1 , num))) + ")";//一闪一闪}num ++; // 注意这里所采用的数学技巧,仍然使用transform:scale(val)来进行缩放。},300);//闪烁的时间}function mouseOver(obj){ //鼠标移入区域响应// 还原所有样式goBack();// 检查相邻choose = [];checkLinked(obj , choose);// 闪烁flicker(choose);// 显示分数selectScore();}function init(){ // JS调用入口table = document.getElementById('pop_star'); // 获取到最外层的父元素作为桌面document.getElementById('targetScore').innerHTML = "Target Score : " + targetScore; //显示目标分数用innerHTML// 循环初始化星星区域for(var i = 0 ; i < boardWidth ; i ++){squareSet[i] = new Array(); //二维数组的创建,对每一个元素new Array()创建新数组for(var j = 0 ; j < boardWidth ; j ++){var square = createSquare(Math.floor(Math.random() 5) , i , j);// 鼠标移入事件square.onmouseover = function(){mouseOver(this);}squareSet[i][j] = square; //必须将新创建的方块放回到数组中table.appendChild(square); //需要将创建的新元素添加到桌面上} }refresh(); //每次页面内容发生变化需要重绘页面}window.onload = function(){init();} // window.onload 保证了在页面全部加载完毕后再执行JS代码 效果2.1 加入这段代码,便会计算闪烁方块得分 function selectScore(){ //可以显示当前选中小方块的得分var score = 0;for(var i = 0 ; i < choose.length ; i ++){score += (baseScore + i stepScore);}document.getElementById('selectScore').innerHTML = choose.length + " blocks " + score + " points";document.getElementById('selectScore').style.opacity = 1;// 设置时间间隔1秒后显示消失的过渡动画setTimeout(function(){document.getElementById('selectScore').style.opacity = 0;document.getElementById('selectScore').style.transition = "opacity 1s";},1000);} 完整代码为: var table; //游戏桌面var squareWidth = 50; //方块宽高var boardWidth = 10; //行列数var squareSet = []; //方块信息集合(二维数组)每个元素保存该方块的全部信息var baseScore = 5; //第一块的分数var stepScore = 10; //每多一块的累加分数var totalScore = 0; //当前总分var targetScore = 1500; //目标分var choose = []; //选中的连通小方块var timer = null; //闪烁定时器var flag = true; //锁,防止点击事件中响应其他点击或移入时间var tempSquare = null; //临时方块function refresh(){for (var i = 0; i < squareSet.length; i++) {for (var j = 0; j < squareSet[i].length; j++) {squareSet[i][j].style.background="url(pic/"+squareSet[i][j].num+".png)"squareSet[i][j].style.left=squareSet[i][j].colsquareWidth+"px";squareSet[i][j].style.bottom=squareSet[i][j].rowsquareWidth+"px";} }}function createSquare(value,row,col){ //创建小方块,传入参数为颜色、行、列,初始化时使用。var temp = document.createElement('div'); //创建div dom对象temp.style.height = squareWidth + "px";temp.style.width = squareWidth + "px";temp.style.position = "absolute"; //相对于背景绝对定位temp.num = value;temp.col = col;temp.row = row;return temp; //返回这个创建出来的对象}function goBack(){ //还原样式if(timer != null){ //清空计时器clearInterval(timer);}for(var i = 0 ; i < squareSet.length ; i ++){for(var j = 0 ; j < squareSet[i].length ; j ++){squareSet[i][j].style.border = "0px solid white";squareSet[i][j].style.transform = "scale(0.95)";} }}function checkLinked(square , arr){ // 递归连通图算法arr.push(square); // 将当前方块放入选中数组中// check leftif( square.col > 0 && //未到边界squareSet[square.row][square.col - 1].num == square.num && //颜色相同arr.indexOf(squareSet[square.row][square.col - 1]) == -1) { //不在choose中,避免循环判断checkLinked(squareSet[square.row][square.col - 1] , arr);}// check rightif( square.col < boardWidth - 1 &&squareSet[square.row][square.col + 1].num == square.num &&arr.indexOf(squareSet[square.row][square.col + 1]) == -1) {checkLinked(squareSet[square.row][square.col + 1] , arr);}// check upif( square.row < boardWidth - 1 &&squareSet[square.row + 1][square.col].num == square.num &&arr.indexOf(squareSet[square.row + 1][square.col]) == -1) {checkLinked(squareSet[square.row + 1][square.col] , arr);}// check downif( square.row > 0 &&squareSet[square.row - 1][square.col].num == square.num &&arr.indexOf(squareSet[square.row - 1][square.col]) == -1) {checkLinked(squareSet[square.row - 1][square.col] , arr);} }function flicker(arr){ // 选中连通的小方块可以闪烁var num = 0;timer = setInterval(function(){for(var i = 0 ; i < arr.length ; i ++){arr[i].style.border = "3px solid BFEFFF";//有个框arr[i].style.transform = "scale(" + (0.9 + (0.05 Math.pow(-1 , num))) + ")";//一闪一闪}num ++; // 注意这里所采用的数学技巧,仍然使用transform:scale(val)来进行缩放。},300);//闪烁的时间}function selectScore(){ //可以显示当前选中小方块的得分var score = 0;for(var i = 0 ; i < choose.length ; i ++){score += (baseScore + i stepScore);}document.getElementById('selectScore').innerHTML = choose.length + " blocks " + score + " points";document.getElementById('selectScore').style.opacity = 1;// 设置时间间隔1秒后显示消失的过渡动画setTimeout(function(){document.getElementById('selectScore').style.opacity = 0;document.getElementById('selectScore').style.transition = "opacity 1s";},1000);}function mouseOver(obj){ //鼠标移入区域响应// 还原所有样式goBack();// 检查相邻choose = [];checkLinked(obj , choose);// 闪烁flicker(choose);// 显示分数selectScore();}function init(){ // JS调用入口table = document.getElementById('pop_star'); // 获取到最外层的父元素作为桌面document.getElementById('targetScore').innerHTML = "Target Score : " + targetScore; //显示目标分数用innerHTML// 循环初始化星星区域for(var i = 0 ; i < boardWidth ; i ++){squareSet[i] = new Array(); //二维数组的创建,对每一个元素new Array()创建新数组for(var j = 0 ; j < boardWidth ; j ++){var square = createSquare(Math.floor(Math.random() 5) , i , j);// 鼠标移入事件square.onmouseover = function(){mouseOver(this);}squareSet[i][j] = square; //必须将新创建的方块放回到数组中table.appendChild(square); //需要将创建的新元素添加到桌面上} }refresh(); //每次页面内容发生变化需要重绘页面}window.onload = function(){init();} // window.onload 保证了在页面全部加载完毕后再执行JS代码 效果2.2 第三阶段:消灭星星(只消灭一次) 只消除选中的星星,但是不会掉下来。 在function init(){}里面添加以下代码: // 鼠标点击事件square.onclick = function(){//为移除增加一个延迟动画,为了防止闭包,这里采用立即执行函数for(var i = 0 ; i < choose.length ; i ++){(function(i){setTimeout(function(){squareSet[choose[i].row][choose[i].col] = null; //为状态数组置空table.removeChild(choose[i]); //将其从桌面上移除} , i 100);})(i);} } 效果 使得星星移动(原作者这里出现错误) function move(){//纵向下落,采用快慢指针算法for(var i = 0 ; i < boardWidth ; i ++){var pointer = 0; //慢指针for(var j = 0 ; j < boardWidth ; j ++){if(squareSet[j][i] != null){ //按行遍历if(pointer != j){ //快慢指针不同步说明中间有空元素squareSet[pointer][i] = squareSet[j][i]; //慢指针设成快指针元素squareSet[j][i].row = pointer;squareSet[j][i] = null; //快指针处置空}pointer ++; //该行非空时慢指针增加} }} 完整代码如下: var table; //游戏桌面var squareWidth = 50; //方块宽高var boardWidth = 10; //行列数var squareSet = []; //方块信息集合(二维数组)每个元素保存该方块的全部信息var baseScore = 5; //第一块的分数var stepScore = 10; //每多一块的累加分数var totalScore = 0; //当前总分var targetScore = 1500; //目标分var choose = []; //选中的连通小方块var timer = null; //闪烁定时器var flag = true; //锁,防止点击事件中响应其他点击或移入时间var tempSquare = null; //临时方块function refresh(){for (var i = 0; i < squareSet.length; i++) {for (var j = 0; j < squareSet[i].length; j++) {squareSet[i][j].style.background="url(pic/"+squareSet[i][j].num+".png)"squareSet[i][j].style.left=squareSet[i][j].colsquareWidth+"px";squareSet[i][j].style.bottom=squareSet[i][j].rowsquareWidth+"px";} }}function createSquare(value,row,col){ //创建小方块,传入参数为颜色、行、列,初始化时使用。var temp = document.createElement('div'); //创建div dom对象temp.style.height = squareWidth + "px";temp.style.width = squareWidth + "px";temp.style.position = "absolute"; //相对于背景绝对定位temp.num = value;temp.col = col;temp.row = row;return temp; //返回这个创建出来的对象}function goBack(){ //还原样式if(timer != null){ //清空计时器clearInterval(timer);}for(var i = 0 ; i < squareSet.length ; i ++){for(var j = 0 ; j < squareSet[i].length ; j ++){squareSet[i][j].style.border = "0px solid white";squareSet[i][j].style.transform = "scale(0.95)";} }}function checkLinked(square , arr){ // 递归连通图算法arr.push(square); // 将当前方块放入选中数组中// check leftif( square.col > 0 && //未到边界squareSet[square.row][square.col - 1].num == square.num && //颜色相同arr.indexOf(squareSet[square.row][square.col - 1]) == -1) { //不在choose中,避免循环判断checkLinked(squareSet[square.row][square.col - 1] , arr);}// check rightif( square.col < boardWidth - 1 &&squareSet[square.row][square.col + 1].num == square.num &&arr.indexOf(squareSet[square.row][square.col + 1]) == -1) {checkLinked(squareSet[square.row][square.col + 1] , arr);}// check upif( square.row < boardWidth - 1 &&squareSet[square.row + 1][square.col].num == square.num &&arr.indexOf(squareSet[square.row + 1][square.col]) == -1) {checkLinked(squareSet[square.row + 1][square.col] , arr);}// check downif( square.row > 0 &&squareSet[square.row - 1][square.col].num == square.num &&arr.indexOf(squareSet[square.row - 1][square.col]) == -1) {checkLinked(squareSet[square.row - 1][square.col] , arr);} }function flicker(arr){ // 选中连通的小方块可以闪烁var num = 0;timer = setInterval(function(){for(var i = 0 ; i < arr.length ; i ++){arr[i].style.border = "3px solid BFEFFF";//有个框arr[i].style.transform = "scale(" + (0.9 + (0.05 Math.pow(-1 , num))) + ")";//一闪一闪}num ++; // 注意这里所采用的数学技巧,仍然使用transform:scale(val)来进行缩放。},300);//闪烁的时间}function selectScore(){ //可以显示当前选中小方块的得分var score = 0;for(var i = 0 ; i < choose.length ; i ++){score += (baseScore + i stepScore);}document.getElementById('selectScore').innerHTML = choose.length + " blocks " + score + " points";document.getElementById('selectScore').style.opacity = 1;// 设置时间间隔1秒后显示消失的过渡动画setTimeout(function(){document.getElementById('selectScore').style.opacity = 0;document.getElementById('selectScore').style.transition = "opacity 1s";},1000);}function mouseOver(obj){ //鼠标移入区域响应// 还原所有样式goBack();// 检查相邻choose = [];checkLinked(obj , choose);// 闪烁flicker(choose);// 显示分数selectScore();}function move(){//纵向下落,采用快慢指针算法for(var i = 0 ; i < boardWidth ; i ++){var pointer = 0; //慢指针for(var j = 0 ; j < boardWidth ; j ++){if(squareSet[j][i] != null){ //按行遍历if(pointer != j){ //快慢指针不同步说明中间有空元素squareSet[pointer][i] = squareSet[j][i]; //慢指针设成快指针元素squareSet[j][i].row = pointer;squareSet[j][i] = null; //快指针处置空}pointer ++; //该行非空时慢指针增加} }}// 横向移动(当出现一列为空时)for(var i = 0 ; i < squareSet[0].length ;){ //必须注意循环结束条件的判断if(squareSet[0][i] == null){ //逻辑:只需判断最低层为空,该行则全为空for(var j = 0 ; j < boardWidth ; j ++){squareSet[j].splice(i , 1); //splice删除数组squareSet[j]中从i开始的1个元素}continue;//注意移动后i不应改变了}i ++;}refresh();}function init(){ // JS调用入口table = document.getElementById('pop_star'); // 获取到最外层的父元素作为桌面document.getElementById('targetScore').innerHTML = "Target Score : " + targetScore; //显示目标分数用innerHTML// 循环初始化星星区域for(var i = 0 ; i < boardWidth ; i ++){squareSet[i] = new Array(); //二维数组的创建,对每一个元素new Array()创建新数组for(var j = 0 ; j < boardWidth ; j ++){var square = createSquare(Math.floor(Math.random() 5) , i , j);// 鼠标移入事件square.onmouseover = function(){mouseOver(this);}// 鼠标点击事件square.onclick = function(){//对锁进行控制if(!flag || choose.length == null){return;}flag = false;tempSquare = null;//更新分数var score = 0;for(var i = 0 ; i < choose.length ; i ++){score += (baseScore + i stepScore);}totalScore += score;document.getElementById('nowScore').innerHTML = "Current Score : " + totalScore;//为移除增加一个延迟动画,为了防止闭包,这里采用立即执行函数for(var i = 0 ; i < choose.length ; i ++){(function(i){setTimeout(function(){squareSet[choose[i].row][choose[i].col] = null; //为状态数组置空table.removeChild(choose[i]); //将其从桌面上移除} , i 100);})(i);}//需要等星星消除完毕后再移动,故需增加一个延迟setTimeout(function(){move(); //调用移动函数},choose.length 100);}squareSet[i][j] = square; //必须将新创建的方块放回到数组中table.appendChild(square); //需要将创建的新元素添加到桌面上} }refresh(); //每次页面内容发生变化需要重绘页面}window.onload = function(){init();} // window.onload 保证了在页面全部加载完毕后再执行JS代码 效果(下降成功,但是有点小bug只有部分下降了) 解决方案:只需要在function refresh(){}的双循环里面增加以下代码: if(squareSet[i][j] == null) continue; 完整代码如下: var table; //游戏桌面var squareWidth = 50; //方块宽高var boardWidth = 10; //行列数var squareSet = []; //方块信息集合(二维数组)每个元素保存该方块的全部信息var baseScore = 5; //第一块的分数var stepScore = 10; //每多一块的累加分数var totalScore = 0; //当前总分var targetScore = 1500; //目标分var choose = []; //选中的连通小方块var timer = null; //闪烁定时器var flag = true; //锁,防止点击事件中响应其他点击或移入时间var tempSquare = null; //临时方块function refresh(){for (var i = 0; i < squareSet.length; i++) {for (var j = 0; j < squareSet[i].length; j++) {if(squareSet[i][j] == null) continue; // 点击后数组中可能有空值需要跳过squareSet[i][j].style.background="url(pic/"+squareSet[i][j].num+".png)"squareSet[i][j].style.left=squareSet[i][j].colsquareWidth+"px";squareSet[i][j].style.bottom=squareSet[i][j].rowsquareWidth+"px";} }}function createSquare(value,row,col){ //创建小方块,传入参数为颜色、行、列,初始化时使用。var temp = document.createElement('div'); //创建div dom对象temp.style.height = squareWidth + "px";temp.style.width = squareWidth + "px";temp.style.position = "absolute"; //相对于背景绝对定位temp.num = value;temp.col = col;temp.row = row;return temp; //返回这个创建出来的对象}function goBack(){ //还原样式if(timer != null){ //清空计时器clearInterval(timer);}for(var i = 0 ; i < squareSet.length ; i ++){for(var j = 0 ; j < squareSet[i].length ; j ++){squareSet[i][j].style.border = "0px solid white";squareSet[i][j].style.transform = "scale(0.95)";} }}function checkLinked(square , arr){ // 递归连通图算法arr.push(square); // 将当前方块放入选中数组中// check leftif( square.col > 0 && //未到边界squareSet[square.row][square.col - 1].num == square.num && //颜色相同arr.indexOf(squareSet[square.row][square.col - 1]) == -1) { //不在choose中,避免循环判断checkLinked(squareSet[square.row][square.col - 1] , arr);}// check rightif( square.col < boardWidth - 1 &&squareSet[square.row][square.col + 1].num == square.num &&arr.indexOf(squareSet[square.row][square.col + 1]) == -1) {checkLinked(squareSet[square.row][square.col + 1] , arr);}// check upif( square.row < boardWidth - 1 &&squareSet[square.row + 1][square.col].num == square.num &&arr.indexOf(squareSet[square.row + 1][square.col]) == -1) {checkLinked(squareSet[square.row + 1][square.col] , arr);}// check downif( square.row > 0 &&squareSet[square.row - 1][square.col].num == square.num &&arr.indexOf(squareSet[square.row - 1][square.col]) == -1) {checkLinked(squareSet[square.row - 1][square.col] , arr);} }function flicker(arr){ // 选中连通的小方块可以闪烁var num = 0;timer = setInterval(function(){for(var i = 0 ; i < arr.length ; i ++){arr[i].style.border = "3px solid BFEFFF";//有个框arr[i].style.transform = "scale(" + (0.9 + (0.05 Math.pow(-1 , num))) + ")";//一闪一闪}num ++; // 注意这里所采用的数学技巧,仍然使用transform:scale(val)来进行缩放。},300);//闪烁的时间}function selectScore(){ //可以显示当前选中小方块的得分var score = 0;for(var i = 0 ; i < choose.length ; i ++){score += (baseScore + i stepScore);}document.getElementById('selectScore').innerHTML = choose.length + " blocks " + score + " points";document.getElementById('selectScore').style.opacity = 1;// 设置时间间隔1秒后显示消失的过渡动画setTimeout(function(){document.getElementById('selectScore').style.opacity = 0;document.getElementById('selectScore').style.transition = "opacity 1s";},1000);}function mouseOver(obj){ //鼠标移入区域响应// 还原所有样式goBack();// 检查相邻choose = [];checkLinked(obj , choose);// 闪烁flicker(choose);// 显示分数selectScore();}function move(){//纵向下落,采用快慢指针算法for(var i = 0 ; i < boardWidth ; i ++){var pointer = 0; //慢指针for(var j = 0 ; j < boardWidth ; j ++){if(squareSet[j][i] != null){ //按行遍历if(pointer != j){ //快慢指针不同步说明中间有空元素squareSet[pointer][i] = squareSet[j][i]; //慢指针设成快指针元素squareSet[j][i].row = pointer;squareSet[j][i] = null; //快指针处置空}pointer ++; //该行非空时慢指针增加} }}// 横向移动(当出现一列为空时)for(var i = 0 ; i < squareSet[0].length ;){ //必须注意循环结束条件的判断if(squareSet[0][i] == null){ //逻辑:只需判断最低层为空,该行则全为空for(var j = 0 ; j < boardWidth ; j ++){squareSet[j].splice(i , 1); //splice删除数组squareSet[j]中从i开始的1个元素}continue;//注意移动后i不应改变了}i ++;}refresh();}function init(){ // JS调用入口table = document.getElementById('pop_star'); // 获取到最外层的父元素作为桌面document.getElementById('targetScore').innerHTML = "Target Score : " + targetScore; //显示目标分数用innerHTML// 循环初始化星星区域for(var i = 0 ; i < boardWidth ; i ++){squareSet[i] = new Array(); //二维数组的创建,对每一个元素new Array()创建新数组for(var j = 0 ; j < boardWidth ; j ++){var square = createSquare(Math.floor(Math.random() 5) , i , j);// 鼠标移入事件square.onmouseover = function(){mouseOver(this);}// 鼠标点击事件square.onclick = function(){//对锁进行控制if(!flag || choose.length == null){return;}flag = false;tempSquare = null;//更新分数var score = 0;for(var i = 0 ; i < choose.length ; i ++){score += (baseScore + i stepScore);}totalScore += score;document.getElementById('nowScore').innerHTML = "Current Score : " + totalScore;//为移除增加一个延迟动画,为了防止闭包,这里采用立即执行函数for(var i = 0 ; i < choose.length ; i ++){(function(i){setTimeout(function(){squareSet[choose[i].row][choose[i].col] = null; //为状态数组置空table.removeChild(choose[i]); //将其从桌面上移除} , i 100);})(i);}//需要等星星消除完毕后再移动,故需增加一个延迟setTimeout(function(){move(); //调用移动函数},choose.length 100);}squareSet[i][j] = square; //必须将新创建的方块放回到数组中table.appendChild(square); //需要将创建的新元素添加到桌面上} }refresh(); //每次页面内容发生变化需要重绘页面}window.onload = function(){init();} // window.onload 保证了在页面全部加载完毕后再执行JS代码 第四阶段:消灭全部星星,返回结果 最终完整版代码如下: var table; //游戏桌面var squareWidth = 50; //方块宽高var boardWidth = 10; //行列数var squareSet = []; //方块信息集合(二维数组)每个元素保存该方块的全部信息var baseScore = 5; //第一块的分数var stepScore = 10; //每多一块的累加分数var totalScore = 0; //当前总分var targetScore = 1500; //目标分var choose = []; //选中的连通小方块var timer = null; //闪烁定时器var flag = true; //锁,防止点击事件中响应其他点击或移入时间var tempSquare = null; //临时方块function refresh(){ //重绘画板,每次鼠标点击后刷新for(var i = 0 ; i < squareSet.length ; i ++){for(var j = 0 ; j < squareSet[i].length ; j ++){if(squareSet[i][j] == null) continue; // 点击后数组中可能有空值需要跳过squareSet[i][j].row = i; //更新当前的行列数squareSet[i][j].col = j;squareSet[i][j].style.backgroundImage = "url(./pic/" + squareSet[i][j].num + ".png)"squareSet[i][j].style.backgroundSize = "cover"; //占满范围squareSet[i][j].style.transform = "scale(0.95)"; //美观效果让不同星星之间留出空隙(缩小至0.95倍大小)squareSet[i][j].style.left = squareSet[i][j].col squareWidth + "px"; // 别忘了加"px"squareSet[i][j].style.bottom = squareSet[i][j].row squareWidth + "px";squareSet[i][j].style.transition = "left 0.3s, bottom 0.3s";} }}function createSquare(value,row,col){ //创建小方块,传入参数为颜色、行、列,初始化时使用。var temp = document.createElement('div'); //创建div dom对象temp.style.height = squareWidth + "px";temp.style.width = squareWidth + "px";temp.style.display = "inline-block"; //需要让对象元素能排列一排temp.style.position = "absolute"; //相对于背景绝对定位temp.style.boxSizing = "border-box"; //重要:不会使增加的边框溢出覆盖到旁边的元素temp.style.borderRadius = "12px";temp.num = value;temp.col = col;temp.row = row;return temp; //返回这个创建出来的对象}function goBack(){ //还原样式if(timer != null){ //清空计时器clearInterval(timer);}for(var i = 0 ; i < squareSet.length ; i ++){for(var j = 0 ; j < squareSet[i].length ; j ++){if(squareSet[i][j] == null) continue;squareSet[i][j].style.border = "0px solid white";squareSet[i][j].style.transform = "scale(0.95)";} }}function checkLinked(square , arr){ // 递归连通图算法if(square == null) return; // 递归边界arr.push(square); // 将当前方块放入选中数组中// check leftif( square.col > 0 && //未到边界squareSet[square.row][square.col - 1] && //左侧有块squareSet[square.row][square.col - 1].num == square.num && //颜色相同arr.indexOf(squareSet[square.row][square.col - 1]) == -1) { //不在choose中,避免循环判断checkLinked(squareSet[square.row][square.col - 1] , arr);}// check rightif( square.col < boardWidth - 1 &&squareSet[square.row][square.col + 1] &&squareSet[square.row][square.col + 1].num == square.num &&arr.indexOf(squareSet[square.row][square.col + 1]) == -1) {checkLinked(squareSet[square.row][square.col + 1] , arr);}// check upif( square.row < boardWidth - 1 &&squareSet[square.row + 1][square.col] &&squareSet[square.row + 1][square.col].num == square.num &&arr.indexOf(squareSet[square.row + 1][square.col]) == -1) {checkLinked(squareSet[square.row + 1][square.col] , arr);}// check downif( square.row > 0 &&squareSet[square.row - 1][square.col] &&squareSet[square.row - 1][square.col].num == square.num &&arr.indexOf(squareSet[square.row - 1][square.col]) == -1) {checkLinked(squareSet[square.row - 1][square.col] , arr);} }function flicker(arr){ // 选中连通的小方块可以闪烁var num = 0;timer = setInterval(function(){for(var i = 0 ; i < arr.length ; i ++){arr[i].style.border = "3px solid BFEFFF";arr[i].style.transform = "scale(" + (0.9 + (0.05 Math.pow(-1 , num))) + ")";}num ++; // 注意这里所采用的数学技巧,仍然使用transform:scale(val)来进行缩放。},300);}function selectScore(){ //可以显示当前选中小方块的得分var score = 0;for(var i = 0 ; i < choose.length ; i ++){score += (baseScore + i stepScore);}if(score == 0) return;document.getElementById('selectScore').innerHTML = choose.length + " blocks " + score + " points";document.getElementById('selectScore').style.opacity = 1;document.getElementById('selectScore').style.transition = null;// 设置时间间隔1秒后显示消失的过渡动画setTimeout(function(){document.getElementById('selectScore').style.opacity = 0;document.getElementById('selectScore').style.transition = "opacity 1s";},1000);}function mouseOver(obj){ //鼠标移入区域响应// 加锁,点击事件过程中不允许其他点击事件与移入事件if(!flag){tempSquare = obj;return;}// 还原所有样式goBack();// 检查相邻choose = [];checkLinked(obj , choose);if(choose.length <= 1){choose = [];return;}// 闪烁flicker(choose);// 显示分数selectScore();}function move(){ //下落移动控制//纵向下落,采用快慢指针算法for(var i = 0 ; i < boardWidth ; i ++){var pointer = 0; //慢指针for(var j = 0 ; j < boardWidth ; j ++){if(squareSet[j][i] != null){ //按行遍历if(pointer != j){ //快慢指针不同步说明中间有空元素squareSet[pointer][i] = squareSet[j][i]; //慢指针设成快指针元素squareSet[j][i].row = pointer;squareSet[j][i] = null; //快指针处置空}pointer ++; //该行非空时慢指针增加} }}// 横向移动(当出现一列为空时)for(var i = 0 ; i < squareSet[0].length ;){ // 注意循环终止条件的判断!!!因为数组长度会更新if(squareSet[0][i] == null){ //逻辑:只需判断最低层为空,该行则全为空for(var j = 0 ; j < boardWidth ; j ++){squareSet[j].splice(i , 1); //splice删除数组squareSet[j]中从i开始的1个元素}continue;//注意移动后i不应改变了}i ++;}refresh();}function isFinish(){ //判断游戏结束flag = true; //重要:需要先解锁,保证后续鼠标事件可以被响应for(var i = 0 ; i < squareSet.length ; i ++){for(var j = 0 ; j < squareSet[i].length ; j ++){if(squareSet[i][j] == null) continue; //遍历每一元素判断连通var temp = [];checkLinked(squareSet[i][j] , temp);if(temp.length > 1) return false; //若有某一元素仍有多块连通,则游戏未结束} }return flag;}function init(){ // JS调用入口table = document.getElementById('pop_star'); // 获取到最外层的父元素作为桌面document.getElementById('targetScore').innerHTML = "Target Score : " + targetScore; //显示目标分数用innerHTML// 循环初始化星星区域for(var i = 0 ; i < boardWidth ; i ++){squareSet[i] = new Array(); //二维数组的创建,对每一个元素new Array()创建新数组for(var j = 0 ; j < boardWidth ; j ++){var square = createSquare(Math.floor(Math.random() 5) , i , j);// 鼠标移入事件square.onmouseover = function(){mouseOver(this);}// 鼠标点击事件square.onclick = function(){//对锁进行控制if(!flag || choose.length == null){return;}flag = false;tempSquare = null;//更新分数var score = 0;for(var i = 0 ; i < choose.length ; i ++){score += (baseScore + i stepScore);}totalScore += score;document.getElementById('nowScore').innerHTML = "Current Score : " + totalScore;//为移除增加一个延迟动画,为了防止闭包,这里采用立即执行函数for(var i = 0 ; i < choose.length ; i ++){(function(i){setTimeout(function(){squareSet[choose[i].row][choose[i].col] = null; //为状态数组置空table.removeChild(choose[i]); //将其从桌面上移除} , i 50);})(i);}//需要等星星消除完毕后再移动,故需增加一个延迟setTimeout(function(){move(); //调用移动函数setTimeout(function(){var judge = isFinish();if(judge){ //游戏达到结束条件if(totalScore > targetScore){alert('Congratulations! You win!');}else{alert('Mission Failed!');} }else{flag = true;choose = [];mouseOver(tempSquare); //处理可能存在的冲突} },300 + choose.length 75); //需要一个判断延迟},choose.length 50);}squareSet[i][j] = square; //必须将新创建的方块放回到数组中table.appendChild(square); //需要将创建的新元素添加到桌面上} }refresh(); //每次页面内容发生变化需要重绘页面}window.onload = function(){init();} // window.onload 保证了在页面全部加载完毕后再执行JS代码 效果 本篇文章为转载内容。原文链接:https://blog.csdn.net/weixin_56471396/article/details/128681321。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-06-08 15:26:34
516
转载
Python
...修改集合内容可能导致索引错误、丢失数据等问题。为解决此类问题,Python提供了多种方法,如使用列表推导式创建新列表代替原列表,或者先记录待删除项,遍历结束后再统一执行删除操作。此外,还可以考虑采用更为安全的数据结构,如集合或生成器表达式,在某些场景下能有效避免迭代过程中的状态改变问题。 另外,Python官方文档也强调了对于可变对象在循环中正确操作的重要性,并提供了一系列最佳实践建议。例如,《Effective Python》一书中提到,“在对容器元素进行迭代的同时对其进行修改是一种反模式,应尽量避免”。这一观点与我们之前分析“外星人入侵”游戏bug时得出的结论相吻合,再次提醒我们在实际编程中关注细节,遵循正确的编程范式,以提升代码质量和程序稳定性。
2023-12-10 11:15:11
201
昨夜星辰昨夜风_t
PostgreSQL
...一个可以显示值出来的索引。首先,咱们得搞明白,啥是索引呢?打个比方吧,索引就类似一本图书的目录,它是一种特别设计的数据结构,能帮咱们像查字典一样,嗖的一下找到你需要的具体数据行。 2. 创建索引的基本语法 那么,如何在PostgreSQL中创建一个索引呢?我们可以使用CREATE INDEX语句来完成这个任务。基本语法如下: sql CREATE INDEX index_name ON table_name (column_name); 这里的index_name是我们给索引起的名字,table_name是我们要为其创建索引的数据表名,而column_name则是我们想要在其上创建索引的列名。 举个例子,假设我们有一个名为users的用户表,其中包含id、name和email三列,如果我们想要在其id列上创建一个索引,我们可以这样操作: sql CREATE INDEX idx_users_id ON users (id); 以上就是创建索引的基本语法,下面我们来看一下更复杂一点的情况。 3. 多列索引 除了单一列的索引外,PostgreSQL还支持多列索引。也就是说,我们可以在一个或者多个列上同时创建索引。创建多列索引的方法与创建单一列索引的方法类似,只是我们在ON后面的括号中需要列出所有的列名,中间用逗号隔开即可。例如,如果我们想要在users表的id和name两列上同时创建索引,我们可以这样做: sql CREATE INDEX idx_users_id_name ON users (id, name); 这种索引的好处是可以加快对多个列的联合查询的效率,因为查询引擎可以直接利用索引来定位数据,而不需要逐行比较。 4. 唯一性索引 除了普通索引外,PostgreSQL还支持唯一性索引。简单来说,唯一性索引呢,就像它的名字一样直截了当。它就像是数据库里的“独一无二标签”,在一个特定的列上,坚决不允许有重复的数据出现,保证每一条记录都是独一无二的存在。如果你试图往PostgreSQL数据库里插一条已经有重复值的记录,它会毫不客气地给你抛出一个错误消息。唯一性索引通常用于保证数据的一致性和完整性。 创建唯一性索引的方法非常简单,我们只需要在创建索引的语句后面添加UNIQUE关键字即可。例如,如果我们想要在users表的email列上创建一个唯一性索引,我们可以这样做: sql CREATE UNIQUE INDEX idx_users_email ON users (email); 以上就是在PostgreSQL中创建索引的一些基础知识,希望能对你有所帮助。如果你还有其他疑问,欢迎随时向我提问!
2023-11-16 14:06:06
485
晚秋落叶_t
PostgreSQL
...一个可以显示值出来的索引? 引言 PostgreSQL是一款强大的开源关系型数据库管理系统,支持多种存储引擎和索引类型。这篇文儿呢,主要是手把手教你咋在PostgreSQL这个数据库里头,捣鼓出一个能够秀出具体数值的索引,让你的数据查询嗖嗖快。 创建索引的基本步骤 在PostgreSQL中,我们可以使用CREATE INDEX语句来创建一个新的索引。以下是一些基本步骤: 步骤一:选择要创建索引的表 首先,我们需要选择要创建索引的表。例如,如果我们有一个名为employees的表,我们可以在其中创建索引: sql CREATE TABLE employees ( id serial primary key, name varchar(50), department varchar(50) ); 步骤二:选择要创建索引的列 接下来,我们需要选择要创建索引的列。例如,如果我们想要根据name列创建一个索引,我们可以这样做: sql CREATE INDEX idx_employees_name ON employees (name); 在这个例子中,idx_employees_name是我们给索引起的名字,ON employees (name)表示我们在employees表的name列上创建了一个新的索引。 步骤三:创建索引 最后,我们可以通过执行上述SQL语句来创建索引。要是没啥意外,PostgreSQL会亲口告诉我们一个好消息,那就是索引已经妥妥地创建成功啦! sql CREATE INDEX idx_employees_name ON employees (name); 如何查看已创建的索引? 如果你想知道哪些索引已经被创建在你的表上,你可以使用pg_indexes系统视图。这个视图可厉害了,它囊括了所有的索引信息,从索引的名字,到它所对应绑定的表,再到索引的各种类型,啥都一清二楚,明明白白。 sql SELECT FROM pg_indexes WHERE tablename = 'employees'; 这将会返回一个结果集,其中包含了employees表上的所有索引的信息。 创建可以显示值的索引 在PostgreSQL中,创建一个可以显示值的索引很简单。我们只需要在创建索引的时候指定我们想要使用的索引类型即可。目前,PostgreSQL支持多种索引类型,包括B-tree、哈希、GiST、SP-GiST和GIN等。不同的索引类型就像不同类型的工具,各有各的适用场合。所以,你得根据自己的实际需求,像挑选合适的工具一样,去选择最适合你的索引类型。别忘了,对症下药才能发挥最大效用! 以下是一个创建B-tree索引的例子: sql CREATE INDEX idx_employees_name_btree ON employees (name); 在这个例子中,idx_employees_name_btree是我们给索引起的名字,ON employees (name)表示我们在employees表的name列上创建了一个新的B-tree索引。如果你想创建不同类型的索引,那就简单啦,只需要把“btree”这个词儿换成你心水的索引类型就大功告成啦!就像是换衣服一样,根据你的需求选择不同的“款式”就行。 总结 创建一个可以显示值的索引并不难。其实,你只需要用一句“CREATE INDEX”命令,就能轻松搞定创建索引的事儿。具体来说,就是在这句命令里头,告诉系统你要在哪个表上建索引、打算对哪一列建立索引,还有你希望用哪种类型的索引,一切就OK啦!就像是在跟数据库说:“嗨,我在某某表的某某列上,想要创建一个这样那样的索引!”另外,你还可以使用pg_indexes系统视图来查看已创建的所有索引。希望这篇文章能对你有所帮助!
2023-11-30 10:13:56
261
半夏微凉_t
PostgreSQL
...eSQL中创建和使用索引以优化查询性能之后,我们可以进一步探索数据库索引的最新研究进展与实践应用。近期,PostgreSQL社区发布了14版本,其中对索引功能进行了多项增强与优化,如引入了BRIN(Block Range Indexes)类型的索引,特别适用于大数据量且数据按时间或其他连续键排序的场景,能够大幅降低存储开销并提升查询效率。 同时,对于索引策略的选择和优化,业界也持续进行深度研究。例如,一篇发表在《ACM Transactions on Database Systems》上的论文详细探讨了在实际业务场景下,如何根据数据分布特性和查询模式动态调整索引结构,以及如何利用分区、覆盖索引等技术来最大化数据库性能。 此外,随着机器学习和AI技术的发展,智能化数据库管理工具也开始崭露头角,它们能够通过分析历史查询数据和实时负载情况,自动推荐或调整索引配置,从而减轻DBA的工作负担,并确保数据库系统的高效运行。 总之,尽管本文介绍了PostgreSQL中创建显示值索引的基础方法,但数据库索引的世界远比这更为丰富和复杂,不断跟进最新的理论研究成果和技术动态,将有助于我们更好地应对各种实际应用场景中的性能挑战。
2023-07-04 17:44:31
345
梦幻星空_t
PostgreSQL
...言 在数据库领域中,索引是一种非常重要的概念,它可以极大地提高数据库查询的速度。在 PostgreSQL 数据库这个大家伙里,如果你想快速查找到你要的记录,就像在书堆里找书时用目录一样,我们可以使出一个“CREATE INDEX”的神奇招数来创建索引。这样一来,当你进行查询操作的时候,就再也不用大海捞针似的慢慢找了,嗖嗖地就能找到你需要的信息。嘿,各位,今天咱们要聊点实用的,一起来研究下如何在 PostgreSQL 这个数据库神器里头动手创建一个能够秀出具体数值的索引,让你的数据查询速度嗖嗖的! 二、什么是索引? 在数据库中,当我们执行 SELECT 查询时,数据库会从存储在磁盘上的所有行中查找匹配我们的查询条件的行。这个过程是非常耗时的,特别是当我们的表很大时。为了把这个过程搞得更溜些,我们可以搞个索引,就像图书目录一样,让数据库能像查书名那样瞬间找到我们需要的那些行。 索引是一个包含表中特定列的数据结构,它可以帮助我们在查询时更快地找到所需的数据。在 PostgreSQL 中,我们可以使用 CREATE INDEX 命令来创建索引。 三、如何创建索引? 在 PostgreSQL 中,我们可以使用 CREATE INDEX 命令来创建索引。这个命令的基本语法如下: sql CREATE INDEX index_name ON table_name (column_name); 在这个命令中,index_name 是我们为索引指定的名称,table_name 是我们要在其上创建索引的表名,column_name 是我们要为其创建索引的列名。 例如,如果我们有一个名为 articles 的表,它有两个字段 id 和 title,我们可以使用以下命令来为 title 列创建一个索引: css CREATE INDEX idx_title ON articles (title); 四、创建可显示值的索引 有时候,我们可能想要创建一个索引,使得查询结果可以直接显示出来,而不仅仅是查询结果的数量。这就需要用到 PostgreSQL 的窗口函数。 窗口函数允许我们在查询结果上进行计算,就像我们在 Excel 中所做的那样。窗口函数可以在一个行或一组行上应用一个函数,并返回结果。这使得我们可以很容易地创建出可以显示值的索引。 例如,假设我们有一个名为 sales 的表,它有两个字段 date 和 amount。我们可以使用以下窗口函数来创建一个可以显示销售额总和的索引: vbnet SELECT date, SUM(amount) OVER (ORDER BY date) AS total_sales FROM sales; 在这个查询中,SUM(amount) OVER (ORDER BY date) 是一个窗口函数,它会对 sales 表中的 amount 列按照 date 列进行分组,并对每个日期求和。这个窗口函数的计算结果,我们打算把它放到 total_sales 这个栏目里展示出来,这样一来,咱们就能一目了然地瞧见每天销售额的具体总数啦! 如果我们想为这个查询创建一个索引,我们可以使用以下命令: python CREATE INDEX idx_total_sales ON sales (date, total_sales); 在这个命令中,我们为 date 和 total_sales 列创建了一个复合索引,这将使查询速度大大加快。 五、总结 在 PostgreSQL 中,我们可以使用 CREATE INDEX 命令来创建索引,以提高数据库查询的速度。用窗口函数这个神器,咱们就能捣鼓出那种带显示数值的索引,这样一来,查询结果就变得贼直观、贼好理解了,跟看懂漫画似的。 如果你正在使用 PostgreSQL,并且想要优化你的查询性能,那么创建索引和窗口函数是非常有用的工具。希望这篇文章能对你有所帮助!
2023-06-22 19:00:45
122
时光倒流_t
转载文章
...。 迭代器,也是一种模式(也叫迭代器模式)。在java中它是一个对象,其目的是遍历并选中其中的每个元素,而使用者(客户端)无需知道里面的具体细节。迭代器要足够的“轻量”——创建迭代器的代价小。所以看迭代器的源代码就会发现,里面会有很多要求: iterator方法返回一个Iterator,Iterator返回序列的头元素。 next方法获取下一个元素 hasNext检查还有元素 remove删除迭代器新返回的元素 下面是迭代器的基本使用 public class UsingIterator { public static void main(String[] args) { List names = Arrays.asList("marson", "shine", "summer", "zhu"); Iterator it = names.iterator(); while(it.hasNext()){ String s = it.next(); print(s); } for (String s : names){ print(s); } System.out.println(); it = names.iterator(); for (int i = 0; i < 4; i++) { it.next(); } print(names); } } ListIterator ListIterator是一个更强大的Iterator子类型,能用于各种List类访问,前面说过Iterator支持单向取数据,ListIterator可以双向移动,所以能指出迭代器当前位置的前一个和后一个索引,可以用set方法替换它访问过的最后一个元素。我们可以通过调用listIterator方法产生一个指向List开始处的ListIterator,并且还可以用过重载方法listIterator(n)来创建一个指定列表索引为n的元素的ListIterator。 public class ListIteration { public static void main(String[] args) { var names = Arrays.asList("marson", "shine", "summer", "zhu"); var it = names.listIterator(); while (it.hasNext()) { print(it.next() + ", " + it.nextIndex() + ", " + it.previousIndex() + "; "); } while (it.hasPrevious()) { print(it.previous() + " "); } print(names); it = names.listIterator(3); while (it.hasNext()) { it.next(); it.set("alias"); } print(names); } } 输出结果为: marson, 1, 0; shine, 2, 1; summer, 3, 2; zhu, 4, 3; zhu summer shine marson [marson, shine, summer, zhu] [marson, shine, summer, alias] Iterator模式 前面说了,迭代器又叫迭代器模式,顾名思义,只要符合这种模式都能叫迭代器模式,自然也能像前面一样使用迭代器 那么Iterator模式具体是个什么样子的模式呢? 我们通过Collection的源码发现其中的样子(为什么要看Collection而不是其他的List?因为Collection是所有容器的基类啊) 通过Collection代码我们发现它继承了一个叫Iterable接口,注解说的很清楚——实现这个接口就说明这个对象是可迭代的;并且其成员函数也很清晰,只有三个方法 public interface Iterable { Iterator iterator(); default void forEach(Consumer super T> action);//省略部分代码 default Spliterator spliterator();//省略部分代码 } public interface Iterator { boolean hasNext(); E next(); default void remove() { throw new UnsupportedOperationException("remove"); } ... } Iterator这个泛型接口才是我们真正实现迭代的核心,通过这些信息我们尝试来写一个迭代器 public class CustomIterator implements Iterable { protected String[] names = ("marson shine summer zhu").split(" "); public Iterator iterator() { return new Iterator() { private int index = 0; @Override public boolean hasNext() { return index < names.length; } @Override public String next() { return names[index++]; } public void remove() { } }; } public static void main(String[] agrs) { for (var s : new CustomIterator()) { print(s + " "); } } } 到这里,自定义的迭代器就写完了,实际上我们只需要继承一个Iterable接口然后实现这个接口就行了,更深入的话,其实还可以自己写一个listIterator实现双向的操作数据 来源:oschina 链接:https://my.oschina.net/u/4353634/blog/4002987 本篇文章为转载内容。原文链接:https://blog.csdn.net/weixin_42516657/article/details/114169640。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-07-30 21:49:56
160
转载
PostgreSQL
B-Tree索引 , B-Tree(Balance Tree,平衡树)索引是PostgreSQL中最常用的索引类型之一,它是一种自平衡的树形数据结构。在数据库查询中,B-Tree索引能够有效地支持点查询、范围查询和排序操作。在PostgreSQL中创建的B-Tree索引会按照键值排序,并将数据组织成分层结构,使得查找、插入和删除等操作的时间复杂度保持在O(log n)级别,从而显著提高数据检索性能。 GiST索引 , GiST(Generalized Search Tree,通用搜索树)索引是PostgreSQL提供的一种索引框架,允许开发人员为特定数据类型实现定制化的索引策略。GiST索引可以支持多种类型的查询,包括但不限于等值查询、范围查询以及更复杂的几何空间关系查询等。例如,在全文搜索或地理空间数据查询场景下,通过使用GiST索引,用户可以根据需求对文本内容或者地理位置信息建立高效的搜索索引。 GIN索引 , GIN(Generalized Inverted Index,通用倒排索引)是PostgreSQL中另一种高级索引类型,特别适用于处理包含大量重复值且需要进行集合成员资格测试的数据列,如JSON或XML文档字段、数组或者全文本搜索。在GIN索引中,存储的是值到记录的映射关系,而不是像B-Tree那样基于记录顺序。因此,对于“是否存在某个值”这类查询,GIN索引通常能提供更快的响应速度,尤其适合于模糊匹配和模式匹配查询。
2023-01-05 19:35:54
189
月影清风_t
PostgreSQL
...是我们的信息仓库,而索引则是加速查询速度的金钥匙。PostgreSQL,这款开源的关系型数据库管理系统,就像是开发者们手里的瑞士军刀,功能强大得不得了,灵活性更是让它圈粉无数,实实在在地赢得了广大开发者的青睐和心水。这篇东西,我将手把手带你潜入PostgreSQL索引的深处,教你如何妙用它们,让咱们的应用程序性能嗖嗖提升,飞得更高更稳!让我们一起踏上这场数据查询的优化之旅吧! 二、索引基础与理解 1. 索引是什么? 索引就像书的目录,帮助我们快速找到所需的信息。在数据库这个大仓库里,索引就像是一本超详细的目录,它能够帮助数据库系统瞬间找到你要的那一行数据,而不需要像翻箱倒柜一样把整张表从头到尾扫一遍。 2. PostgreSQL的索引类型 PostgreSQL支持多种索引类型,如B-Tree、GiST、GIN等。其实吧,B-Tree是最家常便饭的那个,基本上大多数情况下它都能派上用场;不过呢,遇到那些比较复杂的“角儿”,比如JSON或者数组这些数据类型,就得请出GiST和GIN两位大神了。 sql -- 创建一个B-Tree索引 CREATE INDEX idx_users_name ON users (name); 三、选择合适的索引策略 1. 索引选择原则 选择索引时,要考虑查询频率、数据更新频率以及数据分布。频繁查询且更新少的列更适合建立索引。 2. 复合索引 对于同时包含多个字段的查询,可以创建复合索引,但要注意索引的顺序,通常应将最常用于WHERE子句的列放在前面。 sql CREATE INDEX idx_users_first_last ON users (first_name, last_name); 四、优化查询语句 1. 避免在索引列上进行函数操作 函数操作可能导致索引失效,尽量避免在索引列上使用EXTRACT、DATE_TRUNC等函数。 2. 使用覆盖索引 覆盖索引是指查询结果可以直接从索引中获取,减少I/O操作,提高效率。 sql CREATE INDEX idx_users_email ON users (email) WHERE is_active = true; 五、维护和监控索引 1. 定期分析和重建索引 使用ANALYZE命令更新统计信息,当索引不再准确时,使用REINDEX命令重建。 2. 使用pg_stat_user_indexes监控 pg_stat_user_indexes视图可以提供索引的使用情况,包括查询次数、命中率等,有助于了解并调整索引策略。 六、结论 通过合理的索引设计和优化,我们可以显著提升PostgreSQL的查询性能。然而,记住,索引并非万能的,过度使用或不适当的索引可能会带来反效果。在实际操作中,咱们得根据业务的具体需求和数据的特性来灵活调整,让索引真正变成提升数据库性能的独门秘籍。 在这个快速变化的技术世界里,持续学习和实践是关键。愿你在探索PostgreSQL索引的道路上越走越远,收获满满!
2024-03-14 11:15:25
495
初心未变-t
ElasticSearch
...接下来要做的就是配置索引模板(Index Template)。 json PUT _template/my_template { "index_patterns": ["my-index-"], "settings": { "number_of_shards": 1, "number_of_replicas": 1 }, "mappings": { "_source": { "enabled": true }, "properties": { "timestamp": { "type": "date" }, "message": { "type": "text" } } } } 上面这段代码定义了一个名为my_template的模板,适用于所有以my-index-开头的索引。这个模板里头设定了索引的分片数和副本数,还定义了两个字段:一个存时间戳叫timestamp,另一个存消息内容叫message。 4. 使用Logstash采集数据 现在我们有了Elasticsearch,也有了数据采集工具,接下来就是让它们协同工作。这里我们以Logstash为例,看看如何将日志数据采集到Elasticsearch中。 首先,你需要创建一个Logstash配置文件(.conf),指定输入源、过滤器和输出目标。 conf input { file { path => "/var/log/nginx/access.log" start_position => "beginning" } } filter { grok { match => { "message" => "%{COMBINEDAPACHELOG}" } } date { match => [ "timestamp", "dd/MMM/yyyy:HH:mm:ss Z" ] } } output { elasticsearch { hosts => ["localhost:9200"] index => "nginx-access-%{+YYYY.MM.dd}" } } 这段配置文件告诉Logstash从/var/log/nginx/access.log文件读取数据,使用Grok过滤器解析日志格式,然后将解析后的数据存入Elasticsearch中。这里的hosts参数指定了Elasticsearch的地址,index参数定义了索引的命名规则。 5. 实战演练 分析数据 最后,让我们来看看如何通过Elasticsearch查询和分析这些数据。好了,假设你已经把日志数据成功导入到了Elasticsearch里,现在你想看看最近一天内哪些网址被访问得最多。 bash GET /nginx-access-/_search { "size": 0, "aggs": { "top_pages": { "terms": { "field": "request", "size": 10 } } } } 这段查询语句会返回过去一天内访问量最高的10个URL。通过这种方式,你可以快速获取关键信息,从而做出相应的决策。 6. 总结与展望 通过这篇文章,我们学习了如何使用Elasticsearch异步采集非业务数据,并进行了简单的分析。这个过程让我们更懂用户的套路,还挖出了不少宝贝,帮我们更好地升级产品和服务。 当然,实际操作中可能会遇到各种问题和挑战,但只要保持耐心,不断实践和探索,相信你一定能够掌握这项技能。希望这篇教程能对你有所帮助,如果你有任何疑问或者建议,欢迎随时留言交流! --- 好了,朋友们,今天的分享就到这里。希望你能从中获得灵感,开始你的Elasticsearch之旅。记住,技术的力量在于应用,让我们一起用它来创造更美好的世界吧!
2024-12-29 16:00:49
75
飞鸟与鱼_
转载文章
...(PHP必须在非安全模式下运行) 建议使用平台:Linux + Apache2.2 + PHP5.2/PHP5.3 + MySQL5.0 3.PHP必须环境或启用的系统函数: allow_url_fopen GD扩展库 MySQL扩展库 系统函数 —— phpinfo、dir 4.基本目录结构 / ..../install 安装程序目录,安装完后可删除[安装时必须有可写入权限] ..../dede 默认后台管理目录(可任意改名) ..../include 类库文件目录 ..../plus 附助程序目录 ..../member 会员目录 ..../images 系统默认模板图片存放目录 ..../uploads 默认上传目录[必须可写入] ..../a 默认HTML文件存放目录[必须可写入] ..../templets 系统默认内核模板目录 ..../data 系统缓存或其它可写入数据存放目录[必须可写入] ..../special 专题目录[生成一次专题后可以删除special/index.php,必须可写入] 5.PHP环境容易碰到的不兼容性问题 (1)data目录没写入权限,导致系统session无法使用,这将导致无法登录管理后台(直接表现为验证码不能正常显示); (2)php的上传的临时文件夹没设置好或没写入权限,这会导致文件上传的功能无法使用; (3)出现莫名的错误,如安装时显示空白,这样能是由于系统没装载mysql扩展导致的,对于初级用户,可以下载dede的php套件包,以方便简单的使用。 二、程序安装使用 1.下载程序解压到本地目录; 2.上传程序目录中的/uploads到网站根目录 3.运行http://www.yourname.com/install/index.php(yourname表示你的域名),按照安装提速说明进行程序安装 本篇文章为转载内容。原文链接:https://blog.csdn.net/weixin_31879641/article/details/115616068。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-09-24 09:08:23
278
转载
转载文章
...就数据表的规范化、表索引优化、容量设计、事务处理等诸多方面与程序员进行卓有成效的沟通和协作,那么日常的前台管理工作将会变得大为轻松,因为在某种意义上,数据库管理系统就相当于操作系统,在系统中占有同样重要的位置。 这正是SQL SERVER等数据库管理系统和dBASEX、ACCESS等数据库文件系统的本质区别,所以,对数据库管理系统操作能力的强弱在某种程度上也折射出了网管的水平——个人认为,称得上优秀的Admin,至少应该是一个称职的DBA(数据库管理员)。 下面以SQL SERVER(下称 SQLS)为例,将数据库管理中难于理解的“索引原理”问题给各位朋友作一个深入浅出的介绍。其他的数据库管理系统如Oracle、Sybase等,朋友们可以融会贯通,举一反三。 一、数据表的基本结构 建立数据库的目的是管理大量数据,而建立索引的目的就是提高数据检索效率,改善数据库工作性能,提高数据访问速度。对于索引,我们要知其然,更要知其所以然,关键在于认识索引的工作原理,才能更好的管理索引。 为认识索引工作原理,首先有必要对数据表的基本结构作一次全面的复习。 SQLS当一个新表被创建之时,系统将在磁盘中分配一段以8K为单位的连续空间,当字段的值从内存写入磁盘时,就在这一既定空间随机保存,当一个8K用完的时候,SQLS指针会自动分配一个8K的空间。这里,每个8K空间被称为一个数据页(Page),又名页面或数据页面,并分配从0-7的页号,每个文件的第0页记录引导信息,叫文件头(File header);每8个数据页(64K)的组合形成扩展区(Extent),称为扩展。全部数据页的组合形成堆(Heap)。 SQLS规定行不能跨越数据页,所以,每行记录的最大数据量只能为8K。这就是char和varchar这两种字符串类型容量要限制在8K以内的原因,存储超过8K的数据应使用text类型,实际上,text类型的字段值不能直接录入和保存,它只是存储一个指针,指向由若干8K的文本数据页所组成的扩展区,真正的数据正是放在这些数据页中。 页面有空间页面和数据页面之分。 当一个扩展区的8个数据页中既包含了空间页面又包括了数据或索引页面时,称为混合扩展(Mixed Extent),每张表都以混合扩展开始;反之,称为一致扩展(Uniform Extent),专门保存数据及索引信息。 表被创建之时,SQLS在混合扩展中为其分配至少一个数据页面,随着数据量的增长,SQLS可即时在混合扩展中分配出7个页面,当数据超过8个页面时,则从一致扩展中分配数据页面。 空间页面专门负责数据空间的分配和管理,包括:PFS页面(Page free space):记录一个页面是否已分配、位于混合扩展还是一致扩展以及页面上还有多少可用空间等信息;GAM页面(Global allocation map)和SGAM页面(Secodary global allocation map):用来记录空闲的扩展或含有空闲页面的混合扩展的位置。SQLS综合利用这三种类型的页面文件在必要时为数据表创建新空间; 数据页或索引页则专门保存数据及索引信息,SQLS使用4种类型的数据页面来管理表或索引:它们是IAM页、数据页、文本/图像页和索引页。 在WINDOWS中,我们对文件执行的每一步操作,在磁盘上的物理位置只有系统(system)才知道;SQL SERVER沿袭了这种工作方式,在插入数据的过程中,不但每个字段值在数据页面中的保存位置是随机的,而且每个数据页面在“堆”中的排列位置也只有系统(system)才知道。 这是为什么呢?众所周知,OS之所以能管理DISK,是因为在系统启动时首先加载了文件分配表:FAT(File Allocation Table),正是由它管理文件系统并记录对文件的一切操作,系统才得以正常运行;同理,作为管理系统级的SQL SERVER,也有这样一张类似FAT的表存在,它就是索引分布映像页:IAM(Index Allocation Map)。 IAM的存在,使SQLS对数据表的物理管理有了可能。 IAM页从混合扩展中分配,记录了8个初始页面的位置和该扩展区的位置,每个IAM页面能管理512,000个数据页面,如果数据量太大,SQLS也可以增加更多的IAM页,可以位于文件的任何位置。第一个IAM页被称为FirstIAM,其中记录了以后的IAM页的位置。 数据页和文本/图像页互反,前者保存非文本/图像类型的数据,因为它们都不超过8K的容量,后者则只保存超过8K容量的文本或图像类型数据。而索引页顾名思义,保存的是与索引结构相关的数据信息。了解页面的问题有助我们下一步准确理解SQLS维护索引的方式,如页拆分、填充因子等。 二、索引的基本概念 索引是一种特殊类型的数据库对象,它与表有着密切的联系。 索引是为检索而存在的。如一些书籍的末尾就专门附有索引,指明了某个关键字在正文中的出现的页码位置,方便我们查找,但大多数的书籍只有目录,目录不是索引,只是书中内容的排序,并不提供真正的检索功能。可见建立索引要单独占用空间;索引也并不是必须要建立的,它们只是为更好、更快的检索和定位关键字而存在。 再进一步说,我们要在图书馆中查阅图书,该怎么办呢?图书馆的前台有很多叫做索引卡片柜的小柜子,里面分了若干的类别供我们检索图书,比如你可以用书名的笔画顺序或者拼音顺序作为查找的依据,你还可以从作者名的笔画顺序或拼音顺序去查询想要的图书,反正有许多检索方式,但有一点很明白,书库中的书并没有按照这些卡片柜中的顺序排列——虽然理论上可以这样做,事实上,所有图书的脊背上都人工的粘贴了一个特定的编号①,它们是以这个顺序在排列。索引卡片中并没有指明这本书摆放在书库中的第几个书架的第几本,仅仅指明了这个特定的编号。管理员则根据这一编号将请求的图书返回到读者手中。这是很形象的例子,以下的讲解将会反复用到它。 SQLS在安装完成之后,安装程序会自动创建master、model、tempdb等几个特殊的系统数据库,其中master是SQLS的主数据库,用于保存和管理其它系统数据库、用户数据库以及SQLS的系统信息,它在SQLS中的地位与WINDOWS下的注册表相当。 master中有一个名为sysindexes的系统表,专门管理索引。SQLS查询数据表的操作都必须用到它,毫无疑义,它是本文主角之一。 查看一张表的索引属性,可以在查询分析器中使用以下命令:select from sysindexes where id=object_id(‘tablename’) ;而要查看表的索引所占空间的大小,可以使用系统存储过程命令:sp_spaceused tablename,其中参数tablename为被索引的表名。 三、平衡树 如果你通过书后的索引知道了一个关键字所在的页码,你有可能通过随机的翻寻,最终到达正确的页码。但更科学更快捷的方法是:首先把书翻到大概二分之一的位置,如果要找的页码比该页的页码小,就把书向前翻到四分之一处,否则,就把书向后翻到四分之三的地方,依此类推,把书页续分成更小的部分,直至正确的页码。这叫“两分法”,微软在官方教程MOC里另有一种说法:叫B树(B-Tree,Balance Tree),即平衡树。 一个表索引由若干页面组成,这些页面构成了一个树形结构。B树由“根”(root)开始,称为根级节点,它通过指向另外两个页,把一个表的记录从逻辑上分成两个部分:“枝”—--非叶级节点(Non-Leaf Level);而非叶级节点又分别指向更小的部分:“叶”——叶级节点(Leaf Level)。根节点、非叶级节点和叶级节点都位于索引页中,统称为索引节点,属于索引页的范筹。这些“枝”、“叶”最终指向了具体的数据页(Page)。在根级节点和叶级节点之间的叶又叫数据中间页。 “根”(root)对应了sysindexes表的Root字段,其中记载了非叶级节点的物理位置(即指针);非叶级节点位于根节点和叶节点之间,记载了指向叶级节点的指针;而叶级节点则最终指向数据页。这就是“平衡树”。 四、聚集索引和非聚集索引 从形式上而言,索引分为聚集索引(Clustered Indexes)和非聚集索引(NonClustered Indexes)。 聚集索引相当于书籍脊背上那个特定的编号。如果对一张表建立了聚集索引,其索引页中就包含着建立索引的列的值(下称索引键值),那么表中的记录将按照该索引键值进行排序。比如,我们如果在“姓名”这一字段上建立了聚集索引,则表中的记录将按照姓名进行排列;如果建立了聚集索引的列是数值类型的,那么记录将按照该键值的数值大小来进行排列。 非聚集索引用于指定数据的逻辑顺序,也就是说,表中的数据并没有按照索引键值指定的顺序排列,而仍然按照插入记录时的顺序存放。其索引页中包含着索引键值和它所指向该行记录在数据页中的物理位置,叫做行定位符(RID:Row ID)。好似书后面的的索引表,索引表中的顺序与实际的页码顺序也是不一致的。而且一本书也许有多个索引。比如主题索引和作者索引。 SQL Server在默认的情况下建立的索引是非聚集索引,由于非聚集索引不对表中的数据进行重组,而只是存储索引键值并用一个指针指向数据所在的页面。一个表如果没有聚集索引时,理论上可以建立249个非聚集索引。每个非聚集索引提供访问数据的不同排序顺序。 五、数据是怎样被访问的 若能真正理解了以上索引的基础知识,那么再回头来看索引的工作原理就简单和轻松多了。 (一)SQLS怎样访问没有建立任何索引数据表: Heap译成汉语叫做“堆”,其本义暗含杂乱无章、无序的意思,前面提到数据值被写进数据页时,由于每一行记录之间并没地有特定的排列顺序,所以行与行的顺序就是随机无序的,当然表中的数据页也就是无序的了,而表中所有数据页就形成了“堆”,可以说,一张没有索引的数据表,就像一个只有书柜而没有索引卡片柜的图书馆,书库里面塞满了一堆乱七八糟的图书。当读者对管理员提交查询请求后,管理员就一头钻进书库,对照查找内容从头开始一架一柜的逐本查找,运气好的话,在第一个书架的第一本书就找到了,运气不好的话,要到最后一个书架的最后一本书才找到。 SQLS在接到查询请求的时候,首先会分析sysindexes表中一个叫做索引标志符(INDID: Index ID)的字段的值,如果该值为0,表示这是一张数据表而不是索引表,SQLS就会使用sysindexes表的另一个字段——也就是在前面提到过的FirstIAM值中找到该表的IAM页链——也就是所有数据页集合。 这就是对一个没有建立索引的数据表进行数据查找的方式,是不是很没效率?对于没有索引的表,对于一“堆”这样的记录,SQLS也只能这样做,而且更没劲的是,即使在第一行就找到了被查询的记录,SQLS仍然要从头到尾的将表扫描一次。这种查询称为“遍历”,又叫“表扫描”。 可见没有建立索引的数据表照样可以运行,不过这种方法对于小规模的表来说没有什么太大的问题,但要查询海量的数据效率就太低了。 (二)SQLS怎样访问建立了非聚集索引的数据表: 如前所述,非聚集索引可以建多个,具有B树结构,其叶级节点不包含数据页,只包含索引行。假定一个表中只有非聚集索引,则每个索引行包含了非聚集索引键值以及行定位符(ROW ID,RID),他们指向具有该键值的数据行。每一个RID由文件ID、页编号和在页中行的编号组成。 当INDID的值在2-250之间时,意味着表中存在非聚集索引页。此时,SQLS调用ROOT字段的值指向非聚集索引B树的ROOT,在其中查找与被查询最相近的值,根据这个值找到在非叶级节点中的页号,然后顺藤摸瓜,在叶级节点相应的页面中找到该值的RID,最后根据这个RID在Heap中定位所在的页和行并返回到查询端。 例如:假定在Lastname上建立了非聚集索引,则执行Select From Member Where Lastname=’Ota’时,查询过程是:①SQLS查询INDID值为2;②立即从根出发,在非叶级节点中定位最接近Ota的值“Martin”,并查到其位于叶级页面的第61页;③仅在叶级页面的第61页的Martin下搜寻Ota的RID,其RID显示为N∶706∶4,表示Lastname字段中名为Ota的记录位于堆的第707页的第4行,N表示文件的ID值,与数据无关;④根据上述信息,SQLS立马在堆的第 707页第4行将该记录“揪”出来并显示于前台(客户端)。视表的数据量大小,整个查询过程费时从百分之几毫秒到数毫秒不等。 在谈到索引基本概念的时候,我们就提到了这种方式: 图书馆的前台有很多索引卡片柜,里面分了若干的类别,诸如按照书名笔画或拼音顺序、作者笔画或拼音顺序等等,但不同之处有二:① 索引卡片上记录了每本书摆放的具体位置——位于某柜某架的第几本——而不是“特殊编号”;② 书脊上并没有那个“特殊编号”。管理员在索引柜中查到所需图书的具体位置(RID)后,根据RID直接在书库中的具体位置将书提出来。 显然,这种查询方式效率很高,但资源占用极大,因为书库中书的位置随时在发生变化,必然要求管理员花费额外的精力和时间随时做好索引更新。 (三)SQLS怎样访问建立了聚集索引的数据表: 在聚集索引中,数据所在的数据页是叶级,索引数据所在的索引页是非叶级。 查询原理和上述对非聚集索引的查询相似,但由于记录是按照聚集索引中索引键值进行排序,换句话说,聚集索引的索引键值也就是具体的数据页。 这就好比书库中的书就是按照书名的拼音在排序,而且也只按照这一种排序方式建立相应的索引卡片,于是查询起来要比上述只建立非聚集索引的方式要简单得多。仍以上面的查询为例: 假定在Lastname字段上建立了聚集索引,则执行Select From Member Where Lastname=’Ota’时,查询过程是:①SQLS查询INDID值为1,这是在系统中只建立了聚集索引的标志;②立即从根出发,在非叶级节点中定位最接近Ota的值“Martin”,并查到其位于叶级页面的第120页;③在位于叶级页面第120页的Martin下搜寻到Ota条目,而这一条目已是数据记录本身;④将该记录返回客户端。 这一次的效率比第二种方法更高,以致于看起来更美,然而它最大的优点也恰好是它最大的缺点——由于同一张表中同时只能按照一种顺序排列,所以在任何一种数据表中的聚集索引只能建立一个;并且建立聚集索引需要至少相当于源表120%的附加空间,以存放源表的副本和索引中间页! 难道鱼和熊掌就不能兼顾了吗?办法是有的。 (四)SQLS怎样访问既有聚集索引、又有非聚集索引的数据表: 如果我们在建立非聚集索引之前先建立了聚集索引的话,那么非聚集索引就可以使用聚集索引的关键字进行检索,就像在图书馆中,前台卡片柜中的可以有不同类别的图书索引卡,然而每张卡片上都载明了那个特殊编号——并不是书籍存放的具体位置。这样在最大程度上既照顾了数据检索的快捷性,又使索引的日常维护变得更加可行,这是最为科学的检索方法。 也就是说,在只建立了非聚集索引的情况下,每个叶级节点指明了记录的行定位符(RID);而在既有聚集索引又有非聚集索引的情况下,每个叶级节点所指向的是该聚集索引的索引键值,即数据记录本身。 假设聚集索引建立在Lastname上,而非聚集索引建立在Firstname上,当执行Select From Member Where Firstname=’Mike’时,查询过程是:①SQLS查询INDID值为2;②立即从根出发,在Firstname的非聚集索引的非叶级节点中定位最接近Mike的值“Jose”条目;③从Jose条目下的叶级页面中查到Mike逻辑位置——不是RID而是聚集索引的指针;④根据这一指针所指示位置,直接进入位于Lastname的聚集索引中的叶级页面中到达Mike数据记录本身;⑤将该记录返回客户端。 这就完全和我们在“索引的基本概念”中讲到的现实场景完全一样了,当数据发生更新的时候,SQLS只负责对聚集索引的健值驾以维护,而不必考虑非聚集索引,只要我们在ID类的字段上建立聚集索引,而在其它经常需要查询的字段上建立非聚集索引,通过这种科学的、有针对性的在一张表上分别建立聚集索引和非聚集索引的方法,我们既享受了索引带来的灵活与快捷,又相对规避了维护索引所导致的大量的额外资源消耗。 六、索引的优点和不足 索引有一些先天不足:1:建立索引,系统要占用大约为表的1.2倍的硬盘和内存空间来保存索引。2:更新数据的时候,系统必须要有额外的时间来同时对索引进行更新,以维持数据和索引的一致性——这就如同图书馆要有专门的位置来摆放索引柜,并且每当库存图书发生变化时都需要有人将索引卡片重整以保持索引与库存的一致。 当然建立索引的优点也是显而易见的:在海量数据的情况下,如果合理的建立了索引,则会大大加强SQLS执行查询、对结果进行排序、分组的操作效率。 实践表明,不恰当的索引不但于事无补,反而会降低系统性能。因为大量的索引在进行插入、修改和删除操作时比没有索引花费更多的系统时间。比如在如下字段建立索引应该是不恰当的:1、很少或从不引用的字段;2、逻辑型的字段,如男或女(是或否)等。 综上所述,提高查询效率是以消耗一定的系统资源为代价的,索引不能盲目的建立,必须要有统筹的规划,一定要在“加快查询速度”与“降低修改速度”之间做好平衡,有得必有失,此消则彼长。这是考验一个DBA是否优秀的很重要的指标。 至此,我们一直在说SQLS在维护索引时要消耗系统资源,那么SQLS维护索引时究竟消耗了什么资源?会产生哪些问题?究竟应该才能优化字段的索引? 在上篇中,我们就索引的基本概念和数据查询原理作了详细阐述,知道了建立索引时一定要在“加快查询速度”与“降低修改速度”之间做好平衡,有得必有失,此消则彼长。那么,SQLS维护索引时究竟怎样消耗资源?应该从哪些方面对索引进行管理与优化?以下就从七个方面来回答这些问题。 一、页分裂 微软MOC教导我们:当一个数据页达到了8K容量,如果此时发生插入或更新数据的操作,将导致页的分裂(又名页拆分): 1、有聚集索引的情况下:聚集索引将被插入和更新的行指向特定的页,该页由聚集索引关键字决定; 2、只有堆的情况下:只要有空间就可以插入新的行,但是如果我们对行数据的更新需要更多的空间,以致大于了当前页的可用空间,行就被移到新的页中,并且在原位置留下一个转发指针,指向被移动的新行,如果具有转发指针的行又被移动了,那么原来的指针将重新指向新的位置; 3、如果堆中有非聚集索引,那么尽管插入和更新操作在堆中不会发生页分裂,但是在非聚集索引上仍然产生页分裂。 无论有无索引,大约一半的数据将保留在老页面,而另一半将放入新页面,并且新页面可能被分配到任何可用的页。所以,频繁页分裂,后果很严重,将使物理表产生大量数据碎片,导致直接造成I/O效率的急剧下降,最后,停止SQLS的运行并重建索引将是我们的唯一选择! 二、填充因子 然而在“混沌之初”,就可以在一定程度上避免不愉快出现:在创建索引时,可以为这个索引指定一个填充因子,以便在索引的每个叶级页面上保留一定百分比的空间,将来数据可以进行扩充和减少页分裂。填充因子是从0到100的百分比数值,设为100时表示将数据页填满。只有当不会对数据进行更改时(例如只读表中)才用此设置。值越小则数据页上的空闲空间越大,这样可以减少在索引增长过程中进行页分裂的需要,但这一操作需要占用更多的硬盘空间。 填充因子只在创建索引时执行,索引创建以后,当表中进行数据的添加、删除或更新时,是不会保持填充因子的,如果想在数据页上保持额外的空间,则有悖于使用填充因子的本意,因为随着数据的输入,SQLS必须在每个页上进行页拆分,以保持填充因子指定的空闲空间。因此,只有在表中的数据进行了较大的变动,才可以填充数据页的空闲空间。这时,可以从容的重建索引,重新指定填充因子,重新分布数据。 反之,填充因子指定不当,就会降低数据库的读取性能,其降低量与填充因子设置值成反比。例如,当填充因子的值为50时,数据库的读取性能会降低两倍!所以,只有在表中根据现有数据创建新索引,并且可以预见将来会对这些数据进行哪些更改时,设置填充因子才有意义。 三、两道数学题 假定数据库设计没有问题,那么是否象上篇中分析的那样,当你建立了众多的索引,在查询工作中SQLS就只能按照“最高指示”用索引处理每一个提交的查询呢?答案是否定的! 上篇“数据是怎样被访问的”章节中提到的四种索引方案只是一种静态的、标准的和理论上的分析比较,实际上,将在外,军令有所不从,SQLS几乎完全是“自主”的决定是否使用索引或使用哪一个索引! 这是怎么回事呢? 让我们先来算一道题:如果某表的一条记录在磁盘上占用1000字节(1K)的话,我们对其中10字节的一个字段建立索引,那么该记录对应的索引大小只有10字节(0.01K)。上篇说过,SQLS的最小空间分配单元是“页(Page)”,一个页面在磁盘上占用8K空间,所以一页只能存储8条“记录”,但可以存储800条“索引”。现在我们要从一个有8000条记录的表中检索符合某个条件的记录(有Where子句),如果没有索引的话,我们需要遍历8000条×1000字节/8K字节=1000个页面才能够找到结果。如果在检索字段上有上述索引的话,那么我们可以在8000条×10字节/8K字节=10个页面中就检索到满足条件的索引块,然后根据索引块上的指针逐一找到结果数据块,这样I/O访问量肯定要少得多。 然而有时用索引还不如不用索引快! 同上,如果要无条件检索全部记录(不用Where子句),不用索引的话,需要访问8000条×1000字节/8K字节=1000个页面;而使用索引的话,首先检索索引,访问8000条×10字节/8K字节=10个页面得到索引检索结果,再根据索引检索结果去对应数据页面,由于是检索全部数据,所以需要再访问8000条×1000字节/8K字节=1000个页面将全部数据读取出来,一共访问了1010个页面,这显然不如不用索引快。 SQLS内部有一套完整的数据索引优化技术,在上述情况下,SQLS会自动使用表扫描的方式检索数据而不会使用任何索引。那么SQLS是怎么知道什么时候用索引,什么时候不用索引的呢?因为SQLS除了维护数据信息外,还维护着数据统计信息! 四、统计信息 打开企业管理器,单击“Database”节点,右击Northwind数据库→单击“属性”→选择“Options”选项卡,观察“Settings”下的各项复选项,你发现了什么? 从Settings中我们可以看到,在数据库中,SQLS将默认的自动创建和更新统计信息,这些统计信息包括数据密度和分布信息,正是它们帮助SQLS确定最佳的查询策略:建立查询计划和是否使用索引以及使用什么样的索引。 在创建索引时,SQLS会创建分布数据页来存放有关索引的两种统计信息:分布表和密度表。查询优化器使用这些统计信息估算使用该索引进行查询的成本(Cost),并在此基础上判断该索引对某个特定查询是否有用。 随着表中的数据发生变化,SQLS自动定期更新这些统计信息。采样是在各个数据页上随机进行。从磁盘读取一个数据页后,该数据页上的所有行都被用来更新统计信息。统计信息更新的频率取决于字段或索引中的数据量以及数据更改量。比如,对于有一万条记录的表,当1000个索引键值发生改变时,该表的统计信息便可能需要更新,因为1000 个值在该表中占了10%,这是一个很大的比例。而对于有1千万条记录的表来说,1000个索引值发生更改的意义则可以忽略不计,因此统计信息就不会自动更新。 至于它们帮助SQLS建立查询计划的具体过程,限于篇幅,这里就省略了,请有兴趣的朋友们自己研究。 顺便多说一句,SQLS除了能自动记录统计信息之外,还可以记录服务器中所发生的其它活动的详细信息,包括I/O 统计信息、CPU 统计信息、锁定请求、T-SQL 和 RPC 统计信息、索引和表扫描、警告和引发的错误、数据库对象的创建/除去、连接/断开、存储过程操作、游标操作等等。这些信息的读取、设置请朋友们在SQLS联机帮助文档(SQL Server Books Online)中搜索字符串“Profiler”查找。 五、索引的人工维护 上面讲到,某些不合适的索引将影响到SQLS的性能,随着应用系统的运行,数据不断地发生变化,当数据变化达到某一个程度时将会影响到索引的使用。这时需要用户自己来维护索引。 随着数据行的插入、删除和数据页的分裂,有些索引页可能只包含几页数据,另外应用在执行大量I/O的时候,重建非聚聚集索引可以维护I/O的效率。重建索引实质上是重新组织B树。需要重建索引的情况有: 1) 数据和使用模式大幅度变化; 2)排序的顺序发生改变; 3)要进行大量插入操作或已经完成; 4)使用I/O查询的磁盘读次数比预料的要多; 5)由于大量数据修改,使得数据页和索引页没有充分使用而导致空间的使用超出估算; 6)dbcc检查出索引有问题。 六、索引的使用原则 接近尾声的时候,让我们再从另一个角度认识索引的两个重要属性----唯一性索引和复合性索引。 在设计表的时候,可以对字段值进行某些限制,比如可以对字段进行主键约束或唯一性约束。 主键约束是指定某个或多个字段不允许重复,用于防止表中出现两条完全相同的记录,这样的字段称为主键,每张表都可以建立并且只能建立一个主键,构成主键的字段不允许空值。例如职员表中“身份证号”字段或成绩表中“学号、课程编号”字段组合。 而唯一性约束与主键约束类似,区别只在于构成唯一性约束的字段允许出现空值。 建立在主键约束和唯一性约束上的索引,由于其字段值具有唯一性,于是我们将这种索引叫做“唯一性索引”,如果这个唯一性索引是由两个以上字段的组合建立的,那么它又叫“复合性索引”。 注意,唯一索引不是聚集索引,如果对一个字段建立了唯一索引,你仅仅不能向这个字段输入重复的值。并不妨碍你可以对其它类型的字段也建立一个唯一性索引,它们可以是聚集的,也可以是非聚集的。 唯一性索引保证在索引列中的全部数据是唯一的,不会包含冗余数据。如果表中已经有一个主键约束或者唯一性约束,那么当创建表或者修改表时,SQLS自动创建一个唯一性索引。但出于必须保证唯一性,那么应该创建主键约束或者唯一性键约束,而不是创建一个唯一性索引。当创建唯一性索引时,应该认真考虑这些规则:当在表中创建主键约束或者唯一性键约束时, SQLS钭自动创建一个唯一性索引;如果表中已经包含有数据,那么当创建索引时,SQLS检查表中已有数据的冗余性,如果发现冗余值,那么SQLS就取消该语句的执行,并且返回一个错误消息,确保表中的每一行数据都有一个唯一值。 复合索引就是一个索引创建在两个列或者多个列上。在搜索时,当两个或者多个列作为一个关键值时,最好在这些列上创建复合索引。当创建复合索引时,应该考虑这些规则:最多可以把16个列合并成一个单独的复合索引,构成复合索引的列的总长度不能超过900字节,也就是说复合列的长度不能太长;在复合索引中,所有的列必须来自同一个表中,不能跨表建立复合列;在复合索引中,列的排列顺序是非常重要的,原则上,应该首先定义最唯一的列,例如在(COL1,COL2)上的索引与在(COL2,COL1)上的索引是不相同的,因为两个索引的列的顺序不同;为了使查询优化器使用复合索引,查询语句中的WHERE子句必须参考复合索引中第一个列;当表中有多个关键列时,复合索引是非常有用的;使用复合索引可以提高查询性能,减少在一个表中所创建的索引数量。 综上所述,我们总结了如下索引使用原则: 1)逻辑主键使用唯一的成组索引,对系统键(作为存储过程)采用唯一的非成组索引,对任何外键列采用非成组索引。考虑数据库的空间有多大,表如何进行访问,还有这些访问是否主要用作读写。 2)不要索引memo/note 字段,不要索引大型字段(有很多字符),这样作会让索引占用太多的存储空间。 3)不要索引常用的小型表 4)一般不要为小型数据表设置过多的索引,假如它们经常有插入和删除操作就更别这样作了,SQLS对这些插入和删除操作提供的索引维护可能比扫描表空间消耗更多的时间。 七、大结局 查询是一个物理过程,表面上是SQLS在东跑西跑,其实真正大部分压马路的工作是由磁盘输入输出系统(I/O)完成,全表扫描需要从磁盘上读表的每一个数据页,如果有索引指向数据值,则I/O读几次磁盘就可以了。但是,在随时发生的增、删、改操作中,索引的存在会大大增加工作量,因此,合理的索引设计是建立在对各种查询的分析和预测上的,只有正确地使索引与程序结合起来,才能产生最佳的优化方案。 一般来说建立索引的思路是: (1)主键时常作为where子句的条件,应在表的主键列上建立聚聚集索引,尤其当经常用它作为连接的时候。 (2)有大量重复值且经常有范围查询和排序、分组发生的列,或者非常频繁地被访问的列,可考虑建立聚聚集索引。 (3)经常同时存取多列,且每列都含有重复值可考虑建立复合索引来覆盖一个或一组查询,并把查询引用最频繁的列作为前导列,如果可能尽量使关键查询形成覆盖查询。 (4)如果知道索引键的所有值都是唯一的,那么确保把索引定义成唯一索引。 (5)在一个经常做插入操作的表上建索引时,使用fillfactor(填充因子)来减少页分裂,同时提高并发度降低死锁的发生。如果在只读表上建索引,则可以把fillfactor置为100。 (6)在选择索引字段时,尽量选择那些小数据类型的字段作为索引键,以使每个索引页能够容纳尽可能多的索引键和指针,通过这种方式,可使一个查询必须遍历的索引页面降到最小。此外,尽可能地使用整数为键值,因为它能够提供比任何数据类型都快的访问速度。 SQLS是一个很复杂的系统,让索引以及查询背后的东西真相大白,可以帮助我们更为深刻的了解我们的系统。一句话,索引就象盐,少则无味多则咸。 本篇文章为转载内容。原文链接:https://blog.csdn.net/qq_28052907/article/details/75194926。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-04-30 23:10:07
97
转载
转载文章
...File( "d:/index.html");try {Document doc = Jsoup.parse(szContent);Elements elList=doc.getElementsByAttributeValue("id","vulDataTable");szContent=elList.outerHtml();Parser parser = Parser.createParser(szContent, ENCODE);NodeFilter[] filters = new NodeFilter[2];filters[0] = new NodeClassFilter(TableTag.class); filters[1] = new NodeClassFilter(LinkTag.class);NodeFilter filter =new OrFilter (filters);NodeList list = parser.extractAllNodesThatMatch(filter);String ldName="";String ldJianjie="";for (int i = 0; i < list.size(); i++) { Node node = list.elementAt(i); if(node instanceof LinkTag){String nodeHtml=node.toHtml();if(nodeHtml.contains("onclick")&&nodeHtml.contains("vul-")){if(!"".equals(ldName)&&!"".equals(ldJianjie)){//提交数据System.out.println("---commit---漏洞名称-------"+ldName);System.out.println("---commit---漏洞简介-------"+ldJianjie);ldName="";ldJianjie="";}String level="";if(nodeHtml.contains("vul-vh")){level="高危漏洞";}else if(nodeHtml.contains("vul-vm")){level="中危漏洞";}else if(nodeHtml.contains("vul-vl")){level="低危漏洞";}ldName=getLinkTagContent(nodeHtml)+"-----"+level+"------";// System.out.println("---漏洞名称-----"+getLinkTagContent(nodeHtml)+"-----"+level+"------");} }else{ldJianjie=getTableTagContent(node.toHtml());} } } catch (Exception e) {e.printStackTrace();} }/ 提取文件里面的文本信息 @param szFileName @return/public static String openFile(String szFileName) {try {BufferedReader bis = new BufferedReader(new InputStreamReader(new FileInputStream(new File(szFileName)), ENCODE));String szContent = "";String szTemp;while ((szTemp = bis.readLine()) != null) {szContent += szTemp + "\n";}bis.close();return szContent;} catch (Exception e) {return "";} }/ 提取标签<a>a</a>内的内容 return a;/public static String getLinkTagContent(String link){String content="";Pattern pattern = Pattern.compile("<a[^>]>(.?)</a>");Matcher matcher = pattern.matcher(link);if(matcher.find()){content=matcher.group(1);}return content;}/ 解析Table标签内的东西 @param table/public static String getTableTagContent(String table){Map<String,String> conMap=new HashMap<String,String>();String content="";Document doc = Jsoup.parse(table);Elements elList=doc.getElementsByAttributeValue("class","cmn_table plumb");Element el=elList.first();Elements trLists = el.select("tr");for (int i = 0; i < trLists.size(); i++) {Elements tds = trLists.get(i).select("td");String key="";String val="";for (int j = 0; j < tds.size(); j++) {String text = tds.get(j).text();if(j==0){key=text; }else{val=text; } }conMap.put(key, val);content+="|"+key+"-"+val;// System.out.println(key+"-"+val);}return content;} } 本篇文章为转载内容。原文链接:https://blog.csdn.net/zhaoguoshuai91/article/details/51802116。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-07-19 10:42:16
295
转载
转载文章
...览器访问不到图片。 index.html: <!DOCTYPE html><html xmlns:th="http://www.thymeleaf.org"><head><meta charset="UTF-8"><title>Helllo Jenkins</title></head><body><h1 style="text-align: center;">This is my first demo for Jenkins </h1><div align="center"><img th:src="@{/static/doudou.jpg}" style="width: 80%;height: 70%"></div></body></html> 浏览器错误信息: Failed to load resource: the server responded with a status of 404 () 解决方法: 在配置文件application.properties设置静态资源的访问路径 只有静态资源的访问路径为/static/时,才会处理请求spring.mvc.static-path-pattern=/static/ 重新启动后、完美解决问题 本篇文章为转载内容。原文链接:https://lebron.blog.csdn.net/article/details/117636422。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-10-19 11:16:32
248
转载
Lua
...ttempt to index a nil value (field 'address') 在这个例子中,我们尝试从my_table获取"address"对应的值,但该键并不存在于表中,因此Lua抛出了“键不存在”错误。 2. 如何安全地访问可能不存在的键 为了避免上述错误的发生,我们可以利用Lua中的条件判断和nil检查机制来安全地访问表中的键。下面是一个典型的示例: lua local my_table = {name = "John", age = 30} -- 安全访问方式:先检查键是否存在,再进行访问 if my_table.address then print(my_table.address) else print("Address is not set.") end 或者,你可以使用Lua的rawget函数,它不会触发元方法且对键的类型没有限制,同时也不会抛出错误: lua local address = rawget(my_table, "address") if address then print(address) else print("Address is not set.") end 3. 使用pairs和ipairs遍历检查键的存在性 当不确定表中有哪些键时,可以采用遍历的方式来检查: lua for key, value in pairs(my_table) do if key == "address" then print(value) break end end -- 如果是数字索引的连续数组部分,可以使用 ipairs for i = 1, my_table do if i == my_expected_index then print(my_table[i]) break end end 4. 自定义默认返回值——空合并操作符 // Lua 5.3引入了一个非常有用的特性——空合并操作符(也称为nil合并操作符)//,它可以用于提供默认值: lua local my_table = {name = "John", age = 30} print(my_table.address // "No Address") -- 输出 "No Address" 在这个例子中,如果my_table.address为nil,则会返回后面的字符串"No Address",这样就避免了键不存在的错误。 结语:思考与探讨 理解并妥善处理Lua表中键可能不存在的情况,是Lua编程过程中的重要一环。掌握这些技巧不仅可以避免程序因意外的键访问错误而崩溃,还能使我们的代码更加健壮、易读。希望本文的讨论和实例代码能帮助你更深入地理解这一问题,并在今后的编程实践中灵活运用,让Lua代码如丝般顺滑地运行。记住,编程不仅仅是解决问题,更是不断探索、学习和成长的过程。
2023-05-17 14:22:20
38
春暖花开
站内搜索
用于搜索本网站内部文章,支持栏目切换。
知识学习
实践的时候请根据实际情况谨慎操作。
随机学习一条linux命令:
chown user:group file.txt
- 改变文件的所有者和组。
推荐内容
推荐本栏目内的其它文章,看看还有哪些文章让你感兴趣。
2023-04-28
2023-08-09
2023-06-18
2023-04-14
2023-02-18
2023-04-17
2024-01-11
2023-10-03
2023-09-09
2023-06-13
2023-08-07
2023-03-11
历史内容
快速导航到对应月份的历史文章列表。
随便看看
拉到页底了吧,随便看看还有哪些文章你可能感兴趣。
时光飞逝
"流光容易把人抛,红了樱桃,绿了芭蕉。"