前端技术
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
[分布式数据库的元数据操作 ]的搜索结果
这里是文章列表。热门标签的颜色随机变换,标签颜色没有特殊含义。
点击某个标签可搜索标签相关的文章。
点击某个标签可搜索标签相关的文章。
Redis
一、引言 在分布式系统中,经常需要通过锁来协调多个进程之间的操作,以保证数据的一致性和正确性。Redis,这个强大的内存数据库小能手,在开发者圈子里可是备受宠爱。它有个绝招叫setnx命令,这已经变成了众多程序员老铁们在实现分布式锁时的常用“神器”之一了。然而,在我们用Spring Boot 2搭配Docker搭建的线上环境里,遇到了一个让人摸不着头脑的情况:当两个Java程序同时使出“setnx”命令抢夺Redis锁的时候,竟然会出现两个人都能抢到锁的怪事!这可真是让我们一众人大跌眼镜,直呼神奇。本文将尝试分析这一现象的原因,并给出解决方案。 二、问题复现 首先,我们需要准备两台Linux服务器作为开发环境,分别命名为A和B。然后,在服务器A上启动一个Spring Boot应用,并在其中加入如下代码: typescript @Autowired private StringRedisTemplate stringRedisTemplate; public void lock(String key) { String result = stringRedisTemplate.execute((ConnectionFactory connectionFactory, RedisCallback action) -> { Jedis jedis = new Jedis(connectionFactory.getConnection()); try { return jedis.setnx(key, "1"); } catch (Exception e) { log.error("lock failed", e); } finally { if (jedis != null) { jedis.close(); } } return null; }); if (result == null || !result.equals("1")) { throw new RuntimeException("Failed to acquire lock"); } } 接着,在服务器B上也启动同样的应用,并在其中执行上述lock方法。这时候我们注意到一个情况,这“lock”方法时灵时不灵的,有时候它会突然尥蹶子,抛出异常告诉我们锁没拿到;但有时候又乖巧得很,顺利就把锁给拿下了。这是怎么回事呢? 三、问题分析 经过一番研究,我们发现了问题所在。原来,当两个Java进程同时执行setnx命令时,Redis并没有按照我们的预期进行操作。咱们都知道,这个setnx命令啊,它就像个贴心的小管家。如果发现某个key还没在数据库里安家落户,嘿,它立马就动手,给创建一个新的键值对出来。这个键嘛,就是你传给它的第一个小宝贝;而这个值呢,就是紧跟在后面的那个小家伙。不过,要是这key已经存在了,那它可就不干活啦,悠哉悠哉地返回个0给你,表示这次没执行任何操作。不过在实际情况里头,如果两个进程同时发出了“setnx”命令,Redis可能不会马上做出判断,而是会选择先把这两个请求放在一起,排个队,等会儿再逐一处理。想象一下,如果有两个请求一起蹦跶过来,如果其中一个请求抢先被处理了,那么另一个请求很可能就被晾在一边,这样一来,就可能引发一些预料之外的问题啦。 四、解决方案 针对上述问题,我们可以采取以下几种解决方案: 1. 使用Redis Cluster Redis Cluster是一种专门用于处理高并发情况的分布式数据库,它可以通过将数据分散在多个节点上来提高读写效率,同时也能够避免单点故障。通过将Redis部署在Redis Cluster上,我们可以有效防止多线程竞争同一资源的情况发生。 2. 提升Java进程的优先级 我们可以在Java进程中设置更高的优先级,以便让Java进程优先获得CPU资源。这样,即使有两个Java程序小哥同时按下“setnx”这个按钮,也可能会因为CPU这个大忙人只能服务一个请求,导致其中一个程序小哥暂时抢不到锁,只能干等着。 3. 使用Redis的其他命令 除了setnx命令外,Redis还提供了其他的命令来实现分布式锁的功能,例如blpop、brpoplpush等。这些命令有个亮点,就是能把锁的状态存到Redis这个数据库里头,这样一来,就巧妙地化解了多个线程同时抢夺同一块资源的矛盾啦。 五、总结 总的来说,Redis的setnx命令是一个非常有用的工具,可以帮助我们解决分布式系统中的许多问题。不过呢,在实际使用的时候,咱们也得留心一些小细节,这样才能避免那些突如其来的状况,让一切顺顺利利的。比如在同时处理多个任务的情况下,我们得留意把控好向Redis发送请求的个数,别一股脑儿地把太多的请求挤到Redis那里去,让它应接不暇。另外,咱们也得学会对症下药,挑选适合的解决方案来解决具体的问题。比如,为了提升读写速度,我们可以考虑使个巧劲儿,用上Redis Cluster;再比如,为了避免多个线程争抢同一块资源引发的“战争”,我们可以派出其他命令来巧妙化解这类矛盾。最后,我们也应该不断地学习和探索,以便更好地利用Redis这个强大的工具。
2023-05-29 08:16:28
270
草原牧歌_t
Impala
Impala与大数据量处理挑战:深度解析与实例探讨 1. 引言 在当今的大数据世界里,Impala作为一款基于Hadoop的开源MPP(大规模并行处理)SQL查询引擎,因其对HDFS和HBase的支持以及高效的交互式查询能力而广受青睐。然而,在面对大数据量的处理场景时,Impala的表现并不总是尽如人意。在这篇文章里,我们要好好掰扯一下Impala在对付海量数据时可能遇到的那些头疼问题。咱不仅会通过实际的代码实例,抽丝剥茧地找出问题背后的秘密,还会带着咱们作为探索者的人性化视角和情感化的思考过程,一起走进这场大数据的冒险之旅。 2. Impala的基本原理与优势 首先,让我们回顾一下Impala的设计理念。你知道Impala吗?这家伙可厉害了,它采用了超级酷炫的分布式架构设计,可以直接从HDFS或者HBase这些大数据仓库里拽出数据来用,完全不需要像传统那样繁琐地进行ETL数据清洗和转化过程。这样一来,你就能享受到飞一般的速度和超低的查询延迟,轻轻松松实现SQL查询啦!这全靠它那个聪明绝顶的查询优化器和咱们亲手用C++编写的执行引擎,让你能够瞬间对海量数据进行各种复杂的分析操作,就像在现实生活中实时互动一样流畅。 sql -- 示例:使用Impala查询HDFS上的表数据 USE my_database; SELECT FROM large_table WHERE column_a = 'value'; 3. Impala在大数据量下的性能瓶颈 然而,尽管Impala具有诸多优点,但在处理超大数据集时,它却可能面临以下挑战: - 内存资源限制:Impala在处理大量数据时严重依赖内存。当Impala Daemon的内存不够用,无法承载更多的工作负载时,就可能会引发频繁的磁盘数据交换(I/O操作),这样一来,查询速度可就要大打折扣啦,明显慢下来不少。例如,如果一个大型JOIN操作无法完全装入内存,就可能引发此类问题。 sql -- 示例:假设两个大表join操作超出内存限制 SELECT a., b. FROM large_table_a AS a JOIN large_table_b AS b ON a.key = b.key; - 分区策略与数据分布:Impala的性能也受到表分区策略的影响。假如数据分布得不够均匀,或者咱们分区的方法没整对,就很可能让部分节点“压力山大”,这样一来,整体查询速度也跟着“掉链子”啦。 - 并发查询管理:在高并发查询环境下,Impala的资源调度机制也可能成为制约因素。特别是在处理海量数据的时候,大量的同时请求可能会把集群资源挤得够呛,这样一来,查询响应的速度就难免会受到拖累了。 4. 针对性优化措施与思考 面对以上挑战,我们可以采取如下策略来改善Impala处理大数据的能力: - 合理配置硬件资源:根据实际业务需求,为Impala集群增加更多的内存资源,确保其能够有效应对大数据量的查询任务。 - 优化分区策略:对于大数据表,采用合适的分区策略(如范围分区、哈希分区等),保证数据在集群中的均衡分布,减少热点问题。 - 调整并发控制参数:根据集群规模和业务特性,合理设置Impala的并发查询参数(如impalad.memory.limit、query.max-runtime等),以平衡系统资源分配。 - 数据预处理与缓存:对于经常访问的热数据,可以考虑进行适当的预处理和缓存,减轻Impala的在线处理压力。 综上所述,虽然Impala在处理大数据量时存在一定的局限性,但通过深入了解其内在工作机制,结合实际业务需求进行有针对性的优化,我们完全可以将其打造成高效的数据查询利器。在这个过程中,我们实实在在地感受到了人类智慧在挑战技术极限时的那股冲劲儿,同时,也亲眼目睹了科技与挑战之间一场永不停歇、像打乒乓球一样的精彩博弈。 结语 技术的发展总是在不断解决问题的过程中前行,Impala在大数据处理领域的挑战同样推动着我们在实践中去挖掘其潜力,寻求更优解。今后,随着软硬件技术的不断升级和突破,我们完全可以满怀信心地期待,Impala会在处理大数据这个大难题上更上一层楼,为大家带来更加惊艳、无可挑剔的服务体验。
2023-11-16 09:10:53
784
雪落无痕
MemCache
...mcached是一种分布式键值存储系统,它被广泛应用于Web应用程序中的缓存处理,以提高网站性能。然而,在实际应用过程中,我们可能会遇到Memcached进程占用CPU过高的问题。这不仅会影响系统的运行效率,还可能引发一系列问题。这篇文章会手把手教你一步步弄明白,为啥Memcached这个小家伙有时候会使劲霸占CPU资源,然后咱再一起商量商量怎么把它给“治”好,让它恢复正常运作。 二、Memcached进程占用CPU高的原因分析 1. Memcached配置不当 当Memcached配置不当时,会导致其频繁进行数据操作,从而增加CPU负担。比如说,要是你给数据设置的过期时间太长了,让Memcached这个家伙没法及时把没用的数据清理掉,那可能会造成CPU这老兄压力山大,消耗过多的资源。 示例代码如下: python import memcache mc = memcache.Client(['localhost:11211']) mc.set('key', 'value', 120) 上述代码中,设置的数据过期时间为120秒,即两分钟。这就意味着,即使数据已经没啥用了,Memcached这家伙还是会死拽着这些数据不放,在接下来的两分钟里持续占据着CPU资源不肯放手。 2. Memcached与大量客户端交互 当Memcached与大量客户端频繁交互时,会加重其CPU负担。这是因为每次交互都需要进行复杂的计算和数据处理操作。比如,想象一下你运营的Web应用火爆到不行,用户请求多得不得了,每个请求都得去Memcached那儿抓取数据。这时候,Memcached这个家伙可就压力山大了,CPU资源被消耗得嗷嗷叫啊! 示例代码如下: python import requests for i in range(1000): response = requests.get('http://localhost/memcached/data') print(response.text) 上述代码中,循环执行了1000次HTTP GET请求,每次请求都会从Memcached获取数据。这会导致Memcached的CPU资源消耗过大。 三、排查Memcached进程占用CPU高的方法 1. 使用top命令查看CPU使用情况 在排查Memcached进程占用CPU过高的问题时,我们可以首先使用top命令查看系统中哪些进程正在占用大量的CPU资源。例如,以下输出表示PID为31063的Memcached进程正在占用大量的CPU资源: javascript top - 13:34:47 up 1 day, 6:13, 2 users, load average: 0.24, 0.36, 0.41 Tasks: 174 total, 1 running, 173 sleeping, 0 stopped, 0 zombie %Cpu(s): 0.2 us, 0.3 sy, 0.0 ni, 99.5 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st KiB Mem : 16378080 total, 16163528 free, 182704 used, 122848 buff/cache KiB Swap: 0 total, 0 free, 0 used. 2120360 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 3106 root 20 0 1058688 135484 4664 S 45.9 8.3 1:23.79 python memcached_client.py 我们可以看到,PID为31063的Python程序正在占用大量的CPU资源。接着,我们可以使用ps命令进一步了解这个进程的情况: bash ps -p 3106 2. 查看Memcached配置文件 在确认Memcached进程是否异常后,我们需要查看其配置文件,以确定是否存在配置错误导致的高CPU资源消耗。例如,以下是一个默认的Memcached配置文件(/etc/memcached.conf)的一部分: php-template Default MaxItems per key (65536). default_maxbytes 67108864 四、解决Memcached进程占用CPU高的方案 1. 调整Memcached配置 根据Memcached配置不当的原因,我们可以调整相关参数来降低CPU资源消耗。例如,可以减少过期时间、增大最大数据大小等。以下是修改过的配置文件的一部分: php-template Default MaxItems per key (131072). default_maxbytes 134217728 Increase expiration time to reduce CPU usage. default_time_to_live 14400 2. 控制与Memcached的交互频率 对于因大量客户端交互导致的高CPU资源消耗问题,我们可以采取一些措施来限制与Memcached的交互频率。例如,可以在服务器端添加限流机制,防止短时间内产生大量请求。或者,优化客户端代码,减少不必要的网络通信。 3. 提升硬件设备性能 最后,如果其他措施都无法解决问题,我们也可以考虑提升硬件设备性能,如增加CPU核心数量、扩大内存容量等。但这通常不是最佳解决方案,因为这可能会带来更高的成本。 五、结论 总的来说,Memcached进程占用CPU过高是一个常见的问题,其产生的原因是多种多样的。要真正把这个问题给揪出来,咱们得把系统工具和实际操作的经验都使上劲儿,得像钻井工人一样深入挖掘Memcached这家伙的工作内幕和使用门道。只有这样,才能真正找到问题的关键所在,并提出有效的解决方案。 感谢阅读这篇文章,希望对你有所帮助!
2024-01-19 18:02:16
96
醉卧沙场-t
Etcd
...如何使用Etcd进行分布式事务管理? 1. 初识Etcd 为什么我们需要它? 兄弟们,说到分布式系统,大家是不是都有一种既爱又恨的感觉?爱的是它可以扩展到成千上万台机器,恨的是它的复杂性简直让人头大。尤其是当你需要处理分布式事务的时候,简直就是噩梦! 所以,今天咱们聊聊一个神器——Etcd。它是啥呢?简单说,Etcd就是一个分布式的键值存储系统,可以用来保存各种配置信息、状态数据或者元数据。更重要的是,它支持分布式锁、事件通知、一致性协议(Raft),简直是分布式事务管理的好帮手! 不过在开始之前,我想问问你们:有没有想过为什么分布式事务这么难搞? 思考一下: - 如果两个节点同时修改同一个资源怎么办? - 数据怎么保证一致性? - 怎么避免死锁? 这些问题都是痛点啊!而Etcd通过一些机制,比如分布式锁和事务操作,可以很好地解决这些问题。接下来,咱们就一步步看看怎么用它来搞定分布式事务。 --- 2. Etcd的基本概念 锁、事务、观察者 首先,咱们得了解几个核心概念,不然看代码的时候会懵圈的。 2.1 分布式锁 分布式锁的核心思想就是:多个节点共享同一把锁,谁抢到这把锁,谁就能执行关键逻辑。Etcd提供了lease(租约)功能,用来模拟分布式锁。 举个栗子: python import etcd3 client = etcd3.client(host='localhost', port=2379) 创建一个租约,有效期为5秒 lease = client.lease(5) 给某个key加上这个租约 client.put(key='/my-lock', value='locked', lease=lease) 这段代码的意思是:我给/my-lock这个key绑定了一个5秒的租约。只要这个key存在,别的节点就不能再获取这把锁了。如果租约过期了,锁也就自动释放了。 2.2 事务操作 Etcd支持原子性的事务操作,也就是要么全部成功,要么全部失败。这种特性非常适合用来保证分布式事务的一致性。 比如,我们想做一个转账操作: python 检查账户A是否有足够的余额 如果余额足够,扣掉金额并增加到账户B success, _ = client.transaction( compare=[ client.transactions.version('/account/A') > 0, client.transactions.value('/account/A') >= '100' ], success=[ client.transactions.put('/account/A', '50'), client.transactions.put('/account/B', '100') ], failure=[] ) if success: print("Transaction succeeded!") else: print("Transaction failed.") 这里咱们用transaction()方法定义了一个事务,先检查账户A的余额是否大于等于100,如果是的话,就把钱从A转到B。整个过程啊,要么全都搞定,要么就啥也不干,这不就是分布式事务最理想的状态嘛! 2.3 观察者模式 Etcd还有一个很酷的功能叫观察者模式,你可以监听某个key的变化,并实时做出反应。这对于监控系统状态或者触发某些事件非常有用。 比如: python for event in client.watch('/my-key'): print(event) 这段代码会一直监听/my-key的变化,一旦有更新就会打印出来。 --- 3. 实战演练 用Etcd实现分布式事务 现在咱们来实战一下,看看怎么用Etcd搞定分布式事务。假设我们要实现一个简单的库存管理系统。 3.1 场景描述 假设我们有两个服务A和服务B,服务A负责扣减库存,服务B负责记录日志。要让这两个步骤像一个整体似的,中间不能出岔子,那我们就得靠Etcd来管着分布式锁和事务了。 3.2 代码实现 Step 1: 初始化Etcd客户端 python import etcd3 client = etcd3.client(host='localhost', port=2379) Step 2: 获取分布式锁 python 创建一个租约,有效期为10秒 lease = client.lease(10) 尝试获取锁 lock_key = '/inventory-lock' try: lock_result = client.put(lock_key, 'locked', lease=lease) print("Lock acquired!") except Exception as e: print(f"Failed to acquire lock: {e}") Step 3: 执行事务操作 python 假设当前库存是100件 stock_key = '/inventory' current_stock = int(client.get(stock_key)[0].decode('utf-8')) if current_stock >= 10: 开始事务 success, _ = client.transaction( compare=[ client.transactions.version(stock_key) == current_stock ], success=[ client.transactions.put(stock_key, str(current_stock - 10)) ], failure=[] ) if success: print("Inventory updated successfully!") else: print("Failed to update inventory due to race condition.") else: print("Not enough stock available.") Step 4: 释放锁 python 租约到期后自动释放锁 lease.revoke() print("Lock released.") --- 4. 总结与展望 写到这里,我觉得咱们已经掌握了如何用Etcd来进行分布式事务管理。其实啊,事情没那么吓人!别看整个流程听着挺绕的,但只要你把分布式锁、事务操作还有观察者模式这些“法宝”都搞明白了,不管啥情况都能游刃有余地搞定,妥妥的! 不过,我也想提醒大家,分布式事务并不是万能药。有时候,过度依赖分布式事务反而会让系统变得更加复杂。所以,在实际开发中,我们需要根据业务需求权衡利弊。 最后,希望大家都能用好Etcd这个利器,让自己的分布式系统更加健壮和高效!如果你还有其他问题,欢迎随时来找我讨论,咱们一起进步!
2025-03-21 15:52:27
55
凌波微步
Cassandra
...sandra,那可是分布式数据库里的大明星啊!它特别在行的就是对付海量数据和超高并发的请求,简直是这方面的扛把子!不过,Cassandra也有它的烦恼——那就是缓存问题。 在Cassandra中,缓存是提高读性能的重要手段。无论是Key Cache还是Row Cache,它们都能显著提升查询速度。但是,缓存并不是万能的,它也有容量限制。一旦缓存满了,就得进行清理,否则新的数据就没地方存放了。这就引出了我们今天的主题——缓存清洗策略。 缓存清洗策略的核心在于平衡内存使用与性能需求。如果清洗策略不当,可能会导致频繁的缓存失效,从而影响应用性能。所以,咱们得好好研究一下,如何让缓存既高效又稳定。 --- 2. Key Cache 缓存主键索引 先来说说Key Cache。它是用来缓存表的主键索引的。每次Cassandra要查东西的时候,它都会先翻翻Key Cache这个小本本,看看主键索引在不在里面。要是找到了,就顺着线索去磁盘上把数据给捞出来。这样可以大幅减少磁盘I/O操作。 2.1 缓存清洗策略:LRU vs. LRU + TTL Cassandra默认使用的是LRU(Least Recently Used)算法来管理Key Cache。LRU的意思是最少最近使用的缓存会被优先淘汰。简单来说,就是谁最近没被访问过,谁就倒霉。 不过,Cassandra还提供了一种更灵活的策略——结合TTL(Time To Live)。通过设置TTL,我们可以指定缓存项的有效期。就算是刚刚才用到的缓存,如果超过了规定的时间,照样会被踢走。 示例代码: java // 设置Key Cache大小为100MB,并启用TTL功能 Cluster cluster = Cluster.builder() .addContactPoint("127.0.0.1") .withQueryOptions(new QueryOptions().setConsistencyLevel(ConsistencyLevel.ONE)) .withPoolingOptions(new PoolingOptions().setMaxSimultaneousRequestsPerConnectionLocal(128)) .withCodecRegistry(DefaultCodecRegistry.DEFAULT) .withConfigLoader(new ConfigLoader() { @Override public Config loadConfig() { return ConfigFactory.parseString( "cassandra.key_cache_size_in_mb: 100\n" + "cassandra.key_cache_save_period: 14400\n" + "cassandra.key_cache_tti_seconds: 3600" ); } }) .build(); 在这个例子中,我们设置了Key Cache的大小为100MB,并启用了TTL功能,TTL时间为3600秒(即1小时)。这就相当于说,哪怕某个东西刚被人用过没多久,但只要超过了1个小时,就会被系统踢走,不管三七二十一,直接清掉! --- 3. Row Cache 缓存整行数据 接下来聊聊Row Cache。Row Cache就像是个专门存整行数据的小金库,特别适合那种经常被人翻出来看,但几乎没人动它的东西。相比Key Cache,Row Cache的命中率更高,但占用的内存也更多。 3.1 缓存清洗策略:手动控制 Row Cache的清洗策略相对简单,主要依赖于手动配置。你可以通过调整row_cache_size_in_mb参数来控制Row Cache的大小。如果Row Cache满了,Cassandra会根据LRU算法淘汰最老的缓存项。 思考过程: 说实话,Row Cache的使用场景比较有限。Row Cache虽然能加快访问速度,但它特别“占地儿”,把内存占得满满当当的。更麻烦的是,它还爱“喜新厌旧”——一旦被踢出去,下次再想用的时候就得老老实实重新把数据装回来,挺折腾的。这不仅增加了延迟,还可能导致系统抖动。所以,在实际项目中,我建议谨慎使用Row Cache。 示例代码: yaml 配置Row Cache大小为50MB cassandra.row_cache_size_in_mb: 50 这段配置非常直观,直接设置了Row Cache的大小为50MB。要是你的电脑内存还挺空闲的,而且有些数据你经常要用到的话,那就可以试试打开 Row Cache 这个功能,这样能让你查东西的时候更快一点! --- 4. 缓存清洗的挑战与优化 最后,我想谈谈缓存清洗面临的挑战以及一些优化思路。 4.1 挑战:缓存一致性与性能平衡 缓存清洗的一个重要挑战是如何保持一致性。例如,当某个数据被更新时,缓存中的旧版本应该及时失效。然而,频繁的缓存失效会导致性能下降。所以啊,咱们得找那么个折中的办法,既能保证缓存里的数据跟实际的是一模一样的,又不用老是去清理它,省得麻烦。 我的理解: 其实,这个问题的本质是权衡。咱得好好琢磨这缓存的事儿啊!一方面呢,可不能让它变成脏数据的老窝,不然麻烦就大了;另一方面嘛,又希望能把缓存稳住,别老是频繁地刷新清洗,太折腾了。我觉得,可以通过动态调整TTL值来解决这个问题。比如说,那些经常要更新的数据,咱们就给它设个短一点的TTL(就是“生存时间”啦),这样过段时间就自动清理掉,省得占地方。但要是那些很少更新的数据呢,就可以设个长点的TTL,让它在那儿多待会儿,不用频繁操心。 4.2 优化:监控与调参 另一个重要的优化方向是监控和调参。Cassandra自带一堆超实用的监控数据,像缓存命中率这种关键指标,还有缓存命中的具体时间啥的,都能一清二楚地给你展示出来!通过这些指标,我们可以实时了解缓存的状态,并据此调整参数。 实际经验: 记得有一次,我们的Key Cache命中率突然下降,经过排查发现是因为缓存大小设置得太小了。嘿,咱们就实话实说吧!之前Key Cache的容量才50MB,小得可怜,后来一狠心把它调大到200MB,结果怎么样?效果立竿见影啊,命中率直接飙升了20%以上,简直像是给系统开挂了一样!所以,定期监控和动态调整参数是非常必要的。 --- 5. 结语 好了,到这里,关于Cassandra的缓存清洗策略就聊完了。总的来说,缓存清洗是个复杂但有趣的话题。它考验着我们的技术水平,也锻炼着我们的耐心和细心。 希望大家在实际工作中,能够根据自己的业务特点,合理选择缓存策略。记住,没有一成不变的最佳实践,只有最适合你的解决方案。 好了,今天就到这里吧!如果你还有其他问题,欢迎随时来找我讨论。咱们下次再见啦!👋
2025-05-11 16:02:40
68
心灵驿站
Consul
...于那些希望进一步了解分布式系统服务发现、配置管理以及相关工具和技术发展的读者来说,以下内容将为您提供有益的 最近,HashiCorp发布了Consul 1.10版本,引入了多项改进和新功能,其中包括增强的KV Store性能和扩展性。这一版本优化了对大型数据集的支持,并提高了查询与操作的响应速度,使得Consul在处理复杂分布式架构中的配置和服务发现时更为高效(来源:HashiCorp官方博客,发布日期:202X年X月X日)。 同时,InfoQ的一篇深度分析文章《Consul在微服务架构中的实践与挑战》详细阐述了Consul如何在实际场景中解决服务治理问题,并对比了与其他服务发现工具如Etcd和Zookeeper的异同。作者从一致性算法、容错机制以及社区支持等方面展开讨论,为读者提供了全面而实用的指导(来源:InfoQ,发表日期:202X年X月X日)。 此外,随着云原生技术的发展,CNCF基金会下的开源项目Linkerd和Istio等服务网格解决方案也在服务发现领域崭露头角。它们与Consul虽有功能重叠,但在抽象层次、自动化运维以及安全策略方面有所区别。通过对比研究这些新兴技术,《云原生时代的Consul与服务网格之争》一文为我们揭示了未来服务发现架构可能的发展趋势(来源:云技术实践杂志,出版日期:202X年X月X日)。 综上所述,持续关注Consul及其竞品的最新动态和发展趋势,结合实际应用场景理解并运用其强大的数据存储机制,将有助于提升现代分布式系统的可靠性和可维护性。
2024-03-04 11:46:36
433
人生如戏-t
SpringBoot
...目需要与Oracle数据库交互,而我选用了Druid作为数据源管理工具。事情本来挺顺的,大家都觉得没啥问题,结果有一天,我们的系统突然蹦出个消息,说啥“查询超时”!就那么一下,气氛瞬间紧张了,感觉空气都凝固了似的。 当时我整个人都懵了——这到底是什么情况?是Oracle的问题吗?还是Spring Boot的锅?或者是我对Druid的理解还不够深入?带着这些疑问,我开始了一段探索之旅。今天,我想把这段经历分享给大家,希望能帮助那些和我一样遇到类似问题的朋友。 --- 2. 什么是“查询超时”? 简单来说,“查询超时”就是你的SQL语句执行的时间超过了设定的最大允许时间,导致系统直接抛出异常。哎呀,这种情况在实际开发里真的挺常见的,特别是那种高并发的场景。你要是数据库连接池没配好,那问题就容易冒出来了,简直防不胜防! 对于我来说,这个问题尤其令人头疼,因为我们的项目依赖于Oracle数据库,而Oracle本身就是一个功能强大的关系型数据库,但同时也有一些“坑”。比如说啊,它的默认查询超时时间可能设得有点短,要是咱们不改一下这个设置,那查询的时候就容易卡壳儿,最后连结果都拿不到。 --- 3. Spring Boot与Druid集成的基本配置 首先,让我们回顾一下如何在Spring Boot项目中集成Druid。这是一个非常基础的操作,但也是解决问题的第一步。 3.1 添加依赖 在pom.xml文件中添加Druid的相关依赖: xml com.alibaba druid-spring-boot-starter 1.2.8 3.2 配置数据源 接着,在application.yml文件中配置Druid的数据源信息: yaml spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: oracle.jdbc.driver.OracleDriver url: jdbc:oracle:thin:@localhost:1521:orcl username: your_username password: your_password druid: initial-size: 5 max-active: 20 min-idle: 5 max-wait: 60000 time-between-eviction-runs-millis: 60000 min-evictable-idle-time-millis: 300000 validation-query: SELECT 1 FROM DUAL test-while-idle: true test-on-borrow: false test-on-return: false 这段配置看似简单,但实际上每一项参数都需要仔细斟酌。比如说啊,“max-wait”这个参数呢,就是说咱们能等连接连上的最长时间,单位是毫秒,相当于给它设了个“最长等待时间”;然后还有个“validation-query”,这个名字听起来就挺专业的,它的作用就是检查连接是不是还正常好用;最后那个“test-while-idle”,它就像是个“巡逻兵”,负责判断要不要在连接空闲的时候去检测一下这条连接还能不能用。 --- 4. 查询超时问题的初步排查 当我第一次遇到查询超时问题时,我的第一反应是:是不是Oracle那边的SQL语句太慢了?于是,我开始检查SQL语句的性能。 4.1 检查SQL语句 我用PL/SQL Developer连接到Oracle数据库,运行了一下报错的SQL语句。结果显示,这条SQL语句确实需要花费较长时间才能完成。但问题是,为什么Spring Boot会直接抛出超时异常呢? 这时,我才意识到,可能是Druid的数据源配置有问题。于是我翻阅了Druid的官方文档,发现了一个关键点:Druid默认的查询超时时间为10秒。 4.2 修改Druid的查询超时时间 为了延长查询超时时间,我在application.yml中加入了以下配置: yaml spring: datasource: druid: query-timeout: 30000 这里的query-timeout参数就是用来设置查询超时时间的,单位是毫秒。经过这次调整后,我发现查询超时的问题暂时得到了缓解。 --- 5. 进一步优化 结合Oracle的设置 虽然Druid的配置解决了部分问题,但我仍然觉得不够完美。于是,我又转向了Oracle数据库本身的设置。 5.1 设置Oracle的查询超时 在Oracle中,可以通过设置statement_timeout参数来控制查询超时时间。这个参数可以在会话级别或全局级别进行设置。 例如,在Spring Boot项目中,我们可以通过JDBC连接字符串传递这个参数: yaml spring: datasource: url: jdbc:oracle:thin:@localhost:1521:orcl?oracle.net.CONNECT_TIMEOUT=30000&oracle.jdbc.ReadTimeout=30000 这里的CONNECT_TIMEOUT和ReadTimeout分别表示连接超时时间和读取超时时间。通过这种方式,我们可以进一步提高系统的容错能力。 --- 6. 我的感悟与总结 经过这次折腾,我对Spring Boot与Druid的集成有了更深的理解。说实话,好多技术难题没那么玄乎,就是看着吓人而已。只要你肯静下心来琢磨琢磨,肯定能想出个辙来! 在这里,我也想给新手朋友们一些建议: 1. 多看官方文档 无论是Spring Boot还是Druid,它们的官方文档都非常详细,很多时候答案就在那里。 2. 学会调试 遇到问题时,不要急于求解,先用调试工具一步步分析问题所在。 3. 保持耐心 技术问题往往需要反复尝试,不要轻易放弃。 最后,我想说的是,编程之路充满了挑战,但也正因为如此才显得有趣。希望大家都能在这个过程中找到属于自己的乐趣! --- 好了,这篇文章就到这里啦!如果你也有类似的经历或想法,欢迎在评论区跟我交流哦!
2025-04-21 15:34:10
40
冬日暖阳_
Etcd
... 一、引言 在分布式系统中,日志管理是确保系统稳定性和高效运行的关键组件之一。哎呀,你知道嘛,Etcd 这个家伙,它可是个开源的键值存储数据库,专治那些分布式系统里的小病小痛。它最大的本事就是稳定和一致性,就像你的老朋友一样,无论你什么时候需要它,它总是在那,不离不弃。所以,当小伙伴们在构建分布式系统的时候,它就成了大家的首选,就像你去超市买东西,总是会先看看自己常买的那几样。Etcd 就是那种能让你用得顺心,用得放心的好帮手!哎呀,你知道的,在我们真正操作的时候,怎样才能把那些一大堆的日志数据整理得井井有条,防止各种设定撞车,这事儿还真挺让人头疼的。就像是在解一道谜题,需要咱们仔细琢磨才行。 二、日志清理策略的重要性 在Etcd集群中,日志记录了所有操作的历史,包括数据变更、事务执行等。哎呀,你想象一下,就像是你每天扔垃圾,一开始还行,但日子一长,你家的垃圾桶就快装不下了,对吧?同样的道理,当咱们的系统里有好多好多机器(我们叫它们集群)一起工作的时候,它们产生的日志文件就像垃圾一样,越堆越多。时间一长,这些日志文件堆积如山,占用了咱们宝贵的硬盘空间,得赶紧想办法清理或者优化一下,不然电脑大哥就要抗议了!因此,合理的日志清理策略不仅能优化存储空间,还能提升系统性能。哎呀,制定并执行这些策略的时候,可得小心点,别一不小心就碰到了雷区,搞出个策略冲突,结果数据丢了,或者整出些乱七八糟的不可预知状况来。咱们得稳扎稳打,确保每一步都走对了,这样才能避免踩坑。 三、策略冲突的常见类型 策略冲突主要表现在以下几个方面: 1. 数据冗余 在清理日志时,如果策略过于激进,可能会删除关键历史数据,导致后续查询或恢复操作失败。 2. 一致性问题 不同节点之间的日志清理可能不一致,造成集群内数据的一致性被破坏。 3. 性能影响 频繁的日志清理操作可能对系统性能产生负面影响,尤其是在高并发场景下。 4. 数据完整性 错误的清理策略可能导致重要数据的永久丢失。 四、案例分析 Etcd中的日志清理策略冲突 假设我们正在管理一个Etcd集群,用于存储服务配置信息。为了优化存储空间并提高响应速度,我们计划实施定期的日志清理策略。具体策略如下: - 策略一:每日凌晨0点,清理所有超过7天历史的过期日志条目。 - 策略二:每月末,清理所有超过30天历史的过期日志条目。 问题:当策略一和策略二同时执行时,可能会出现冲突。想象一下,就像你家的书架,有一天你整理了书架(策略一),把一些不再需要的书拿走了,但过了22天,你的朋友又来帮忙整理(策略二),又把一些书从书架上取了下来。这样一来,原本在书架上的书,因为两次整理,可能就不见了,这就是数据丢失的意思。 五、解决策略 优化日志清理逻辑 为了解决上述策略冲突,我们可以采取以下措施: 1. 引入版本控制 在Etcd中,每条日志都关联着一个版本号。通过维护版本号,可以准确追踪每个操作的历史状态,避免不必要的数据删除。 代码示例: go // 假设etcdClient为Etcd客户端实例 resp, err := etcdClient.Put(context.Background(), "/config/key", "value", clientv3.WithVersion(1)) if err != nil { log.Fatalf("Failed to put value: %s", err) } 2. 实施并行清理机制 设计一个系统级别的时间线清理逻辑,确保同一时间点的数据不会被重复清理。 代码示例: go // 清理逻辑函数 func cleanupLogs() error { // 根据时间戳进行清理,避免冲突 // 实现细节略去 return nil } 3. 引入审计跟踪 对于关键操作,如日志清理,记录详细的审计日志,便于事后审查和问题定位。 代码示例: go // 审计日志记录函数 func auditLog(operation string, timestamp time.Time) { // 记录审计日志 // 实现细节略去 } 六、总结与反思 通过上述策略和代码示例的讨论,我们可以看到在Etcd集群中管理日志清理策略时,需要细致考虑各种潜在的冲突和影响。哎呀,你得知道,咱们要想在项目里防住那些让人头疼的策略冲突,有几个招儿可使。首先,咱们得搞个版本控制系统,就像有个大本营,随时记录着每个人对代码的修改,这样就算有冲突,也能轻松回溯,找到问题源头。然后,咱还得上个并行清理机制,就像是给团队的工作分配任务时,能确保每个人都清楚自己的责任,不会乱了套,这样就能大大减少因为分工不明产生的冲突。最后,建立一个审计跟踪系统,就相当于给项目装了个监控,每次有人改动了什么,都得有迹可循,这样一来,一旦出现矛盾,就能快速查清谁是谁非,解决起来也快多了。这三招合在一起,简直就是防冲突的无敌组合拳啊!嘿,兄弟!你得知道,监控和评估清理策略的执行效果,然后根据实际情况灵活调整,这可是保证咱们系统健健康康、高效运作的不二法门!就像咱们打游戏时,随时观察自己的状态和环境变化,及时调整战术一样,这样才能稳坐钓鱼台,轻松应对各种挑战嘛! --- 通过本文的探讨,我们不仅深入理解了Etcd集群日志清理策略的重要性和可能遇到的挑战,还学习了如何通过实际的代码示例来解决策略冲突,从而为构建更稳定、高效的分布式系统提供了实践指导。
2024-07-30 16:28:05
456
飞鸟与鱼
转载文章
... , HDFS是一种分布式文件系统,专为在大规模集群环境中存储和处理海量数据而设计。在本文语境中,HDFS的主要特点是支持一次写入多次读取且不支持文件修改,每个文件被划分为固定大小的块(如文中提到的128M),并分布在多台计算机节点上以实现高效的数据存储与访问。用户可以通过Hadoop提供的shell命令或客户端API进行文件上传、下载、复制、移动、删除等操作,并可以对文件权限、所有者、组以及其他元数据进行管理。 NameNode (NN) , 在HDFS架构中,NameNode是主控节点,负责整个文件系统的元数据管理。它维护着文件系统的目录树结构、文件到数据块的映射关系以及每个数据块所在的DataNode列表。当客户端发起文件操作请求时,首先会与NameNode交互获取必要的元数据信息,确保数据操作能够在正确的DataNode上执行。为了提高系统的可靠性和可用性,实际生产环境中通常会部署Secondary NameNode或启用HA高可用方案来辅助或替代NameNode工作。 Secondary NameNode (2NN) , Secondary NameNode是Hadoop早期版本中提供的一种辅助服务角色,用于减轻NameNode的工作负担,尤其是在定期合并FsImage(文件系统镜像)和EditLog(编辑日志)方面。虽然名为“Secondary”,但它并不是NameNode的实时备份节点,不能直接接管NameNode的工作。其主要职责是在预定的时间间隔内,从NameNode获取FsImage和EditLog,将它们合并成新的FsImage,然后将其推送给NameNode,这样NameNode就可以用新合并的FsImage替换旧的FsImage,从而释放一部分资源并减少系统恢复时间。随着Hadoop的发展,更先进的高可用(High Availability, HA)解决方案逐渐取代了Secondary NameNode的角色,例如使用多个Active/Standby NameNode节点。
2023-12-05 22:55:20
278
转载
MySQL
...慌乱与冷静 作为一个数据库运维人员,每天面对着各种各样的问题,而当看到MySQL的日志文件里充满了大量的错误信息时,我的第一反应通常是——“天啊!这是什么情况?”尤其是在半夜加班的时候,这种感觉尤其强烈。 不过,作为一名资深的技术人,我很快意识到,慌张解决不了任何问题。咱们先别急着慌,坐下来好好琢磨琢磨这些错误到底是啥意思,到底是咋冒出来的,然后想想接下来该怎么处理才好。于是,我开始仔细阅读日志内容,并尝试重现这些错误。 比如,最近我在维护的一个生产环境下的MySQL服务器上,突然发现日志里出现了大量这样的错误信息: [ERROR] InnoDB: Operating system error number 24 in a file operation. 这让我有点懵,因为我之前从未遇到过类似的错误。所以,我决定深入研究一下这个问题,看看能不能找到解决方案。 --- 2. 错误日志解读 从表面现象到本质原因 首先,我需要弄清楚这个错误到底意味着什么。我翻了翻官方文档,又逛了逛一些社区论坛,感觉这错误八成跟操作系统里的文件操作有关系。具体来说,错误号24在Linux系统中表示“Too many open files”(打开的文件太多)。 这让我立刻联想到,可能是因为MySQL的某些进程打开了过多的文件句柄,导致操作系统限制了它进一步的操作。为了验证这一点,我执行了一个简单的命令来检查当前系统的文件描述符限制: bash ulimit -n 结果显示默认值为1024。这意味着每个进程最多只能同时打开1024个文件。说实话,咱们的MySQL实例现在正忙着应付一大堆同时连进来的需求,还得折腾临时表呢。这么一看,那个限制就跟挠痒痒似的——太不够用了! 接下来,我查看了MySQL的配置文件my.cnf,发现确实没有显式设置文件描述符的上限。于是,我修改了配置文件,将open_files_limit参数调整为更大的值: ini [mysqld] open_files_limit=65535 然后重启了MySQL服务,再次检查日志,果然,错误消失了! --- 3. 实践中的代码调试与优化 当然,仅仅解决问题还不够,我还想进一步优化整个系统的性能。于是,我编写了一些脚本来监控MySQL的运行状态,特别是文件描述符的使用情况。 以下是一个简单的Python脚本,用于统计MySQL当前使用的文件描述符数量: python import psutil import subprocess def get_mysql_open_files(): 获取所有MySQL进程ID mysql_pids = [] result = subprocess.run(['pgrep', 'mysqld'], capture_output=True, text=True) for line in result.stdout.splitlines(): mysql_pids.append(int(line)) total_open_files = 0 for pid in mysql_pids: try: proc = psutil.Process(pid) open_files = len(proc.open_files()) print(f"Process {pid} has opened {open_files} files.") total_open_files += open_files except Exception as e: print(f"Error checking process {pid}: {e}") print(f"Total open files by MySQL processes: {total_open_files}") if __name__ == "__main__": get_mysql_open_files() 运行这个脚本后,我发现某些特定的查询会导致文件描述符迅速增加。经过分析,这些问题主要出现在涉及大文件读写的场景中。所以呢,我觉得咱们开发的小伙伴们得好好捯饬捯饬这些查询语句啦!比如说,能不能少建那些没用的临时表啊?再比如,能不能换个更快的存储引擎啥的?反正就是得让这个程序跑得更顺畅些,别老是卡在那里干瞪眼不是? --- 4. 总结与反思 从问题中学到的东西 回顾这次经历,我深刻体会到,处理数据库问题时,不能仅凭直觉行事,而是要结合实际数据和技术手段,逐步排查问题的根本原因。同时,我也认识到,预防胜于治疗。如果能在日常运维中提前做好监控和预警,就可以避免很多突发状况。 最后,我想分享一点个人感悟:技术之路永无止境,每一次遇到难题都是一次成长的机会。说实话,有时候真的会觉得头大,甚至怀疑自己是不是走错了路。但我觉得啊,这就好比在黑暗里找钥匙,你得不停地摸索、试错才行。只要别轻易放弃,一直在学、一直在练,总有一天你会发现,“!原来它在这儿呢!”就跟我在处理这个MySQL报错的时候似的,最后不光把问题搞定了,还顺带学了不少实用的招儿呢! 如果你也遇到了类似的情况,不妨试试上面提到的方法,也许能帮到你!
2025-04-17 16:17:44
109
山涧溪流_
DorisDB
...》 引言:数据之海的波涛 在数据管理的世界里,DorisDB无疑是一艘载满现代数据处理技术的巨轮。哎呀,这家伙可真是个宝啊!不仅性能杠杠的,稳定性也是没得说,而且还能轻松升级扩容,怪不得那么多大公司都离不开它,用它来做数据的存储和分析,简直是如虎添翼!然而,就像任何航海之旅,DorisDB航行中也会遭遇风浪——“写入失败”。嘿,兄弟!这篇文章就像是一场探险之旅,带你深入揭秘这个棘手问题的真相。咱们不只停留在表面,而是要挖出问题的根儿,然后一起找寻解决的钥匙。想象一下,我们是在大海捞针,但有了指南针和渔网,这场寻找就变得既刺激又充满乐趣。跟着我,咱们在数据的汪洋里畅游,找到属于你的那片宁静海港,让你不再被信息的洪流淹没,而是能稳稳驾驭,轻松自在地航行。准备好了吗?出发吧! 第一章:写入失败的初探 现象描述:当你尝试向DorisDB表中插入数据时,突然间,一切变得静止。查询返回一个错误信息,告诉你“写入失败”。这不仅让你感到沮丧,还可能影响了业务流程的连续性。 原因分析:写入失败可能是由多种因素引起的,包括但不限于网络延迟、资源限制(如磁盘空间不足)、事务冲突、以及数据库配置问题等。理解这些原因有助于我们对症下药。 第二章:案例研究:网络延迟引发的写入失败 场景还原:假设你正使用Python的dorisdb库进行数据插入操作。代码如下: python from dorisdb import DorisDBClient client = DorisDBClient(host='your_host', port=your_port, database='your_db') cursor = client.cursor() 插入数据 cursor.execute("INSERT INTO your_table (column1, column2) VALUES ('value1', 'value2')") 问题浮现:执行上述代码后,你收到了“写入失败”的消息,同时发现网络连接偶尔会中断。 解决方案:首先,检查网络连接稳定性。确保你的服务器与DorisDB实例之间的网络畅通无阻。其次,优化SQL语句的执行效率,减少网络传输的数据量。例如,可以考虑批量插入数据,而不是逐条插入。 第三章:资源限制:磁盘空间不足的挑战 场景还原:你的DorisDB实例运行在一个资源有限的环境中,某天,当你试图插入大量数据时,系统提示磁盘空间不足。 问题浮现:尽管你已经确保了网络连接稳定,但写入仍然失败。 解决方案:增加磁盘空间是显而易见的解决方法,但这需要时间和成本。哎呀,兄弟,你得知道,咱们手头的空间那可是个大问题啊!要是想在短时间内搞定它,我这儿有个小妙招给你。首先,咱们得做个大扫除,把那些用不上的数据扔掉。就像家里大扫除一样,那些过时的文件、照片啥的,该删就删,别让它占着地方。其次呢,咱们可以用更牛逼的压缩工具,比如ZIP或者RAR,它们能把文件压缩得更小,让硬盘喘口气。这样一来,不仅空间大了,还能节省点资源,挺划算的嘛!试试看,说不定你会发现自己的设备运行起来比以前流畅多了!嘿,兄弟!你听说过 DorisDB 的分片和分布式功能吗?这玩意儿超级厉害!它就像个大仓库,能把咱们的数据均匀地摆放在多个小仓库里(那些就是节点),这样不仅能让数据更高效地存储起来,还能让我们的系统跑得更快,用起来更顺畅。试试看,保管让你爱不释手! 第四章:事务冲突与并发控制 场景还原:在高并发环境下,多个用户同时尝试插入数据到同一表中,导致了写入失败。 问题浮现:即使网络连接稳定,磁盘空间充足,事务冲突仍可能导致写入失败。 解决方案:引入适当的并发控制机制是关键。在DorisDB中,可以通过设置合理的锁策略来避免或减少事务冲突。例如,使用行级锁或表级锁,根据具体需求选择最合适的锁模式。哎呀,兄弟,咱们在优化程序的时候,得注意一点,别搞那些没必要的同时进行的操作,这样能大大提升系统的稳定性。就像是做饭,你要是同时炒好几个菜,肯定得忙得团团转,而且容易出错。所以啊,咱们得一个个来,稳扎稳打,这样才能让系统跑得又快又稳! 结语:从困惑到解决的旅程 面对“写入失败”,我们需要冷静分析,从不同的角度寻找问题所在。哎呀,你知道嘛,不管是网速慢了点、硬件不够给力、操作过程中卡壳了,还是设置哪里没对劲,这些事儿啊,都有各自的小妙招来解决。就像是遇到堵车了,你得找找是哪段路的问题,然后对症下药,说不定就是换个路线或者等等红绿灯,就能顺畅起来呢!哎呀,你知道不?咱们要是能持续地学习和动手做,那咱处理问题的能力就能慢慢上个新台阶。就像给水管通了塞子,数据的流动就更顺畅了。这样一来,咱们的业务跑起来也快多了,就像是有了个贴身保镖,保护着业务高效运转呢!嘿!听好了,每回遇到难题都不是白来的,那可是让你升级打怪的好机会!咱们就一起手牵手,勇闯数据的汪洋大海,去发现那些藏在暗处的新世界吧!别怕,有我在你身边,咱俩一起探险,一起成长!
2024-10-07 15:51:26
124
醉卧沙场
ElasticSearch
...一切看起来都很顺利,数据导入、索引创建啥的都没问题。但当我尝试对某些节点进行操作时,突然蹦出了这么一行错误: org.elasticsearch.cluster.block.ClusterBlockException: blocked by: [SERVICE_UNAVAILABLE/2/no active shards]; 当时我心里那个急啊!赶紧去查文档,发现这是NodeNotActiveException的表现之一。简单说吧,就好比某个关键的小哥突然“罢工”了,可能是因为它内存不够用,或者网络断了啥的,结果整个团队的工作都乱套了,没法正常运转了。 我当时就纳闷了:“这不是应该自动恢复吗?为啥还要报错呢?”后来才明白,虽然ElasticSearch确实有自我修复机制,但有时候我们需要手动干预才能让它恢复正常。 --- 2. 理解背后的逻辑 为什么会出现这种问题? 在深入了解之前,我觉得有必要先搞清楚这个异常的根本原因。其实NodeNotActiveException并不是什么特别复杂的概念,它主要出现在以下几种情况: - 节点宕机:某个节点由于硬件故障或者网络问题离线了。 - 磁盘空间不足:如果某个节点的磁盘满了,ElasticSearch会自动将其标记为不可用。 - 配置错误:比如分配给节点的资源不够,导致其无法启动。 对于我来说,问题出在第二个点上——磁盘空间不足。我当时为了省钱,给服务器分配的空间少得可怜,结果没多久就发现磁盘直接爆满,把自己都吓了一跳!于是ElasticSearch很生气,直接把该节点踢出了集群。 --- 3. 解决方案一 扩容磁盘空间 既然问题找到了,那就动手解决吧!首先,我决定先扩展磁盘容量。这一步其实很简单,只要登录服务器,增加磁盘大小就行。具体步骤如下: bash 查看当前磁盘状态 df -h 扩展磁盘(假设你已经购买了额外的存储) sudo growpart /dev/xvda 1 sudo resize2fs /dev/xvda1 完成后记得重启ElasticSearch服务: bash sudo systemctl restart elasticsearch 重启之后,神奇的事情发生了——我的节点重新上线了!不过这里有个小技巧分享给大家:如果你不确定扩容是否成功,可以通过以下命令检查磁盘使用情况: bash df -h 看到磁盘空间变大了,心里顿时舒坦了不少。 --- 4. 解决方案二 调整ElasticSearch配置 当然啦,仅仅扩容还不够,还需要优化ElasticSearch的配置文件。特别是那些容易导致内存不足或磁盘占用过高的参数,比如indices.memory.index_buffer_size和indices.store.throttle.max_bytes_per_sec。修改后的配置文件大概长这样: yaml cluster.routing.allocation.disk.threshold_enabled: true cluster.routing.allocation.disk.watermark.low: 85% cluster.routing.allocation.disk.watermark.high: 90% cluster.routing.allocation.disk.watermark.flood_stage: 95% cluster.info.update.interval: 30s 这些设置的意思是告诉ElasticSearch,当磁盘使用率达到85%时开始警告,达到90%时限制写入,超过95%时完全停止操作。这样可以有效避免再次出现类似的问题。 --- 5. 实战演练 代码中的应对策略 除了调整配置,我们还可以通过编写脚本来监控和处理NodeNotActiveException。比如,下面这段Java代码展示了如何捕获异常并记录日志: java import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.indices.CreateIndexRequest; import org.elasticsearch.client.indices.CreateIndexResponse; public class ElasticSearchExample { public static void main(String[] args) { RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http"))); try { CreateIndexRequest request = new CreateIndexRequest("test_index"); CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT); System.out.println("Index created: " + response.isAcknowledged()); } catch (Exception e) { if (e instanceof ClusterBlockException) { System.err.println("Cluster block detected: " + e.getMessage()); } else { System.err.println("Unexpected error: " + e.getMessage()); } } finally { try { client.close(); } catch (IOException ex) { System.err.println("Failed to close client: " + ex.getMessage()); } } } } 这段代码的作用是在创建索引时捕获可能发生的异常,并根据异常类型采取不同的处理方式。如果遇到ClusterBlockException,我们可以选择延迟重试或者其他补偿措施。 --- 6. 总结与反思 成长路上的一课 通过这次经历,我深刻体会到,作为一名开发者,不仅要掌握技术细节,还要学会从实际问题出发,找到最优解。NodeNotActiveException这个错误看着不起眼,但其实背后有不少门道呢!比如说,你的服务器硬件是不是有点吃不消了?集群那边有没有啥小毛病没及时发现?还有啊,咱们平时运维的时候是不是也有点松懈了?这些都是得好好琢磨的地方! 最后,我想说的是,技术学习的过程就像爬山一样,有时候会遇到陡峭的山坡,但只要坚持下去,总能看到美丽的风景。希望这篇文章能给大家带来一些启发和帮助!如果还有其他疑问,欢迎随时交流哦~
2025-03-14 15:40:13
65
林中小径
转载文章
...译安装MariaDB数据库的区别以及如何在CentOS 7系统上进行实际操作后,我们不妨进一步探讨数据库管理与运维的最新趋势和技术动态。近期,MariaDB发布了10.7版本,引入了一系列性能优化和新特性,如原生支持Temporal Tables、JSONTABLES等,对于数据库开发者和管理员来说,熟悉这些新功能将有助于提升数据管理效率并保障业务系统的稳定运行。 此外,随着云服务的普及与发展,越来越多的企业选择将数据库部署在云端,阿里云等服务商也推出了针对MariaDB的高可用集群解决方案,用户不仅可以享受到一键部署、自动备份恢复、弹性伸缩等便捷服务,还能通过精细权限管理和日志审计等功能确保数据安全合规。因此,了解和研究云环境下的数据库运维策略,对于提升企业IT基础设施水平至关重要。 同时,在数据库主从复制领域,MySQL 8.0及MariaDB的新版本中增强了GTID(全局事务标识符)功能,简化了主从配置流程,并提高了数据同步的一致性和可靠性。结合最新的数据库监控工具如Prometheus和Grafana,可以实时监测主从复制状态,及时发现并解决潜在问题,这对于构建高性能、高可用的分布式数据库架构具有重要意义。 综上所述,紧跟数据库技术发展潮流,关注MariaDB等开源数据库软件的更新动态,探索云端数据库运维实践与高可用性设计,无疑将助力企业在数字化转型过程中更好地利用数据库这一关键基础设施,以支撑更加复杂多变的业务场景需求。
2023-07-12 10:11:01
311
转载
转载文章
...置 8 2.2.2 数据库配置 8 3系统分析 11 3.1 可行性分析 11 3.1.1 技术可行性 11 3.1.2 操作可行性 11 3.1.3 经济可行性 11 3.1.4 法律可行性 11 3.2 腕表交易系统功能需求分析 11 3.3 数据库需求分析 12 4系统设计 13 4.1 系统功能模块设计 13 4.2系统流程设计 13 4.2.1 系统开发流程 13 4.2.2 用户登录流程 14 4.2.3 系统操作流程 15 4.2.4 添加信息流程 15 4.2.5 修改信息流程 16 4.2.6 删除信息流程 16 4.3系统用例分析 17 4.3.1 管理员用例图 17 4.3.2 用户用例图 18 4.4 数据库设计 19 4.4.1 tb_Ware(商品信息表) 19 4.4.2 tb_manager(管理员信息表) 19 4.4.3 tb_sub(订单生成表) 19 4.4.4 tb_Link(超级链接表) 20 4.4.5 tb_Affiche(公告信息表) 20 4.3 用SSM连接数据库 20 5系统实现 22 5.1 前台部分 22 5.1.1 前台总体框架 22 5.1.2 商城首页 22 5.1.3 产品详情页 23 5.1.4 评价 23 5.2 后台部分 24 5.2.1 后台主页 24 5.2.2 后台评价管理 25 5.2.3 商品管理 25 5.2.4 商品修改 26 5.2.5 分类管理 26 5.2.6 订单管理 27 5.2.7 腕表购物车管理 27 6系统测试 28 6.1系统测试的意义 28 6.2性能测试 29 6.3测试分析 29 总 结 30 致 谢 31 参考文献 31 3系统分析 3.1 可行性分析 腕表交易系统主要目标是实现网上展示腕表交易系统信息,购买腕表产品。在确定了目标后,我们从以下四方面对能否实现本系统目标进行可行性分析。 3.1.1 技术可行性 腕表交易系统主要采用Java技术,基于B/S结构,MYSQL数据库,主要包括前端应用程序的开发以及后台数据库的建立和维护两个方面。对于应用程序的开发要求具备功能要完备、使用应简单等特点,而对于数据库的建立和维护则要求建立一个数据完整性强、数据安全性好、数据稳定性高的库。腕表交易系统的开发技术具有很高可行性,且开发人员掌握了一定的开发技术,所以系统的开发具有可行性。 3.1.2 操作可行性 腕表交易系统的登录界面简单易于操作,采用常见的界面窗口来登录界面,通过电脑进行访问操作,会员只要平时使用过电脑都能进行访问操作。此系统的开发采用PHP语言开发,基于B/S结构,这些开发环境使系统更加完善。本系统具有易操作、易管理、交互性好的特点,在操作上是非常简单的。因此本系统可以进行开发。 3.1.3 经济可行性 腕表交易系统是基于B/S模式,采用MYSQL数据库储存数据,所要求的硬件和软件环境,市场上都很容易购买,程序开发主要是管理系统的开发和维护。所以程序在开发人力、财力上要求不高,而且此系统不是很复杂,开发周期短,在经济方面具有较高的可行性。 3.1.4 法律可行性 此腕表交易系统是自己设计的管理系统,具有很大的实际意义。开发环境软件和使用的数据库都是开源代码,因此对这个系统进行开发与普通的系统软件设计存在很大不同,没有侵权等问题,在法律上完全具有可行性。 综上所述,腕表交易系统在技术、经济、操作和法律上都具有很高的可行性,开发此程序是很必要的。 3.2 腕表交易系统功能需求分析 此基于SSM的腕表交易系统分前台功能和后台功能: 1)前台部分由用户使用,主要包括用户注册,腕表购物车管理,订单管理,个人资料管理,留言板管理 2)后台部分由管理员使用,主要包括管理员身份验证,商品管理,处理订单,用户信息管理,连接信息管理 3.3 数据库需求分析 数据库的设计通常是以一个已经存在的数据库管理系统为基础的,常用的数据库管理系统有MYSQL,SQL,Oracle等。我采用了Mysql数据库管理系统,建立的数据库名为db_business。 整个系统功能需要以下数据项: 用户:用户id、用户名称、登录密码、用户真实姓名、性别、邮箱地址、联系地址、联系电话、密码问题、答案、注册时间。 留言:主题id、作者姓名、Email、主题名称、留言内容、发布时间。 商品:商品id、名称、价格、图片路径、类型、简要介绍、存储地址、上传人姓名、发布时间、是否推荐。 订单:订单号、用户名、真实姓名、订购日期、Email、地址、邮编、付款方式、联系方式、运送方式、订单核对、其他。 管理员:管理员id、管理员名称、管理员密码。 公告:公告内容、公告时间。 4系统设计 4.1 系统功能模块设计 功能结构图如下: 图9 功能模块设计图 从图中可以看出,网上腕表交易系统可以分为前台和后台两个部分,前台部分由用户使用,主要包括用户注册,生成订单,腕表购物车管理,查看腕表购物车,查看留言,订购产品,订单查询和发布留言7个模块;本文转载自http://www.biyezuopin.vip/onews.asp?id=11975后台部分由管理员使用,主要包括管理员身份验证,商品管理,处理订单,用户信息管理,连接信息管理5个模块。 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><base href="<%=basePath%>"/><title>腕表商城</title><meta http-equiv="pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache"><meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"><meta http-equiv="description" content="This is my page"><meta name="viewport" content="width=device-width, initial-scale=1"><!-- Favicon --><link rel="shortcut icon" type="image/x-icon" href="img/favicon.png"><link rel="stylesheet" type="text/css" href="<%=basePath%>home/css/font-awesome.min.css" /><link rel="stylesheet" type="text/css" href="<%=basePath%>home/css/bootstrap.css" /><link rel="stylesheet" type="text/css" href="<%=basePath%>home/css/style.css"><link rel="stylesheet" type="text/css" href="<%=basePath%>home/css/magnific-popup.css"><link rel="stylesheet" type="text/css" href="<%=basePath%>home/css/owl.carousel.css"><script type="text/javascript">function getprofenlei(){ var html = ""; $.ajax({url: "leixing.action?list&page=0&rows=30",type: "POST",async: false, contentType: "application/x-www-form-urlencoded;charset=UTF-8",success: function (data) { $.each(data.rows, function (i, val) { html += ' <li ><a href="home/search.jsp?fenlei='+val.id+'" >'+val.a1+' </a></li>';})} }); $("fenlei").html(html);}function gettop1(){var html = "";$.ajax({url: "leixing.action?list&page=0&rows=10",type: "POST",async: false,success: function (data) {var total='';//<div class="tab-pane active" id="nArrivals">// <div class="nArrivals owl-carousel" id="top1">$.each(data.rows, function (i, valmm) { html+='<div class="nArrivals owl-carousel" id="'+valmm.id+'">';$.ajax({url: "shangpin.action?list&page=0&rows=10",type: "POST",async: false,data: { fenlei:valmm.id },success: function (data) { $.each(data.rows, function (i, val) { html+='<div class="product-grid">'+'<div class="item">'+' <div class="product-thumb">'+' <div class="image product-imageblock"> <a href="home/details.jsp?ids='+val.id+'"> <img data-name="product_image" style="width:223px;height:285px;" src="<%=basePath%>'+val.tupian1+'" alt="iPod Classic" title="iPod Classic" class="img-responsive"> <img style="width:223px;height:285px;" src="<%=basePath%>'+val.tupian1+'" alt="iPod Classic" title="iPod Classic" class="img-responsive"> </a> </div>'+' <div class="caption product-detail text-left">'+' <h6 data-name="product_name" class="product-name mt_20"><a href="home/details.jsp?ids='+val.id+'" title="Casual Shirt With Ruffle Hem">'+val.biaoti+'</a></h6>'+' <div class="rating"> <span class="fa fa-stack"><i class="fa fa-star-o fa-stack-1x"></i><i class="fa fa-star fa-stack-1x"></i></span> <span class="fa fa-stack"><i class="fa fa-star-o fa-stack-1x"></i><i class="fa fa-star fa-stack-1x"></i></span> <span class="fa fa-stack"><i class="fa fa-star-o fa-stack-1x"></i><i class="fa fa-star fa-stack-1x"></i></span> <span class="fa fa-stack"><i class="fa fa-star-o fa-stack-1x"></i><i class="fa fa-star fa-stack-1x"></i></span> <span class="fa fa-stack"><i class="fa fa-star-o fa-stack-1x"></i><i class="fa fa-star fa-stack-x"></i></span> </div>'+'<span class="price"><span class="amount"><span class="currencySymbol">$</span>'+val.jiage+'</span>'+'</span>'+'<div class="button-group text-center">'+' <div class="wishlist"><a href="home/details.jsp?ids='+val.id+'"><span>wishlist</span></a></div>'+'<div class="quickview"><a href="home/details.jsp?ids='+val.id+'"><span>Quick View</span></a></div>'+'<div class="compare"><a href="home/details.jsp?ids='+val.id+'"><span>Compare</span></a></div>'+'<div class="add-to-cart"><a href="home/details.jsp?ids='+val.id+'"><span>Add to cart</span></a></div>'+'</div>'+'</div>'+'</div>'+'</div>'+' </div>'; })html+='</div>'; } })}) $("nArrivals").html(html); } }); 本篇文章为转载内容。原文链接:https://blog.csdn.net/newlw/article/details/127608579。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-03-21 18:24:50
67
转载
转载文章
...重要角色,而且在现代操作系统和云计算环境中也愈发关键。近期,随着容器化技术和微服务架构的广泛应用,内存映射机制对于提高系统资源利用率、实现高效的数据共享与交换具有重要意义。 例如,在Docker和Kubernetes等容器平台中,mmap系统调用被用于实现容器内部进程与宿主机文件系统的高效交互,以及容器间共享内存通信。通过内存映射,容器可以将宿主机上的持久化存储直接加载到内存中,实现数据的快速读取与更新,极大地提升了I/O性能。 此外,针对云原生环境下的大规模并行计算和实时数据处理场景,研究者们正在探索如何优化mmap以适应更高的并发需求和更低延迟的要求。2021年,有研究人员提出了一种改进的内存映射策略,旨在减少在高负载环境下由于频繁的内存映射操作导致的系统开销,并已在分布式数据库和大数据分析应用中取得了显著效果。 同时,内存映射的安全性问题也引起了业界的关注。今年早些时候,一项安全研究报告揭示了利用mmap进行提权攻击的新方法,再次提醒开发者在享受内存映射带来的便利时,也需要关注其潜在的安全风险,并采取相应的防御措施。 总之,内存映射作为底层系统调用的重要组成部分,其发展与优化将持续影响着整个软件生态系统的性能表现与安全性,值得广大开发者和技术研究者深入探究和实践。
2023-09-20 22:49:12
465
转载
转载文章
...删除相应内容。 1、分布式事务出现场景 场景描述:支付宝转账余额宝 分布式事务必须满足的条件: 1、远程RPC调用,支付宝和余额宝存在接口调用 2、支付宝和余额宝使用不同的数据库 如图: 2、分布式事务解决方案 1、基于数据库XA协议的两段提交 XA协议是数据库支持的一种协议,其核心是一个事务管理器用来统一管理两个分布式数据库,如图 事务管理器负责跟支付宝数据库和余额宝数据库打交道,一旦有一个数据库连接失败,另一个数据库的操作就不会进行,一个数据库操作失败就会导致另一个数据库回滚,只有他们全部成功两个数据库的事务才会提交。 基于XA协议的两段和三段提交是一种严格的安全确认机制,其安全性是非常高的,但是保证安全性的前提是牺牲了性能,这个就是分布式系统里面的CAP理论,做任何架构的前提需要有取舍。所以基于XA协议的分布式事务并发性不高,不适合高并发场景。 2、基于activemq的解决方案 如图: 1、支付宝扣款成功时往message表插入消息 2、message表有message_id(流水id,标识夸系统的一次转账操作),status(confirm,unconfirm) 3、timer扫描message表的unconfirm状态记录往activemq插入消息 4、余额宝收到消息消费消息时先查询message表如果有记录就不处理如果没记录就进行数据库增款操作 5、如果余额宝数据库操作成功往余额宝message表插入消息,表字段跟支付宝message一致 6、如果5操作成功,回调支付宝接口修改message表状态,把unconfirm状态转换成confirm状态 问题描述: 1、支付宝设计message表的目的 如果支付宝往activemq插入消息而余额宝消费消息异常,有可能是消费消息成功而事务操作异常,有可能是网络异常等等不确定因素。如果出现异常而activemq收到了确认消息的信号,这时候activemq中的消息是删除了的,消息丢失了。设置message表就是有一个消息存根,activemq中消息丢失了message表中的消息还在。解决了activemq消息丢失问题 2、余额宝设计message表的目的 当余额宝消费成功并且数据库操作成功时,回调支付宝的消息确认接口,如果回调接口时出现异常导致支付宝状态修改失败还是unconfirm状态,这时候还会被timer扫描到,又会往activemq插入消息,又会被余额宝消费一边,但是这条消息已经消费成功了的只是回调失败而已,所以就需要有一个这样的message表,当余额宝消费时先插入message表,如果message根据message_id能查询到记录就说明之前这条消息被消费过就不再消费只需要回调成功即可,如果查询不到消息就消费这条消息继续数据库操作,数据库操作成功就往message表插入消息。 这样就解决了消息重复消费问题,这也是消费端的幂等操作。 基于消息中间件的分布式事务是最理想的分布式事务解决方案,兼顾了安全性和并发性! 接下来贴代码: 支付宝代码: @Controller@RequestMapping("/order")public class OrderController {/ @Description TODO @param @return 参数 @return String 返回类型 @throws userID:转账的用户ID amount:转多少钱/@Autowired@Qualifier("activemq")OrderService orderService;@RequestMapping("/transfer")public @ResponseBody String transferAmount(String userId,String messageId, int amount) {try {orderService.updateAmount(amount,messageId, userId);}catch (Exception e) {e.printStackTrace();return "===============================transferAmount failed===================";}return "===============================transferAmount successfull===================";}@RequestMapping("/callback")public String callback(String param) {JSONObject parse = JSONObject.parseObject(param);String respCode = parse.getString("respCode");if(!"OK".equalsIgnoreCase(respCode)) {return null;}try {orderService.updateMessage(param);}catch (Exception e) {e.printStackTrace();return "fail";}return "ok";} } public interface OrderService {public void updateAmount(int amount, String userId,String messageId);public void updateMessage(String param);} @Service("activemq")@Transactional(rollbackFor = Exception.class)public class OrderServiceActivemqImpl implements OrderService {Logger logger = LoggerFactory.getLogger(getClass());@AutowiredJdbcTemplate jdbcTemplate;@AutowiredJmsTemplate jmsTemplate;@Overridepublic void updateAmount(final int amount, final String messageId, final String userId) {String sql = "update account set amount = amount - ?,update_time=now() where user_id = ?";int count = jdbcTemplate.update(sql, new Object[]{amount, userId});if (count == 1) {//插入到消息记录表sql = "insert into message(user_id,message_id,amount,status) values (?,?,?,?)";int row = jdbcTemplate.update(sql,new Object[]{userId,messageId,amount,"unconfirm"});if(row == 1) {//往activemq中插入消息jmsTemplate.send("zg.jack.queue", new MessageCreator() {@Overridepublic Message createMessage(Session session) throws JMSException {com.zhuguang.jack.bean.Message message = new com.zhuguang.jack.bean.Message();message.setAmount(Integer.valueOf(amount));message.setStatus("unconfirm");message.setUserId(userId);message.setMessageId(messageId);return session.createObjectMessage(message);} });} }}@Overridepublic void updateMessage(String param) {JSONObject parse = JSONObject.parseObject(param);String messageId = parse.getString("messageId");String sql = "update message set status = ? where message_id = ?";int count = jdbcTemplate.update(sql,new Object[]{"confirm",messageId});if(count == 1) {logger.info(messageId + " callback successfull");} }} activemq.xml <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:amq="http://activemq.apache.org/schema/core"xmlns:jms="http://www.springframework.org/schema/jms"xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.1.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-4.1.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc-4.1.xsdhttp://www.springframework.org/schema/jmshttp://www.springframework.org/schema/jms/spring-jms-4.1.xsdhttp://activemq.apache.org/schema/corehttp://activemq.apache.org/schema/core/activemq-core-5.12.1.xsd"><context:component-scan base-package="com.zhuguang.jack" /><mvc:annotation-driven /><amq:connectionFactory id="amqConnectionFactory"brokerURL="tcp://192.168.88.131:61616"userName="system"password="manager" /><!-- 配置JMS连接工长 --><bean id="connectionFactory"class="org.springframework.jms.connection.CachingConnectionFactory"><constructor-arg ref="amqConnectionFactory" /><property name="sessionCacheSize" value="100" /></bean><!-- 定义消息队列(Queue) --><bean id="demoQueueDestination" class="org.apache.activemq.command.ActiveMQQueue"><!-- 设置消息队列的名字 --><constructor-arg><value>zg.jack.queue</value></constructor-arg></bean><!-- 配置JMS模板(Queue),Spring提供的JMS工具类,它发送、接收消息。 --><bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"><property name="connectionFactory" ref="connectionFactory" /><property name="defaultDestination" ref="demoQueueDestination" /><property name="receiveTimeout" value="10000" /><!-- true是topic,false是queue,默认是false,此处显示写出false --><property name="pubSubDomain" value="false" /></bean></beans> spring-dispatcher.xml <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xmlns:task="http://www.springframework.org/schema/task" xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:util="http://www.springframework.org/schema/util" xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util-3.2.xsdhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc-3.2.xsdhttp://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-3.0.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"><!-- 引入同文件夹下的redis属性配置文件 --><!-- 解决springMVC响应数据乱码 text/plain就是响应的时候原样返回数据--><import resource="../activemq/activemq.xml"/><!--<context:property-placeholder ignore-unresolvable="true" location="classpath:config/core/core.properties,classpath:config/redis/redis-config.properties" />--><bean id="propertyConfigurerForProject1" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"><property name="order" value="1" /><property name="ignoreUnresolvablePlaceholders" value="true" /><property name="location"><value>classpath:config/core/core.properties</value></property></bean><mvc:annotation-driven><mvc:message-converters register-defaults="true"><bean class="org.springframework.http.converter.StringHttpMessageConverter"><property name="supportedMediaTypes" value = "text/plain;charset=UTF-8" /></bean></mvc:message-converters></mvc:annotation-driven><!-- 避免IE执行AJAX时,返回JSON出现下载文件 --><bean id="mappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"><property name="supportedMediaTypes"><list><value>text/html;charset=UTF-8</value></list></property></bean><!-- 开启controller注解支持 --><!-- 注:如果base-package=com.avicit 则注解事务不起作用 TODO 读源码 --><context:component-scan base-package="com.zhuguang"></context:component-scan><mvc:view-controller path="/" view-name="redirect:/index" /><beanclass="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" /><bean id="handlerAdapter"class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"></bean><beanclass="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"><property name="mediaTypes"><map><entry key="json" value="application/json" /><entry key="xml" value="application/xml" /><entry key="html" value="text/html" /></map></property><property name="viewResolvers"><list><bean class="org.springframework.web.servlet.view.BeanNameViewResolver" /><bean class="org.springframework.web.servlet.view.UrlBasedViewResolver"><property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /><property name="prefix" value="/" /><property name="suffix" value=".jsp" /></bean></list></property></bean><!-- 支持上传文件 --> <!-- 控制器异常处理 --><bean id="exceptionResolver"class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"><property name="exceptionMappings"><props><prop key="java.lang.Exception">error</prop></props></property></bean><bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"><property name="driverClass"><value>${jdbc.driverClassName}</value></property><property name="jdbcUrl"><value>${jdbc.url}</value></property><property name="user"><value>${jdbc.username}</value></property><property name="password"><value>${jdbc.password}</value></property><property name="minPoolSize" value="10" /><property name="maxPoolSize" value="100" /><property name="maxIdleTime" value="1800" /><property name="acquireIncrement" value="3" /><property name="maxStatements" value="1000" /><property name="initialPoolSize" value="10" /><property name="idleConnectionTestPeriod" value="60" /><property name="acquireRetryAttempts" value="30" /><property name="breakAfterAcquireFailure" value="false" /><property name="testConnectionOnCheckout" value="false" /><property name="acquireRetryDelay"><value>100</value></property></bean><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"></property></bean><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean><tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" /><aop:aspectj-autoproxy expose-proxy="true"/></beans> logback.xml <?xml version="1.0" encoding="UTF-8"?><!--scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒当scan为true时,此属性生效。默认的时间间隔为1分钟。debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。--><configuration scan="false" scanPeriod="60 seconds" debug="false"><!-- 定义日志的根目录 --><!-- <property name="LOG_HOME" value="/app/log" /> --><!-- 定义日志文件名称 --><property name="appName" value="netty"></property><!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 --><appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"><Encoding>UTF-8</Encoding><!--日志输出格式:%d表示日期时间,%thread表示线程名,%-5level:级别从左显示5个字符宽度%logger{50} 表示logger名字最长50个字符,否则按照句点分割。 %msg:日志消息,%n是换行符--><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern></encoder></appender><!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 --> <appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender"><Encoding>UTF-8</Encoding><!-- 指定日志文件的名称 --> <file>${appName}.log</file><!--当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。--><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!--滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}:按天进行日志滚动 %i:当文件大小超过maxFileSize时,按照i进行文件滚动--><fileNamePattern>${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern><!-- 可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每天滚动,且maxHistory是365,则只保存最近365天的文件,删除之前的旧文件。注意,删除旧文件是,那些为了归档而创建的目录也会被删除。--><MaxHistory>365</MaxHistory><!-- 当日志文件超过maxFileSize指定的大小是,根据上面提到的%i进行日志文件滚动 注意此处配置SizeBasedTriggeringPolicy是无法实现按文件大小进行滚动的,必须配置timeBasedFileNamingAndTriggeringPolicy--><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>100MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy></rollingPolicy><!--日志输出格式:%d表示日期时间,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %logger{50} 表示logger名字最长50个字符,否则按照句点分割。 %msg:日志消息,%n是换行符--> <encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern></encoder></appender><!-- logger主要用于存放日志对象,也可以定义日志类型、级别name:表示匹配的logger类型前缀,也就是包的前半部分level:要记录的日志级别,包括 TRACE < DEBUG < INFO < WARN < ERRORadditivity:作用在于children-logger是否使用 rootLogger配置的appender进行输出,false:表示只用当前logger的appender-ref,true:表示当前logger的appender-ref和rootLogger的appender-ref都有效--><!-- <logger name="edu.hyh" level="info" additivity="true"><appender-ref ref="appLogAppender" /></logger> --><!-- root与logger是父子关系,没有特别定义则默认为root,任何一个类只会和一个logger对应,要么是定义的logger,要么是root,判断的关键在于找到这个logger,然后判断这个logger的appender和level。 --><root level="debug"><appender-ref ref="stdout" /><appender-ref ref="appLogAppender" /></root></configuration> 2、余额宝代码 package com.zhuguang.jack.controller;import com.alibaba.fastjson.JSONObject;import com.zhuguang.jack.service.OrderService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;@Controller@RequestMapping("/order")public class OrderController {/ @Description TODO @param @return 参数 @return String 返回类型 @throws 模拟银行转账 userID:转账的用户ID amount:转多少钱/@AutowiredOrderService orderService;@RequestMapping("/transfer")public @ResponseBody String transferAmount(String userId, String amount) {try {orderService.updateAmount(Integer.valueOf(amount), userId);}catch (Exception e) {e.printStackTrace();return "===============================transferAmount failed===================";}return "===============================transferAmount successfull===================";} } 消息监听器 package com.zhuguang.jack.listener;import com.alibaba.fastjson.JSONObject;import com.zhuguang.jack.service.OrderService;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.client.SimpleClientHttpRequestFactory;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;import org.springframework.web.client.RestTemplate;import javax.jms.JMSException;import javax.jms.Message;import javax.jms.MessageListener;import javax.jms.ObjectMessage;@Service("queueMessageListener")public class QueueMessageListener implements MessageListener {private Logger logger = LoggerFactory.getLogger(getClass());@AutowiredOrderService orderService;@Transactional(rollbackFor = Exception.class)@Overridepublic void onMessage(Message message) {if (message instanceof ObjectMessage) {ObjectMessage objectMessage = (ObjectMessage) message;try {com.zhuguang.jack.bean.Message message1 = (com.zhuguang.jack.bean.Message) objectMessage.getObject();String userId = message1.getUserId();int count = orderService.queryMessageCountByUserId(userId);if (count == 0) {orderService.updateAmount(message1.getAmount(), message1.getUserId());orderService.insertMessage(message1.getUserId(), message1.getMessageId(), message1.getAmount(), "ok");} else {logger.info("异常转账");}RestTemplate restTemplate = createRestTemplate();JSONObject jo = new JSONObject();jo.put("messageId", message1.getMessageId());jo.put("respCode", "OK");String url = "http://jack.bank_a.com:8080/alipay/order/callback?param="+ jo.toJSONString();restTemplate.getForObject(url,null);} catch (JMSException e) {e.printStackTrace();throw new RuntimeException("异常");} }}public RestTemplate createRestTemplate() {SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory();simpleClientHttpRequestFactory.setConnectTimeout(3000);simpleClientHttpRequestFactory.setReadTimeout(2000);return new RestTemplate(simpleClientHttpRequestFactory);} } package com.zhuguang.jack.service;public interface OrderService {public void updateAmount(int amount, String userId);public int queryMessageCountByUserId(String userId);public int insertMessage(String userId,String messageId,int amount,String status);} package com.zhuguang.jack.service;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.client.SimpleClientHttpRequestFactory;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;import org.springframework.web.client.RestTemplate;@Service@Transactional(rollbackFor = Exception.class)public class OrderServiceImpl implements OrderService {private Logger logger = LoggerFactory.getLogger(getClass());@AutowiredJdbcTemplate jdbcTemplate;/ 更新数据库表,把账户余额减去amountd/@Overridepublic void updateAmount(int amount, String userId) {//1、农业银行转账3000,也就说农业银行jack账户要减3000String sql = "update account set amount = amount + ?,update_time=now() where user_id = ?";int count = jdbcTemplate.update(sql, new Object[] {amount, userId});if (count != 1) {throw new RuntimeException("订单创建失败,农业银行转账失败!");} }public RestTemplate createRestTemplate() {SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory();simpleClientHttpRequestFactory.setConnectTimeout(3000);simpleClientHttpRequestFactory.setReadTimeout(2000);return new RestTemplate(simpleClientHttpRequestFactory);}@Overridepublic int queryMessageCountByUserId(String messageId) {String sql = "select count() from message where message_id = ?";int count = jdbcTemplate.queryForInt(sql, new Object[]{messageId});return count;}@Overridepublic int insertMessage(String userId, String message_id,int amount, String status) {String sql = "insert into message(user_id,message_id,amount,status) values(?,?,?)";int count = jdbcTemplate.update(sql, new Object[]{userId, message_id,amount, status});if(count == 1) {logger.info("Ok");}return count;} } activemq.xml <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:amq="http://activemq.apache.org/schema/core"xmlns:jms="http://www.springframework.org/schema/jms"xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.1.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-4.1.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc-4.1.xsdhttp://www.springframework.org/schema/jmshttp://www.springframework.org/schema/jms/spring-jms-4.1.xsdhttp://activemq.apache.org/schema/corehttp://activemq.apache.org/schema/core/activemq-core-5.12.1.xsd"><context:component-scan base-package="com.zhuguang.jack" /><mvc:annotation-driven /><amq:connectionFactory id="amqConnectionFactory"brokerURL="tcp://192.168.88.131:61616"userName="system"password="manager" /><!-- 配置JMS连接工长 --><bean id="connectionFactory"class="org.springframework.jms.connection.CachingConnectionFactory"><constructor-arg ref="amqConnectionFactory" /><property name="sessionCacheSize" value="100" /></bean><!-- 定义消息队列(Queue) --><bean id="demoQueueDestination" class="org.apache.activemq.command.ActiveMQQueue"><!-- 设置消息队列的名字 --><constructor-arg><value>zg.jack.queue</value></constructor-arg></bean><!-- 显示注入消息监听容器(Queue),配置连接工厂,监听的目标是demoQueueDestination,监听器是上面定义的监听器 --><bean id="queueListenerContainer"class="org.springframework.jms.listener.DefaultMessageListenerContainer"><property name="connectionFactory" ref="connectionFactory" /><property name="destination" ref="demoQueueDestination" /><property name="messageListener" ref="queueMessageListener" /></bean><!-- 配置JMS模板(Queue),Spring提供的JMS工具类,它发送、接收消息。 --><bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"><property name="connectionFactory" ref="connectionFactory" /><property name="defaultDestination" ref="demoQueueDestination" /><property name="receiveTimeout" value="10000" /><!-- true是topic,false是queue,默认是false,此处显示写出false --><property name="pubSubDomain" value="false" /></bean></beans> OK~~~~~~~~~~~~大功告成!!!, 如果大家觉得满意并且对技术感兴趣请加群:171239762, 纯技术交流群,非诚勿扰。 本篇文章为转载内容。原文链接:https://blog.csdn.net/luoyang_java/article/details/84953241。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-04-16 22:34:52
500
转载
转载文章
...tice公司及其收购数据库技术公司–StormDB的产品。Postgres-XL是一个横向扩展的开源数据库集群,具有足够的灵活性来处理不同的数据库任务。 Postgres-XL功能特性 开放源代码:(源协议使用宽松的“Mozilla Public License”许可,允许将开源代码与闭源代码混在一起使用。) 完全的ACID支持 可横向扩展的关系型数据库(RDBMS) 支持OLAP应用,采用MPP(Massively Parallel Processing:大规模并行处理系统)架构模式 支持OLTP应用,读写性能可扩展 集群级别的ACID特性 多租户安全 也可被用作分布式Key-Value存储 事务处理与数据分析处理混合型数据库 支持丰富的SQL语句类型,比如:关联子查询 支持绝大部分PostgreSQL的SQL语句 分布式多版本并发控制(MVCC:Multi-version Concurrency Control) 支持JSON和XML格式 Postgres-XL缺少的功能 内建的高可用机制 使用外部机制实现高可能,如:Corosync/Pacemaker 有未来功能提升的空间 增加节点/重新分片数据(re-shard)的简便性 数据重分布(redistribution)期间会锁表 可采用预分片(pre-shard)方式解决,在同台物理服务器上建立多个数据节点,每个节点存储一个数据分片。数据重分布时,将一些数据节点迁出即可 某些外键、唯一性约束功能 Postgres-XL架构 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M9lFuEIP-1640133702200)(./assets/postgre-xl.jpg)] 基于开源项目Postgres-XC XL增加了MPP,允许数据节点间直接通讯,交换复杂跨节点关联查询相关数据信息,减少协调器负载。 多个协调器(Coordinator) 应用程序的数据库连入点 分析查询语句,生成执行计划 多个数据节点(DataNode) 实际的数据存储 数据自动打散分布到集群中各数据节点 本地执行查询 一个查询在所有相关节点上并行查询 全局事务管理器(GTM:Global Transaction Manager) 提供事务间一致性视图 部署GTM Proxy实例,以提高性能 Postgre-XL主要组件 GTM (Global Transaction Manager) - 全局事务管理器 GTM是Postgres-XL的一个关键组件,用于提供一致的事务管理和元组可见性控制。 GTM Standby GTM的备节点,在pgxc,pgxl中,GTM控制所有的全局事务分配,如果出现问题,就会导致整个集群不可用,为了增加可用性,增加该备用节点。当GTM出现问题时,GTM Standby可以升级为GTM,保证集群正常工作。 GTM-Proxy GTM需要与所有的Coordinators通信,为了降低压力,可以在每个Coordinator机器上部署一个GTM-Proxy。 Coordinator --协调器 协调器是应用程序到数据库的接口。它的作用类似于传统的PostgreSQL后台进程,但是协调器不存储任何实际数据。实际数据由数据节点存储。协调器接收SQL语句,根据需要获取全局事务Id和全局快照,确定涉及哪些数据节点,并要求它们执行(部分)语句。当向数据节点发出语句时,它与GXID和全局快照相关联,以便多版本并发控制(MVCC)属性扩展到集群范围。 Datanode --数据节点 用于实际存储数据。表可以分布在各个数据节点之间,也可以复制到所有数据节点。数据节点没有整个数据库的全局视图,它只负责本地存储的数据。接下来,协调器将检查传入语句,并制定子计划。然后,根据需要将这些数据连同GXID和全局快照一起传输到涉及的每个数据节点。数据节点可以在不同的会话中接收来自各个协调器的请求。但是,由于每个事务都是惟一标识的,并且与一致的(全局)快照相关联,所以每个数据节点都可以在其事务和快照上下文中正确执行。 Postgres-XL继承了PostgreSQL Postgres-XL是PostgreSQL的扩展并继承了其很多特性: 复杂查询 外键 触发器 视图 事务 MVCC(多版本控制) 此外,类似于PostgreSQL,用户可以通过多种方式扩展Postgres-XL,例如添加新的 数据类型 函数 操作 聚合函数 索引类型 过程语言 安装 环境说明 由于资源有限,gtm一台、另外两台身兼数职。 主机名 IP 角色 端口 nodename 数据目录 gtm 192.168.20.132 GTM 6666 gtm /nodes/gtm 协调器 5432 coord1 /nodes/coordinator xl1 192.168.20.133 数据节点 5433 node1 /nodes/pgdata gtm代理 6666 gtmpoxy01 /nodes/gtm_pxy1 协调器 5432 coord2 /nodes/coordinator xl2 192.168.20.134 数据节点 5433 node2 /nodes/pgdata gtm代理 6666 gtmpoxy02 /nodes/gtm_pxy2 要求 GNU make版本 3.8及以上版本 [root@pg ~] make --versionGNU Make 3.82Built for x86_64-redhat-linux-gnuCopyright (C) 2010 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law. 需安装GCC包 需安装tar包 用于解压缩文件 默认需要GNU Readline library 其作用是可以让psql命令行记住执行过的命令,并且可以通过键盘上下键切换命令。但是可以通过--without-readline禁用这个特性,或者可以指定--withlibedit-preferred选项来使用libedit 默认使用zlib压缩库 可通过--without-zlib选项来禁用 配置hosts 所有主机上都配置 [root@xl2 11] cat /etc/hosts127.0.0.1 localhost192.168.20.132 gtm192.168.20.133 xl1192.168.20.134 xl2 关闭防火墙、Selinux 所有主机都执行 关闭防火墙: [root@gtm ~] systemctl stop firewalld.service[root@gtm ~] systemctl disable firewalld.service selinux设置: [root@gtm ~]vim /etc/selinux/config 设置SELINUX=disabled,保存退出。 This file controls the state of SELinux on the system. SELINUX= can take one of these three values: enforcing - SELinux security policy is enforced. permissive - SELinux prints warnings instead of enforcing. disabled - No SELinux policy is loaded.SELINUX=disabled SELINUXTYPE= can take one of three two values: targeted - Targeted processes are protected, minimum - Modification of targeted policy. Only selected processes are protected. mls - Multi Level Security protection. 安装依赖包 所有主机上都执行 yum install -y flex bison readline-devel zlib-devel openjade docbook-style-dsssl gcc 创建用户 所有主机上都执行 [root@gtm ~] useradd postgres[root@gtm ~] passwd postgres[root@gtm ~] su - postgres[root@gtm ~] mkdir ~/.ssh[root@gtm ~] chmod 700 ~/.ssh 配置SSH免密登录 仅仅在gtm节点配置如下操作: [root@gtm ~] su - postgres[postgres@gtm ~] ssh-keygen -t rsa[postgres@gtm ~] cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys[postgres@gtm ~] chmod 600 ~/.ssh/authorized_keys 将刚生成的认证文件拷贝到xl1到xl2中,使得gtm节点可以免密码登录xl1~xl2的任意一个节点: [postgres@gtm ~] scp ~/.ssh/authorized_keys postgres@xl1:~/.ssh/[postgres@gtm ~] scp ~/.ssh/authorized_keys postgres@xl2:~/.ssh/ 对所有提示都不要输入,直接enter下一步。直到最后,因为第一次要求输入目标机器的用户密码,输入即可。 下载源码 下载地址:https://www.postgres-xl.org/download/ [root@slave ~] ll postgres-xl-10r1.1.tar.gz-rw-r--r-- 1 root root 28121666 May 30 05:21 postgres-xl-10r1.1.tar.gz 编译、安装Postgres-XL 所有节点都安装,编译需要一点时间,最好同时进行编译。 [root@slave ~] tar xvf postgres-xl-10r1.1.tar.gz[root@slave ~] ./configure --prefix=/home/postgres/pgxl/[root@slave ~] make[root@slave ~] make install[root@slave ~] cd contrib/ --安装必要的工具,在gtm节点上安装即可[root@slave ~] make[root@slave ~] make install 配置环境变量 所有节点都要配置 进入postgres用户,修改其环境变量,开始编辑 [root@gtm ~]su - postgres[postgres@gtm ~]vi .bashrc --不是.bash_profile 在打开的文件末尾,新增如下变量配置: export PGHOME=/home/postgres/pgxlexport LD_LIBRARY_PATH=$PGHOME/lib:$LD_LIBRARY_PATHexport PATH=$PGHOME/bin:$PATH 按住esc,然后输入:wq!保存退出。输入以下命令对更改重启生效。 [postgres@gtm ~] source .bashrc --不是.bash_profile 输入以下语句,如果输出变量结果,代表生效 [postgres@gtm ~] echo $PGHOME 应该输出/home/postgres/pgxl代表生效 配置集群 生成pgxc_ctl.conf配置文件 [postgres@gtm ~] pgxc_ctl prepare/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxl/pgxc_ctl/pgxc_ctl_bash.ERROR: File "/home/postgres/pgxl/pgxc_ctl/pgxc_ctl.conf" not found or not a regular file. No such file or directoryInstalling pgxc_ctl_bash script as /home/postgres/pgxl/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxl/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxl/pgxc_ctl --configuration /home/postgres/pgxl/pgxc_ctl/pgxc_ctl.confFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxl/pgxc_ctl 配置pgxc_ctl.conf 新建/home/postgres/pgxc_ctl/pgxc_ctl.conf文件,编辑如下: 对着模板文件一个一个修改,否则会造成初始化过程出现各种神奇问题。 pgxcInstallDir=$PGHOMEpgxlDATA=$PGHOME/data pgxcOwner=postgres---- GTM Master -----------------------------------------gtmName=gtmgtmMasterServer=gtmgtmMasterPort=6666gtmMasterDir=$pgxlDATA/nodes/gtmgtmSlave=y Specify y if you configure GTM Slave. Otherwise, GTM slave will not be configured and all the following variables will be reset.gtmSlaveName=gtmSlavegtmSlaveServer=gtm value none means GTM slave is not available. Give none if you don't configure GTM Slave.gtmSlavePort=20001 Not used if you don't configure GTM slave.gtmSlaveDir=$pgxlDATA/nodes/gtmSlave Not used if you don't configure GTM slave.---- GTM-Proxy Master -------gtmProxyDir=$pgxlDATA/nodes/gtm_proxygtmProxy=y gtmProxyNames=(gtm_pxy1 gtm_pxy2) gtmProxyServers=(xl1 xl2) gtmProxyPorts=(6666 6666) gtmProxyDirs=($gtmProxyDir $gtmProxyDir) ---- Coordinators ---------coordMasterDir=$pgxlDATA/nodes/coordcoordNames=(coord1 coord2) coordPorts=(5432 5432) poolerPorts=(6667 6667) coordPgHbaEntries=(0.0.0.0/0)coordMasterServers=(xl1 xl2) coordMasterDirs=($coordMasterDir $coordMasterDir)coordMaxWALsernder=0 没设置备份节点,设置为0coordMaxWALSenders=($coordMaxWALsernder $coordMaxWALsernder) 数量保持和coordMasterServers一致coordSlave=n---- Datanodes ----------datanodeMasterDir=$pgxlDATA/nodes/dn_masterprimaryDatanode=xl1 主数据节点datanodeNames=(node1 node2)datanodePorts=(5433 5433) datanodePoolerPorts=(6668 6668) datanodePgHbaEntries=(0.0.0.0/0)datanodeMasterServers=(xl1 xl2)datanodeMasterDirs=($datanodeMasterDir $datanodeMasterDir)datanodeMaxWalSender=4datanodeMaxWALSenders=($datanodeMaxWalSender $datanodeMaxWalSender) 集群初始化,启动,停止 初始化 pgxc_ctl -c /home/postgres/pgxc_ctl/pgxc_ctl.conf init all 输出结果: /bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.conf/home/postgres/pgxc_ctl/pgxc_ctl.conf: line 189: $coordExtraConfig: ambiguous redirectFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlStopping all the coordinator masters.Stopping coordinator master coord1.Stopping coordinator master coord2.pg_ctl: directory "/home/postgres/pgxc/nodes/coord/coord1" does not existpg_ctl: directory "/home/postgres/pgxc/nodes/coord/coord2" does not existDone.Stopping all the datanode masters.Stopping datanode master datanode1.Stopping datanode master datanode2.pg_ctl: PID file "/home/postgres/pgxc/nodes/datanode/datanode1/postmaster.pid" does not existIs server running?Done.Stop GTM masterwaiting for server to shut down.... doneserver stopped[postgres@gtm ~]$ echo $PGHOME/home/postgres/pgxl[postgres@gtm ~]$ ll /home/postgres/pgxl/pgxc/nodes/gtm/gtm.^C[postgres@gtm ~]$ pgxc_ctl -c /home/postgres/pgxc_ctl/pgxc_ctl.conf init all/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.conf/home/postgres/pgxc_ctl/pgxc_ctl.conf: line 189: $coordExtraConfig: ambiguous redirectFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlInitialize GTM masterERROR: target directory (/home/postgres/pgxc/nodes/gtm) exists and not empty. Skip GTM initilializationDone.Start GTM masterserver startingInitialize all the coordinator masters.Initialize coordinator master coord1.ERROR: target coordinator master coord1 is running now. Skip initilialization.Initialize coordinator master coord2.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/coord/coord2 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.Done.Starting coordinator master.Starting coordinator master coord1ERROR: target coordinator master coord1 is already running now. Skip initialization.Starting coordinator master coord22019-05-30 21:09:25.562 EDT [2148] LOG: listening on IPv4 address "0.0.0.0", port 54322019-05-30 21:09:25.562 EDT [2148] LOG: listening on IPv6 address "::", port 54322019-05-30 21:09:25.563 EDT [2148] LOG: listening on Unix socket "/tmp/.s.PGSQL.5432"2019-05-30 21:09:25.601 EDT [2149] LOG: database system was shut down at 2019-05-30 21:09:22 EDT2019-05-30 21:09:25.605 EDT [2148] LOG: database system is ready to accept connections2019-05-30 21:09:25.612 EDT [2156] LOG: cluster monitor startedDone.Initialize all the datanode masters.Initialize the datanode master datanode1.Initialize the datanode master datanode2.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/datanode/datanode1 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/datanode/datanode2 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.Done.Starting all the datanode masters.Starting datanode master datanode1.WARNING: datanode master datanode1 is running now. Skipping.Starting datanode master datanode2.2019-05-30 21:09:33.352 EDT [2404] LOG: listening on IPv4 address "0.0.0.0", port 154322019-05-30 21:09:33.352 EDT [2404] LOG: listening on IPv6 address "::", port 154322019-05-30 21:09:33.355 EDT [2404] LOG: listening on Unix socket "/tmp/.s.PGSQL.15432"2019-05-30 21:09:33.392 EDT [2404] LOG: redirecting log output to logging collector process2019-05-30 21:09:33.392 EDT [2404] HINT: Future log output will appear in directory "pg_log".Done.psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"Done.psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"Done.[postgres@gtm ~]$ pgxc_ctl -c /home/postgres/pgxc_ctl/pgxc_ctl.conf stop all/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.conf/home/postgres/pgxc_ctl/pgxc_ctl.conf: line 189: $coordExtraConfig: ambiguous redirectFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlStopping all the coordinator masters.Stopping coordinator master coord1.Stopping coordinator master coord2.pg_ctl: directory "/home/postgres/pgxc/nodes/coord/coord1" does not existDone.Stopping all the datanode masters.Stopping datanode master datanode1.Stopping datanode master datanode2.pg_ctl: PID file "/home/postgres/pgxc/nodes/datanode/datanode1/postmaster.pid" does not existIs server running?Done.Stop GTM masterwaiting for server to shut down.... doneserver stopped[postgres@gtm ~]$ pgxc_ctl/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.conf/home/postgres/pgxc_ctl/pgxc_ctl.conf: line 189: $coordExtraConfig: ambiguous redirectFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlPGXC monitor allNot running: gtm masterRunning: coordinator master coord1Not running: coordinator master coord2Running: datanode master datanode1Not running: datanode master datanode2PGXC stop coordinator master coord1Stopping coordinator master coord1.pg_ctl: directory "/home/postgres/pgxc/nodes/coord/coord1" does not existDone.PGXC stop datanode master datanode1Stopping datanode master datanode1.pg_ctl: PID file "/home/postgres/pgxc/nodes/datanode/datanode1/postmaster.pid" does not existIs server running?Done.PGXC monitor allNot running: gtm masterRunning: coordinator master coord1Not running: coordinator master coord2Running: datanode master datanode1Not running: datanode master datanode2PGXC monitor allNot running: gtm masterNot running: coordinator master coord1Not running: coordinator master coord2Not running: datanode master datanode1Not running: datanode master datanode2PGXC exit[postgres@gtm ~]$ pgxc_ctl -c /home/postgres/pgxc_ctl/pgxc_ctl.conf init all/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.conf/home/postgres/pgxc_ctl/pgxc_ctl.conf: line 189: $coordExtraConfig: ambiguous redirectFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlInitialize GTM masterERROR: target directory (/home/postgres/pgxc/nodes/gtm) exists and not empty. Skip GTM initilializationDone.Start GTM masterserver startingInitialize all the coordinator masters.Initialize coordinator master coord1.Initialize coordinator master coord2.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/coord/coord1 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/coord/coord2 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.Done.Starting coordinator master.Starting coordinator master coord1Starting coordinator master coord22019-05-30 21:13:03.998 EDT [25137] LOG: listening on IPv4 address "0.0.0.0", port 54322019-05-30 21:13:03.998 EDT [25137] LOG: listening on IPv6 address "::", port 54322019-05-30 21:13:04.000 EDT [25137] LOG: listening on Unix socket "/tmp/.s.PGSQL.5432"2019-05-30 21:13:04.038 EDT [25138] LOG: database system was shut down at 2019-05-30 21:13:00 EDT2019-05-30 21:13:04.042 EDT [25137] LOG: database system is ready to accept connections2019-05-30 21:13:04.049 EDT [25145] LOG: cluster monitor started2019-05-30 21:13:04.020 EDT [2730] LOG: listening on IPv4 address "0.0.0.0", port 54322019-05-30 21:13:04.020 EDT [2730] LOG: listening on IPv6 address "::", port 54322019-05-30 21:13:04.021 EDT [2730] LOG: listening on Unix socket "/tmp/.s.PGSQL.5432"2019-05-30 21:13:04.057 EDT [2731] LOG: database system was shut down at 2019-05-30 21:13:00 EDT2019-05-30 21:13:04.061 EDT [2730] LOG: database system is ready to accept connections2019-05-30 21:13:04.062 EDT [2738] LOG: cluster monitor startedDone.Initialize all the datanode masters.Initialize the datanode master datanode1.Initialize the datanode master datanode2.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/datanode/datanode1 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/datanode/datanode2 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.Done.Starting all the datanode masters.Starting datanode master datanode1.Starting datanode master datanode2.2019-05-30 21:13:12.077 EDT [25392] LOG: listening on IPv4 address "0.0.0.0", port 154322019-05-30 21:13:12.077 EDT [25392] LOG: listening on IPv6 address "::", port 154322019-05-30 21:13:12.079 EDT [25392] LOG: listening on Unix socket "/tmp/.s.PGSQL.15432"2019-05-30 21:13:12.114 EDT [25392] LOG: redirecting log output to logging collector process2019-05-30 21:13:12.114 EDT [25392] HINT: Future log output will appear in directory "pg_log".2019-05-30 21:13:12.079 EDT [2985] LOG: listening on IPv4 address "0.0.0.0", port 154322019-05-30 21:13:12.079 EDT [2985] LOG: listening on IPv6 address "::", port 154322019-05-30 21:13:12.081 EDT [2985] LOG: listening on Unix socket "/tmp/.s.PGSQL.15432"2019-05-30 21:13:12.117 EDT [2985] LOG: redirecting log output to logging collector process2019-05-30 21:13:12.117 EDT [2985] HINT: Future log output will appear in directory "pg_log".Done.psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"Done.psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"Done. 启动 pgxc_ctl -c /home/postgres/pgxc_ctl/pgxc_ctl.conf start all 关闭 pgxc_ctl -c /home/postgres/pgxc_ctl/pgxc_ctl.conf stop all 查看集群状态 [postgres@gtm ~]$ pgxc_ctl monitor all/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.conf/home/postgres/pgxc_ctl/pgxc_ctl.conf: line 189: $coordExtraConfig: ambiguous redirectFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlRunning: gtm masterRunning: coordinator master coord1Running: coordinator master coord2Running: datanode master datanode1Running: datanode master datanode2 配置集群信息 分别在数据节点、协调器节点上分别执行以下命令: 注:本节点只执行修改操作即可(alert node),其他节点执行创建命令(create node)。因为本节点已经包含本节点的信息。 create node coord1 with (type=coordinator,host=xl1, port=5432);create node coord2 with (type=coordinator,host=xl2, port=5432);alter node coord1 with (type=coordinator,host=xl1, port=5432);alter node coord2 with (type=coordinator,host=xl2, port=5432);create node datanode1 with (type=datanode, host=xl1,port=15432,primary=true,PREFERRED);create node datanode2 with (type=datanode, host=xl2,port=15432);alter node datanode1 with (type=datanode, host=xl1,port=15432,primary=true,PREFERRED);alter node datanode2 with (type=datanode, host=xl2,port=15432);select pgxc_pool_reload(); 分别登陆数据节点、协调器节点验证 postgres= select from pgxc_node;node_name | node_type | node_port | node_host | nodeis_primary | nodeis_preferred | node_id-----------+-----------+-----------+-----------+----------------+------------------+-------------coord1 | C | 5432 | xl1 | f | f | 1885696643coord2 | C | 5432 | xl2 | f | f | -1197102633datanode2 | D | 15432 | xl2 | f | f | -905831925datanode1 | D | 15432 | xl1 | t | f | 888802358(4 rows) 测试 插入数据 在数据节点1,执行相关操作。 通过协调器端口登录PG [postgres@xl1 ~]$ psql -p 5432psql (PGXL 10r1.1, based on PG 10.6 (Postgres-XL 10r1.1))Type "help" for help.postgres= create database lei;CREATE DATABASEpostgres= \c lei;You are now connected to database "lei" as user "postgres".lei= create table test1(id int,name text);CREATE TABLElei= insert into test1(id,name) select generate_series(1,8),'测试';INSERT 0 8lei= select from test1;id | name----+------1 | 测试2 | 测试5 | 测试6 | 测试8 | 测试3 | 测试4 | 测试7 | 测试(8 rows) 注:默认创建的表为分布式表,也就是每个数据节点值存储表的部分数据。关于表类型具体说明,下面有说明。 通过15432端口登录数据节点,查看数据 有5条数据 [postgres@xl1 ~]$ psql -p 15432psql (PGXL 10r1.1, based on PG 10.6 (Postgres-XL 10r1.1))Type "help" for help.postgres= \c lei;You are now connected to database "lei" as user "postgres".lei= select from test1;id | name----+------1 | 测试2 | 测试5 | 测试6 | 测试8 | 测试(5 rows) 登录到节点2,查看数据 有3条数据 [postgres@xl2 ~]$ psql -p15432psql (PGXL 10r1.1, based on PG 10.6 (Postgres-XL 10r1.1))Type "help" for help.postgres= \c lei;You are now connected to database "lei" as user "postgres".lei= select from test1;id | name----+------3 | 测试4 | 测试7 | 测试(3 rows) 两个节点的数据加起来整个8条,没有问题。 至此Postgre-XL集群搭建完成。 创建数据库、表时可能会出现以下错误: ERROR: Failed to get pooled connections 是因为pg_hba.conf配置不对,所有节点加上host all all 192.168.20.0/0 trust并重启集群即可。 ERROR: No Datanode defined in cluster 首先确认是否创建了数据节点,也就是create node相关的命令。如果创建了则执行select pgxc_pool_reload();使其生效即可。 集群管理与应用 表类型说明 REPLICATION表:各个datanode节点中,表的数据完全相同,也就是说,插入数据时,会分别在每个datanode节点插入相同数据。读数据时,只需要读任意一个datanode节点上的数据。 建表语法: CREATE TABLE repltab (col1 int, col2 int) DISTRIBUTE BY REPLICATION; DISTRIBUTE :会将插入的数据,按照拆分规则,分配到不同的datanode节点中存储,也就是sharding技术。每个datanode节点只保存了部分数据,通过coordinate节点可以查询完整的数据视图。 CREATE TABLE disttab(col1 int, col2 int, col3 text) DISTRIBUTE BY HASH(col1); 模拟数据插入 任意登录一个coordinate节点进行建表操作 [postgres@gtm ~]$ psql -h xl1 -p 5432 -U postgrespostgres= INSERT INTO disttab SELECT generate_series(1,100), generate_series(101, 200), 'foo';INSERT 0 100postgres= INSERT INTO repltab SELECT generate_series(1,100), generate_series(101, 200);INSERT 0 100 查看数据分布结果: DISTRIBUTE表分布结果 postgres= SELECT xc_node_id, count() FROM disttab GROUP BY xc_node_id;xc_node_id | count ------------+-------1148549230 | 42-927910690 | 58(2 rows) REPLICATION表分布结果 postgres= SELECT count() FROM repltab;count -------100(1 row) 查看另一个datanode2中repltab表结果 [postgres@datanode2 pgxl9.5]$ psql -p 15432psql (PGXL 10r1.1, based on PG 10.6 (Postgres-XL 10r1.1))Type "help" for help.postgres= SELECT count() FROM repltab;count -------100(1 row) 结论:REPLICATION表中,datanode1,datanode2中表是全部数据,一模一样。而DISTRIBUTE表,数据散落近乎平均分配到了datanode1,datanode2节点中。 新增数据节点与数据重分布 在线新增节点、并重新分布数据。 新增datanode节点 在gtm集群管理节点上执行pgxc_ctl命令 [postgres@gtm ~]$ pgxc_ctl/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.confFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlPGXC 在服务器xl3上,新增一个master角色的datanode节点,名称是datanode3 端口号暂定5430,pool master暂定6669 ,指定好数据目录位置,从两个节点升级到3个节点,之后要写3个none none应该是datanodeSpecificExtraConfig或者datanodeSpecificExtraPgHba配置PGXC add datanode master datanode3 xl3 15432 6671 /home/postgres/pgxc/nodes/datanode/datanode3 none none none 等待新增完成后,查询集群节点状态: postgres= select from pgxc_node;node_name | node_type | node_port | node_host | nodeis_primary | nodeis_preferred | node_id-----------+-----------+-----------+-----------+----------------+------------------+-------------datanode1 | D | 15432 | xl1 | t | f | 888802358datanode2 | D | 15432 | xl2 | f | f | -905831925datanode3 | D | 15432 | xl3 | f | f | -705831925coord1 | C | 5432 | xl1 | f | f | 1885696643coord2 | C | 5432 | xl2 | f | f | -1197102633(4 rows) 节点新增完毕 数据重新分布 由于新增节点后无法自动完成数据重新分布,需要手动操作。 DISTRIBUTE表分布在了node1,node2节点上,如下: postgres= SELECT xc_node_id, count() FROM disttab GROUP BY xc_node_id;xc_node_id | count ------------+-------1148549230 | 42-927910690 | 58(2 rows) 新增一个节点后,将sharding表数据重新分配到三个节点上,将repl表复制到新节点 重分布sharding表postgres= ALTER TABLE disttab ADD NODE (datanode3);ALTER TABLE 复制数据到新节点postgres= ALTER TABLE repltab ADD NODE (datanode3);ALTER TABLE 查看新的数据分布: postgres= SELECT xc_node_id, count() FROM disttab GROUP BY xc_node_id;xc_node_id | count ------------+--------700122826 | 36-927910690 | 321148549230 | 32(3 rows) 登录datanode3(新增的时候,放在了xl3服务器上,端口15432)节点查看数据: [postgres@gtm ~]$ psql -h xl3 -p 15432 -U postgrespsql (PGXL 10r1.1, based on PG 10.6 (Postgres-XL 10r1.1))Type "help" for help.postgres= select count() from repltab;count -------100(1 row) 很明显,通过 ALTER TABLE tt ADD NODE (dn)命令,可以将DISTRIBUTE表数据重新分布到新节点,重分布过程中会中断所有事务。可以将REPLICATION表数据复制到新节点。 从datanode节点中回收数据 postgres= ALTER TABLE disttab DELETE NODE (datanode3);ALTER TABLEpostgres= ALTER TABLE repltab DELETE NODE (datanode3);ALTER TABLE 删除数据节点 Postgresql-XL并没有检查将被删除的datanode节点是否有replicated/distributed表的数据,为了数据安全,在删除之前需要检查下被删除节点上的数据,有数据的话,要回收掉分配到其他节点,然后才能安全删除。删除数据节点分为四步骤: 1.查询要删除节点dn3的oid postgres= SELECT oid, FROM pgxc_node;oid | node_name | node_type | node_port | node_host | nodeis_primary | nodeis_preferred | node_id -------+-----------+-----------+-----------+-----------+----------------+------------------+-------------11819 | coord1 | C | 5432 | datanode1 | f | f | 188569664316384 | coord2 | C | 5432 | datanode2 | f | f | -119710263316385 | node1 | D | 5433 | datanode1 | f | t | 114854923016386 | node2 | D | 5433 | datanode2 | f | f | -92791069016397 | dn3 | D | 5430 | datanode1 | f | f | -700122826(5 rows) 2.查询dn3对应的oid中是否有数据 testdb= SELECT FROM pgxc_class WHERE nodeoids::integer[] @> ARRAY[16397];pcrelid | pclocatortype | pcattnum | pchashalgorithm | pchashbuckets | nodeoids ---------+---------------+----------+-----------------+---------------+-------------------16388 | H | 1 | 1 | 4096 | 16397 16385 1638616394 | R | 0 | 0 | 0 | 16397 16385 16386(2 rows) 3.有数据的先回收数据 postgres= ALTER TABLE disttab DELETE NODE (dn3);ALTER TABLEpostgres= ALTER TABLE repltab DELETE NODE (dn3);ALTER TABLEpostgres= SELECT FROM pgxc_class WHERE nodeoids::integer[] @> ARRAY[16397];pcrelid | pclocatortype | pcattnum | pchashalgorithm | pchashbuckets | nodeoids ---------+---------------+----------+-----------------+---------------+----------(0 rows) 4.安全删除dn3 PGXC$ remove datanode master dn3 clean 故障节点FAILOVER 1.查看当前集群状态 [postgres@gtm ~]$ psql -h xl1 -p 5432psql (PGXL 10r1.1, based on PG 10.6 (Postgres-XL 10r1.1))Type "help" for help.postgres= SELECT oid, FROM pgxc_node;oid | node_name | node_type | node_port | node_host | nodeis_primary | nodeis_preferred | node_id-------+-----------+-----------+-----------+-----------+----------------+------------------+-------------11739 | coord1 | C | 5432 | xl1 | f | f | 188569664316384 | coord2 | C | 5432 | xl2 | f | f | -119710263316387 | datanode2 | D | 15432 | xl2 | f | f | -90583192516388 | datanode1 | D | 15432 | xl1 | t | t | 888802358(4 rows) 2.模拟datanode1节点故障 直接关闭即可 PGXC stop -m immediate datanode master datanode1Stopping datanode master datanode1.Done. 3.测试查询 只要查询涉及到datanode1上的数据,那么该查询就会报错 postgres= SELECT xc_node_id, count() FROM disttab GROUP BY xc_node_id;WARNING: failed to receive file descriptors for connectionsERROR: Failed to get pooled connectionsHINT: This may happen because one or more nodes are currently unreachable, either because of node or network failure.Its also possible that the target node may have hit the connection limit or the pooler is configured with low connections.Please check if all nodes are running fine and also review max_connections and max_pool_size configuration parameterspostgres= SELECT xc_node_id, FROM disttab WHERE col1 = 3;xc_node_id | col1 | col2 | col3------------+------+------+-------905831925 | 3 | 103 | foo(1 row) 测试发现,查询范围如果涉及到故障的node1节点,会报错,而查询的数据范围不在node1上的话,仍然可以查询。 4.手动切换 要想切换,必须要提前配置slave节点。 PGXC$ failover datanode node1 切换完成后,查询集群 postgres= SELECT oid, FROM pgxc_node;oid | node_name | node_type | node_port | node_host | nodeis_primary | nodeis_preferred | node_id -------+-----------+-----------+-----------+-----------+----------------+------------------+-------------11819 | coord1 | C | 5432 | datanode1 | f | f | 188569664316384 | coord2 | C | 5432 | datanode2 | f | f | -119710263316386 | node2 | D | 15432 | datanode2 | f | f | -92791069016385 | node1 | D | 15433 | datanode2 | f | t | 1148549230(4 rows) 发现datanode1节点的ip和端口都已经替换为配置的slave了。 本篇文章为转载内容。原文链接:https://blog.csdn.net/qianglei6077/article/details/94379331。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-01-30 11:09:03
95
转载
转载文章
...成,一是前台的可视化操作,二是后台的数据库管理。网管对前台的管理和维护工作包括保障网络链路通畅、处理MIS终端的突发事件以及对操作员的管理、培训等,这是网管们日常做得最多、最辛苦的功课;然而MIS系统架构中同等重要的针对数据库的管理、维护和优化工作,现实中似乎并没有得到网管朋友的足够重视,看起来这都是程序员的事,事实上,一个网管如果能在MIS设计期间就数据表的规范化、表索引优化、容量设计、事务处理等诸多方面与程序员进行卓有成效的沟通和协作,那么日常的前台管理工作将会变得大为轻松,因为在某种意义上,数据库管理系统就相当于操作系统,在系统中占有同样重要的位置。 这正是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
98
转载
Docker
...如何实现高效文件读写操作后,我们进一步探索其在现代云原生环境中的实际应用和最新发展动态。近日,Docker发布了最新的版本更新,强化了与Kubernetes的集成能力,使得Docker容器中的文件管理、数据持久化以及多容器间的共享存储更为便捷和安全。 同时,随着微服务架构的广泛应用,Docker Compose工具因其对多容器应用程序定义和部署的简化而备受瞩目。通过Compose文件,开发人员可以轻松配置多个容器间的数据卷挂载策略,从而确保服务间数据的可靠传输与同步。 另外,对于数据敏感型应用,诸如数据库容器等,Docker持续优化其对存储驱动的支持,如支持本地存储、网络存储(NFS、iSCSI)以及云服务商提供的块存储服务,这无疑提升了容器环境中数据的安全性和可用性。 此外,业界也在积极研究和发展基于Docker的新型文件系统解决方案,例如结合分布式存储系统以满足大规模集群环境下容器对高性能、高可用文件读写的诉求。这些前沿技术和实践为Docker在企业级应用场景中提供了更强大的支撑,也体现了容器技术在持续演进中不断解决实际问题的决心与创新力。 总之,深入掌握Docker容器中的文件读写机制,并关注其在云原生领域的发展动态和技术革新,将有助于我们在构建现代化、可扩展的应用架构时,更好地利用Docker的优势,提升开发运维效率,保障业务系统的稳定运行。
2023-12-30 15:13:37
472
编程狂人
MySQL
...可以按照以下步骤进行操作: //1.进入到mysql文件夹下 cd /usr/local/mysql/bin/ //2.以root用户登录mysql mysql -u root -p //3.创建新用户,并授权 create user 'yourusername'@'localhost' identified by 'yourpassword'; grant all privileges on . to 'yourusername'@'localhost'; //4.退出mysql exit 在以上代码中,你需要根据自己的实际情况修改'yourusername'和'yourpassword',并授权给你的新用户所有权限。 如果需要进行更多的数据库操作,你可以接着使用以下命令操作: //1.进入到mysql文件夹下 cd /usr/local/mysql/bin/ //2.以新用户登录mysql mysql -u yourusername -p //3.创建新的数据库和数据表 create database yourdatabasename; use yourdatabasename; create table tablename (column1 datatype, column2 datatype, column3 datatype); //4.退出mysql exit 以上代码中,你需要将'yourdatabasename'和'tablename'替换为你需要创建的数据库名和表名,同时根据实际情况定义相应的表字段。 批处理注册MySQL服务器并不复杂,只需按照以上步骤进行操作即可。如果你对SQL语句不熟悉,也可以使用可视化工具对数据库进行操作。
2024-05-08 15:31:53
111
程序媛
ElasticSearch
...西就像一个超级给力的分布式搜索和分析神器,它实实在在地搭建了一个大舞台,让我们能轻松存储、快速查找到海量数据,并且还能麻溜儿地处理这些数据。 二、什么是ElasticSearch? 简单来说,ElasticSearch是一个基于Lucene的开源搜索引擎,能够进行全文搜索、实时分析和索引管理。它的设计理念是提供一种易于扩展、高性能且实时的搜索解决方案。 三、Painless scripting编程实践 在ElasticSearch中,我们可以通过脚本语言进行各种复杂的操作。这就是我要详细介绍的Painless scripting。 四、Painless scripting的基本概念 Painless是ElasticSearch的一种新的脚本语言,它被设计成一种易学易用的语言,可以方便地与ElasticSearch的数据模型集成。 五、Painless scripting的优势 1. 简单易学 Painless script语言的设计目标就是使用户能够快速上手,并且其语法也尽可能接近Java。 2. 高性能 Painless script语言是在JVM上运行的,因此它的性能非常优秀。 3. 安全性 ElasticSearch对Painless script语言进行了严格的安全检查,防止恶意攻击。 六、Painless scripting的应用场景 1. 数据过滤 我们可以使用Painless脚本来过滤出我们需要的数据。 2. 数据转换 如果我们需要对数据进行一些特殊的处理,例如计算某个字段的平均值或者总和,也可以使用Painless脚本来实现。 3. 数据聚合 Painless脚本可以帮助我们对大量的数据进行聚合操作,例如计算某段时间内的日均访问量。 七、Painless scripting的基本语法 1. 变量定义 在Painless脚本中,我们可以使用var关键字来定义变量。 2. 控制结构 Painless脚本支持if/else、for等控制结构。 3. 函数调用 我们可以直接调用ElasticSearch中的函数,例如avg()、sum()等。 4. 异常处理 在Painless脚本中,我们可以使用try/catch来捕获并处理异常。 八、Painless scripting的示例代码 java GET my-index/_search { "script_fields": { "average_price": { "script": { "source": """ Double total = doc['price'].value(); int count = doc['count'].value(); return total / count; """, "lang": "painless" } } } } 在这段代码中,我们使用了Painless脚本来计算文档中价格的平均值。 九、结论 总的来说,Painless scripting是一种强大而灵活的工具,它可以让我们在ElasticSearch中实现许多复杂的功能。学习并熟练掌握Painless scripting这项技能后,我真心相信咱们的工作效率绝对会蹭蹭往上涨,效果显著到让你惊讶。
2023-02-04 22:33:34
480
风轻云淡-t
MySQL
关系型数据库管理系统(RDBMS) , 关系型数据库管理系统是一种基于关系模型的数据库管理系统,它以表格的形式存储数据,并通过预定义的关系来组织和管理这些表格中的数据。在MySQL中,RDBMS允许用户通过SQL语言对数据进行查询、更新、删除等操作,并确保数据的一致性和完整性。在文章语境中,MySQL作为最广泛使用的关系型数据库管理系统,具备高性能和可扩展性等优点。 分布式系统 , 分布式系统是由多台计算机通过网络连接并协同工作,共同完成一个或多个任务的计算系统。在MySQL的分布式实现中,多台计算机组成集群,每台计算机处理部分数据库负载,它们之间相互协作,实现数据共享和高可用性,以此提高整个系统的处理能力和并发性能。 MySQL Proxy , MySQL Proxy是一款轻量级的中间件软件,设计用于MySQL数据库的负载均衡、读写分离以及访问控制等功能。在MySQL分布式架构中,MySQL Proxy接收客户端的所有数据库请求,根据预设策略将这些请求分发到不同的MySQL服务器上,从而实现数据库访问流量的均衡分布,保证系统整体性能和高可用性。此外,MySQL Proxy还支持复制和故障转移功能,增强了MySQL分布式环境下的稳定性和可靠性。
2023-02-25 16:35:15
123
逻辑鬼才
站内搜索
用于搜索本网站内部文章,支持栏目切换。
知识学习
实践的时候请根据实际情况谨慎操作。
随机学习一条linux命令:
history | grep keyword
- 查看历史命令中包含特定关键词的部分。
推荐内容
推荐本栏目内的其它文章,看看还有哪些文章让你感兴趣。
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
历史内容
快速导航到对应月份的历史文章列表。
随便看看
拉到页底了吧,随便看看还有哪些文章你可能感兴趣。
时光飞逝
"流光容易把人抛,红了樱桃,绿了芭蕉。"