范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

了解事务和锁事务保持逻辑,一致性与可恢复性,必不可少的利器

  了解事务和锁
  事务:保持逻辑数据一致性与可恢复性,必不可少的利器。
  锁:多用户访问同一数据库资源时,对访问的先后次序权限管理的一种机制,没有他事务或许将会一塌糊涂,不能保证数据的安全正确读写。
  死锁:是数据库性能的重量级杀手之一,而死锁却是不同事务之间抢占数据资源造成的。
  不懂的听上去,挺神奇的,懂的感觉我在扯淡,下面带你好好领略下他们的风采,嗅査下他们的狂骚…先说事务--概念,分类
  用华仔无间道中的一句话来给你诠释下:去不了终点,回到原点。
  举例说明:
  在一个事务中,你写了2条sql语句,一条是修改订单表状态,一条是修改库存表库存-1 。 如果在修改订单表状态的时候出错,事务能够回滚,数据将恢复到没修改之前的数据状态,下面的修改库存也就不执行,这样确保你关系逻辑的一致,安全…
  事务就是这个样子,倔脾气,要么全部执行,要么全部不执行,回到原数据状态。
  书面解释:事务具有原子性,一致性,隔离性,持久性。原子性:事务必须是一个自动工作的单元,要么全部执行,要么全部不执行。一致性:事务结束的时候,所有的内部数据都是正确的。隔离性:并发多个事务时,各个事务不干涉内部数据,处理的都是另外一个事务处理之前或之后的数据。持久性:事务提交之后,数据是永久性的,不可再回滚。
  然而在SQL Server中事务被分为3类常见的事务:自动提交事务:是SQL Server默认的一种事务模式,每条Sql语句都被看成一个事务进行处理,你应该没有见过,一条Update 修改2个字段的语句,只修改了1个字段而另外一个字段没有修改…显式事务:T-sql标明,由Begin Transaction开启事务开始,由Commit Transaction 提交事务、Rollback Transaction 回滚事务结束。隐式事务:使用Set IMPLICIT_TRANSACTIONS ON 将将隐式事务模式打开,不用Begin Transaction开启事务,当一个事务结束,这个模式会自动启用下一个事务,只用Commit Transaction 提交事务、Rollback Transaction 回滚事务即可。显式事务的应用
  更多linux内核视频教程文本资料免费获取后台私信【内核】。
  常用的语句就四个。Begin Transaction:标记事务开始。Commit Transaction:事务已经成功执行,数据已经处理妥当。Rollback Transaction:数据处理过程中出错,回滚到没有处理之前的数据状态,或回滚到事务内部的保存点。Save Transaction:事务内部设置的保存点,就是事务可以不全部回滚,只回滚到这里,保证事务内部不出错的前提下。
  上面的都是心法,下面的给你来个招式,要看仔细啦。 1 ---开启事务  2 begin tran  3 --错误扑捉机制,看好啦,这里也有的。并且可以嵌套。  4 begin try    5    --语句正确  6    insert into lives (Eat,Play,Numb) values ("猪肉","足球",1)  7    --Numb为int类型,出错  8    insert into lives (Eat,Play,Numb) values ("猪肉","足球","abc")  9    --语句正确 10    insert into lives (Eat,Play,Numb) values ("狗肉","篮球",2) 11 end try 12 begin catch 13    select Error_number() as ErrorNumber,  --错误代码 14           Error_severity() as ErrorSeverity,  --错误严重级别,级别小于10 try catch 捕获不到 15           Error_state() as ErrorState ,  --错误状态码 16           Error_Procedure() as ErrorProcedure , --出现错误的存储过程或触发器的名称。 17           Error_line() as ErrorLine,  --发生错误的行号 18           Error_message() as ErrorMessage  --错误的具体信息 19    if(@@trancount>0) --全局变量@@trancount,事务开启此值+1,他用来判断是有开启事务 20       rollback tran  ---由于出错,这里回滚到开始,第一条语句也没有插入成功。 21 end catch 22 if(@@trancount>0) 23 commit tran  --如果成功Lives表中,将会有3条数据。 24  25 --表本身为空表,ID ,Numb为int 类型,其它为nvarchar类型 26 select * from lives
  ---开启事务 begin tran --错误扑捉机制,看好啦,这里也有的。并且可以嵌套。 begin try        --语句正确    insert into lives (Eat,Play,Numb) values ("猪肉","足球",1)        --加入保存点    save tran pigOneIn    --Numb为int类型,出错    insert into lives (Eat,Play,Numb) values ("猪肉","足球",2)    --语句正确    insert into lives (Eat,Play,Numb) values ("狗肉","篮球",3) end try begin catch    select Error_number() as ErrorNumber,  --错误代码           Error_severity() as ErrorSeverity,  --错误严重级别,级别小于10 try catch 捕获不到           Error_state() as ErrorState ,  --错误状态码           Error_Procedure() as ErrorProcedure , --出现错误的存储过程或触发器的名称。           Error_line() as ErrorLine,  --发生错误的行号           Error_message() as ErrorMessage  --错误的具体信息    if(@@trancount>0) --全局变量@@trancount,事务开启此值+1,他用来判断是有开启事务       rollback tran   ---由于出错,这里回滚事务到原点,第一条语句也没有插入成功。 end catch if(@@trancount>0) rollback tran pigOneIn --如果成功Lives表中,将会有3条数据。  --表本身为空表,ID ,Numb为int 类型,其它为nvarchar类型 select * from lives
  使用set xact_abort
  设置 xact_abort on/off , 指定是否回滚当前事务,为on时如果当前sql出错,回滚整个事务,为off时如果sql出错回滚当前sql语句,其它语句照常运行读写数据库。
  需要注意的是:xact_abort只对运行时出现的错误有用,如果sql语句存在编译时错误,那么他就失灵啦。delete lives  --清空数据 set xact_abort off begin tran      --语句正确    insert into lives (Eat,Play,Numb) values ("猪肉","足球",1)       --Numb为int类型,出错,如果1234..那个大数据换成"132dsaf" xact_abort将失效    insert into lives (Eat,Play,Numb) values ("猪肉","足球",12345646879783213)    --语句正确    insert into lives (Eat,Play,Numb) values ("狗肉","篮球",3) commit tran select * from lives
  为on时,结果集为空,因为运行是数据过大溢出出错,回滚整个事务。事务把死锁给整出来啦
  跟着做:打开两个查询窗口,把下面的语句,分别放入2个查询窗口,在5秒内运行2个事务模块。begin tran    update lives set play="羽毛球"   waitfor delay "0:0:5"     update dbo.Earth set Animal="老虎"  commit tranbegin tran    update Earth set Animal="老虎"    waitfor  delay "0:0:5" --等待5秒执行下面的语句   update lives set play="羽毛球" commit tran select * from lives select * from Earth
  为什么呢,下面我们看看锁,什么是锁。并发事务成败皆归于锁——锁定
  在多用户都用事务同时访问同一个数据资源的情况下,就会造成以下几种数据错误。更新丢失:多个用户同时对一个数据资源进行更新,必定会产生被覆盖的数据,造成数据读写异常。不可重复读:如果一个用户在一个事务中多次读取一条数据,而另外一个用户则同时更新啦这条数据,造成第一个用户多次读取数据不一致。脏读:第一个事务读取第二个事务正在更新的数据表,如果第二个事务还没有更新完成,那么第一个事务读取的数据将是一半为更新过的,一半还没更新过的数据,这样的数据毫无意义。幻读:第一个事务读取一个结果集后,第二个事务,对这个结果集经行增删操作,然而第一个事务中再次对这个结果集进行查询时,数据发现丢失或新增。
  然而锁定,就是为解决这些问题所生的,他的存在使得一个事务对他自己的数据块进行操作的时候,而另外一个事务则不能插足这些数据块。这就是所谓的锁定。
  锁定从数据库系统的角度大致可以分为6种:共享锁(S):还可以叫他读锁。可以并发读取数据,但不能修改数据。也就是说当数据资源上存在共享锁的时候,所有的事务都不能对这个资源进行修改,直到数据读取完成,共享锁释放。排它锁(X):还可以叫他独占锁、写锁。就是如果你对数据资源进行增删改操作时,不允许其它任何事务操作这块资源,直到排它锁被释放,防止同时对同一资源进行多重操作。更新锁(U):防止出现死锁的锁模式,两个事务对一个数据资源进行先读取在修改的情况下,使用共享锁和排它锁有时会出现死锁现象,而使用更新锁则可以避免死锁的出现。资源的更新锁一次只能分配给一个事务,如果需要对资源进行修改,更新锁会变成排他锁,否则变为共享锁。意向锁:SQL Server需要在层次结构中的底层资源上(如行,列)获取共享锁,排它锁,更新锁。例如表级放置了意向共享锁,就表示事务要对表的页或行上使用共享锁。在表的某一行上上放置意向锁,可以防止其它事务获取其它不兼容的的锁。意向锁可以提高性能,因为数据引擎不需要检测资源的每一列每一行,就能判断是否可以获取到该资源的兼容锁。意向锁包括三种类型:意向共享锁(IS),意向排他锁(IX),意向排他共享锁(SIX)。架构锁:防止修改表结构时,并发访问的锁。大容量更新锁:允许多个线程将大容量数据并发的插入到同一个表中,在加载的同时,不允许其它进程访问该表。
  这些锁之间的相互兼容性,也就是,是否可以同时存在。
  现有的授权模式
  请求的模式
  IS
  S
  U
  IX
  SIX
  X
  意向共享 (IS)
  是
  是
  是
  是
  是
  否
  共享 (S)
  是
  是
  是
  否
  否
  否
  更新 (U)
  是
  是
  否
  否
  否
  否
  意向排他 (IX)
  是
  否
  否
  是
  否
  否
  意向排他共享 (SIX)
  是
  否
  否
  否
  否
  否
  排他 (X)
  否
  否
  否
  否
  否
  否
  锁兼容性具体参见:http://msdn.microsoft.com/zh-cn/library/ms186396.aspx
  锁粒度和层次结构参见:http://msdn.microsoft.com/zh-cn/library/ms189849(v=sql.105).aspx死锁
  什么是死锁,为什么会产生死锁。我用 "事务把死锁给整出来啦" 标题下的两个事务产生的死锁来解释应该会更加生动形象点。
  例子是这样的:
  第一个事务(称为A):先更新lives表 --->>停顿5秒---->>更新earth表
  第二个事务(称为B):先更新earth表--->>停顿5秒---->>更新lives表
  先执行事务A----5秒之内---执行事务B,出现死锁现象。
  过程是这样子的:A更新lives表,请求lives的排他锁,成功。B更新earth表,请求earth的排他锁,成功。5秒过后A更新earth,请求earth的排它锁,由于B占用着earth的排它锁,等待。B更新lives,请求lives的排它锁,由于A占用着lives的排它锁,等待。
  这样相互等待对方释放资源,造成资源读写拥挤堵塞的情况,就被称为死锁现象,也叫做阻塞。而为什么会产生,上例就列举出来啦。
  然而数据库并没有出现无限等待的情况,是因为数据库搜索引擎会定期检测这种状况,一旦发现有情况,立马选择一个事务作为牺牲品。牺牲的事务,将会回滚数据。有点像两个人在过独木桥,两个无脑的人都走在啦独木桥中间,如果不落水,必定要有一个人给退回来。这种相互等待的过程,是一种耗时耗资源的现象,所以能避则避。
  哪个人会被退回来,作为牺牲品,这个我们是可以控制的。控制语法:set deadlock_priority  <级别>
  死锁处理的优先级别为 low
  read uncommitted隔离级别的例子: begin tran    set deadlock_priority low   update Earth set Animal="老虎"    waitfor  delay "0:0:5" --等待5秒执行下面的语句 rollback tran
  开另外一个查询窗口执行下面语句set tran isolation level read uncommitted select * from Earth  --读取的数据为正在修改的数据 ,脏读 waitfor  delay "0:0:5"  --5秒之后数据已经回滚 select * from Earth  --回滚之后的数据
  read committed隔离级别的例子: begin tran    update Earth set Animal="老虎"    waitfor  delay "0:0:10" --等待5秒执行下面的语句 rollback transet tran isolation level read committed select * from Earth ---获取不到老虎,不能脏读 update Earth set Animal="猴子1"   --可以修改 waitfor  delay "0:0:10"  --10秒之后上一个事务已经回滚 select * from Earth  --修改之后的数据,而不是猴子
  剩下的几个级别,不一一列举啦,自己理解吧。设置锁超时时间
  发生死锁的时候,数据库引擎会自动检测死锁,解决问题,然而这样子是很被动,只能在发生死锁后,等待处理。
  然而我们也可以主动出击,设置锁超时时间,一旦资源被锁定阻塞,超过设置的锁定时间,阻塞语句自动取消,释放资源,报1222错误。
  好东西一般都具有两面性,调优的同时,也有他的不足之处,那就是一旦超过时间,语句取消,释放资源,但是当前报错事务,不会回滚,会造成数据错误,你需要在程序中捕获1222错误,用程序处理当前事务的逻辑,使数据正确。--查看超时时间,默认为-1 select @@lock_timeout --设置超时时间 set lock_timeout 0 --为0时,即为一旦发现资源锁定,立即报错,不在等待,当前事务不回滚,设置时间需谨慎处理后事啊,你hold不住的。
  查看与杀死锁和进程--检测死锁 --如果发生死锁了,我们怎么去检测具体发生死锁的是哪条SQL语句或存储过程? --这时我们可以使用以下存储过程来检测,就可以查出引起死锁的进程和SQL语句。SQL Server自带的系统存储过程sp_who和sp_lock也可以用来查找阻塞和死锁, 但没有这里介绍的方法好用。   use master go create procedure sp_who_lock as begin declare @spid int,@bl int,  @intTransactionCountOnEntry  int,         @intRowcount    int,         @intCountProperties   int,         @intCounter    int   create table #tmp_lock_who (  id int identity(1,1),  spid smallint,  bl smallint)    IF @@ERROR<>0 RETURN @@ERROR    insert into #tmp_lock_who(spid,bl) select  0 ,blocked    from (select * from sysprocesses where  blocked>0 ) a     where not exists(select * from (select * from sysprocesses where  blocked>0 ) b     where a.blocked=spid)    union select spid,blocked from sysprocesses where  blocked>0   IF @@ERROR<>0 RETURN @@ERROR     -- 找到临时表的记录数  select  @intCountProperties = Count(*),@intCounter = 1  from #tmp_lock_who    IF @@ERROR<>0 RETURN @@ERROR     if @intCountProperties=0   select "现在没有阻塞和死锁信息" as message  -- 循环开始 while @intCounter <= @intCountProperties begin -- 取第一条记录   select  @spid = spid,@bl = bl   from #tmp_lock_who where Id = @intCounter   begin   if @spid =0              select "引起数据库死锁的是: "+ CAST(@bl AS VARCHAR(10)) + "进程号,其执行的SQL语法如下"  else             select "进程号SPID:"+ CAST(@spid AS VARCHAR(10))+ "被" + "进程号SPID:"+ CAST(@bl AS VARCHAR(10)) +"阻塞,其当前进程执行的SQL语法如下"  DBCC INPUTBUFFER (@bl )  end   -- 循环指针下移  set @intCounter = @intCounter + 1 end  drop table #tmp_lock_who  return 0 end    --杀死锁和进程 --如何去手动的杀死进程和锁?最简单的办法,重新启动服务。但是这里要介绍一个存储过程,通过显式的调用,可以杀死进程和锁。  use master go  if exists (select * from dbo.sysobjects where id = object_id(N"[dbo].[p_killspid]") and OBJECTPROPERTY(id, N"IsProcedure") = 1) drop procedure [dbo].[p_killspid] GO  create proc p_killspid @dbname varchar(200)    --要关闭进程的数据库名 as       declare @sql  nvarchar(500)       declare @spid nvarchar(20)      declare #tb cursor for         select spid=cast(spid as varchar(20)) from master..sysprocesses where dbid=db_id(@dbname)     open #tb     fetch next from #tb into @spid     while @@fetch_status=0     begin           exec("kill "+@spid)         fetch next from #tb into @spid     end       close #tb     deallocate #tb go  --用法   exec p_killspid  "newdbpy"   --查看锁信息 --如何查看系统中所有锁的详细信息?在企业管理管理器中,我们可以看到一些进程和锁的信息,这里介绍另外一种方法。 --查看锁信息 create table #t(req_spid int,obj_name sysname)  declare @s nvarchar(4000)     ,@rid int,@dbname sysname,@id int,@objname sysname  declare tb cursor for      select distinct req_spid,dbname=db_name(rsc_dbid),rsc_objid     from master..syslockinfo where rsc_type in(4,5) open tb fetch next from tb into @rid,@dbname,@id while @@fetch_status=0 begin     set @s="select @objname=name from ["+@dbname+"]..sysobjects where id=@id"     exec sp_executesql @s,N"@objname sysname out,@id int",@objname out,@id     insert into #t values(@rid,@objname)     fetch next from tb into @rid,@dbname,@id end close tb deallocate tb  select 进程id=a.req_spid     ,数据库=db_name(rsc_dbid)     ,类型=case rsc_type when 1 then "NULL 资源(未使用)"         when 2 then "数据库"         when 3 then "文件"         when 4 then "索引"         when 5 then "表"         when 6 then "页"         when 7 then "键"         when 8 then "扩展盘区"         when 9 then "RID(行 ID)"         when 10 then "应用程序"     end     ,对象id=rsc_objid     ,对象名=b.obj_name     ,rsc_indid  from master..syslockinfo a left join #t b on a.req_spid=b.req_spid  go drop table #t
  仔细阅读,希望能分享给你一点点东西,谢谢,over。

丁俊晖领衔,五名中国选手征战世锦赛文羊城晚报全媒体记者苏荇2023年斯诺克世锦赛13日在英国结束资格赛争夺,庞俊旭以10比5战胜徐思,周跃龙以5比10不敌斯莱塞。如此一来,范争一吴宜泽斯佳辉和庞俊旭四人跻身正赛,加C罗又开炮!向沙特豪门提无理要求,球迷嘲讽要你有啥用?(点击阅读可看更多857体育赛事资讯)关注世界足坛的球迷可能都知道,在今年的冬季转会窗,葡萄牙球星C罗以自由身加盟了沙特豪门利雅得胜利,开启了职业生涯的新篇章。如今三个多月过去了,斯诺克一哥丁俊晖带队四人首秀中国军团五人征战世锦赛北京时间4月13日凌晨,2023斯诺克世锦赛资格赛在英国谢菲尔德全部完成,决出了最终能够前往克鲁斯堡剧院参加正赛的32人名单。中国球员此次加上以种子身份参战的老将丁俊晖,共有5人拿花样滑冰全国冠军赛开赛4月12日,哈尔滨市冬季运动项目训练中心选手陈溪梓(左)邢珈宁在冰舞韵律舞比赛中,他们以68。44分的成绩位列第一名。新华社记者许雅楠摄当日,20222023赛季全国花样滑冰冠军赛切尔西被皇马完胜,代理主帅兰帕德赛后仍嘴硬文羊城晚报全媒体记者徐扬扬欧冠14决赛首回合4月13日迎来一场西甲与英超两大豪门的对决,结果皇家马德里在主场以2比0完虐切尔西,在两回合的较量中占得先机。我为球员们感到骄傲。我不认米兰主场掀翻意甲领头羊迈尼昂神扑屡建功十六年夙愿或得偿北京时间4月13日凌晨,欧冠四分之一决赛首回合,意甲内战,AC米兰主场迎战意甲领头羊那不勒斯。本场比赛,米兰排出吉鲁莱奥和迪亚斯的进攻组合,那不勒斯则因为头号射手奥斯梅恩缺阵,排出20!三大豪门一只脚踏入欧冠4强,曼城成夺冠最大热门今天凌晨,欧冠14决赛首回合继续进行皇马VS切尔西。第21分钟,本泽马破门第74分钟,阿森西奥进球。最终,皇马20战胜切尔西。AC米兰,主场迎战那不勒斯。第40分钟,本纳赛尔打进全曼联收购案进入第三轮报价阶段,英媒预测最终成交价会创纪录环球时报特约记者李佳寅美国商业体育网站体育人11日称,由于格雷泽家族正在寻求出售曼联俱乐部,曼联的收购案将进入第三轮报价阶段,潜在买家需要在4月结束前提供新报价。路透社称,英国亿万中超16强巡礼浙江队昔日升降机,迎来最好时代2022赛季,武汉三镇创造了中超处子赛季即夺冠的奇迹,以及中超顶级的球员配置,夺冠并不意外。时隔五年重返中超的浙江队,在投入不大的情况下,最终获得季军,更加难得。季军,也是浙江队史可以让孩子优秀,请不要逼他们完美北京多乐2099少儿编程你可以让孩子优秀,请不要逼他们完美。得第一,考100,要打屁股。最近又出了新鲜事。为什么会这样?因为现在很多家长都喝了毒鸡汤。你家孩子考个98分,人家孩子考个100分。你以为你俩之广交会开幕在即广州地区住宿预订量同比大涨保利洲际酒店将在早餐上展现早茶文化。通讯员供图中新网广州4月12日电(记者许青青)中国进出口商品交易会(简称广交会)将于4月15日开幕,世界各地参展人员齐聚广州,全城酒店迎来接待高
英雄联盟手游排位上分,这四个英雄就够了Hello大家好,本期为您推荐四个版本强势英雄,助您排位轻松上分,话不多说,直接上干货。小鱼人小鱼人作为人气非常高的法系刺客,集突进无敌和击飞等优点于一身,无论是对线能力还是边路游国外男子存万枚Bitcoin被当垃圾抛掉亿万富翁梦碎瞬间崩溃现今虚拟货币众多,其中比特币的呼声更是在近年来越来越高涨,让不少人纷纷投入其研究。不过就有位网友于美国网路论坛Reddit上发表自己曾购买比特币的经历,而因为自己妈妈一个无心的举动生活不能过度节俭!3种食物最佳归宿是垃圾桶,别滋养了癌症在中国有这样一句老话冰冻三尺,非一日之寒!其实,这句话也可以用在癌症身上,很多人都被癌症的表象迷惑,错误认为癌症就是突然出现的恶性病。但事实上,从细胞DNA发生变异,到癌细胞形成肿气虚血少耳鸣生!李东垣用上这2味中药,让气血风生水起今天继续说说耳鸣。很多人知道肾虚耳鸣,脾胃虚耳鸣,当然,气血虚,也是会导致耳鸣!中医将人看作一个整体,气血充盈,才能无死角的滋养身体,冬天手脚凉也是因为气血不足,手脚是身体末梢,气今日冬至适宜温补多晒太阳激发阳气图ICphoto今日冬至,南方地区有祭祖的习俗,北方地区有吃饺子的习俗。冬至过后白昼将逐日增长,气温仍会持续降低,即将进入最寒冷的时节。民间由此开始计算数九寒天,数九寒天究竟暗藏着年末寒潮来袭,数九时节,女性朋友第一要务就是增强身体阳气数九寒天,马上要进入一年中最冷的日子,怕冷的女性朋友怎样保护好自己呢?我们知道人生病一般跟外因和内因两方面都有关。内因有喜怒忧思悲恐惊。外因有寒暑燥湿风火,古人称之为六淫。淫就是过希芸带你看,秋冬护肤流程怎么走?秋冬天气干燥,很多姐妹脸上会起皮干燥。这时候很多姐妹会过于担心,往脸上胡乱涂抹一大堆护肤品,最后可能会有得不偿失的结果。导致皮肤屏障受损等一系列问题。其实只要做好一些简单的护肤流程3040岁的女性多穿毛衣,搭配牛仔裤小白裤,时髦又高级毛衣作为冬季保暖性最强的单品,自从出世以来就一直深受女性的喜爱,它能帮助大家安然的度过冬天。其实,毛衣也有很多种打开方式,这4组搭配非常值得你借鉴,简单又好看,如果你想避免穿出土俗动不动就闭口粉刺,可能是因为你我们皮脂腺分泌的油脂会从毛孔正常的代谢排出,但是当皮脂堵塞无法排除的时候,就会形成黄色的皮脂角栓堵塞在毛孔里,这样就形成了粉刺,那开放的粉刺角栓它外面的一头接触到了空气,被氧化变成导游曾劝诫最好不要接苗寨美女的私人物品,基本很难脱身由于我国地大物博,所以小伙伴们会趁着空闲的时间段走走看看。然而在节假日的时候,许多小伙伴会发现国内的旅游景点处于爆满的状态,于是一些小伙伴会到特色的地方是游玩。(此处已添加小程序,在泰国住酒店,别忘记在枕头底下放20泰铢,等你夜晚回来会有惊喜随着各国旅游业的发展,尤其是东南亚地区,当地的旅游服务深受海外游客的喜爱。在东南亚旅行,不仅能欣赏异国的独特风光,还能感悟当地的风俗和文化。(此处已添加小程序,请到今日头条客户端查