`
forchenyun
  • 浏览: 310067 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

使用Spring和Hibernate框架操作数据库水平分区

    博客分类:
  • Java
阅读更多

翻译了几年前的一篇文章,思想很不错。

http://www.jroller.com/kenwdelong/entry/horizontal_database_partitioning_with_spring

简介

       大约在一年以前,我决定水平扩展我们的数据库。在我们的数据库中我们拥有数百万的用户,我们期望我们的用户为我们的网站生成更多的内容,同时我们将收集更多的用户行为。我们已经被垂直扩展的策略搞得焦头烂额,我们越来越难于对硬件进行扩展,你只能同时扩展一两个硬件,并且当它挂掉时,所有的东西都崩溃掉了。因此,我们决定使用普通硬件设备对数据库进行水平扩展。

       一个专门从事Mysql数据库的可扩展性方面研究的顾问建议我们,基于用户进行水平分区:一个用户及其所有数据(人物概况,用户生成的内容等等)将存在某一个分区上。一个全局用户数据库(GLUD)将会是这组数据库的主键,GLUD将存储每个用户的主键和这个用户所在的那个分区的ID

       我们继续。我们最初的想法是为每个分区创建一个Hibernatesession factory。假设我们有两个用户数据库,user1user2.那么我们将有两个session factories,每个数据库对应一个。使用这些数据库的Service(例如ProfileService)将会为每个数据库创建一个实例。Profile1Service将关联到使用use1SessionFactoryprofile1Dao.对于N个分区的类似。调用该Service将触发一个Spring aop的拦截器,它将获取该用户的标识符,查询GLUD来决定该用户的数据存在哪个分区上,随后将会转发调用到正确的ProfileService实例上。

       我们实现了一个这种方式的原型,并且它运行良好。随后我们遇到了两个想法。第一个是Interface21's Mark Fisher的博客所介绍的AbstractRoutingDataSource。第二个是Hibernate shards项目。第一种方式我们只需要创建一个ProfileService,一个ProfileDao以及一个UserSessionFactory,并让datasource知道有多个用户数据库。Hibernate shards是一个项目,其运行原理和我们最初的那个想法类似,为每个数据库创建一个session factory实例。

       我们倾向于使用hibernate shards,这样就不用编写我们自己的分区系统。但是hibernate shards目前只发布了测试版。最好我们由于以下几个原因放弃使用hibernate shards:我们观察了数周,但是只有极少的人活跃在hibernate shards的社区。在我们核心基础设施使用如此新和不确定的项目使得我们没有安全感。第二,多session factories的策略本来就不具备扩展性:你需要为你的每个新加入的分区生成一个session factory。如果你变得像myspace那样成功,你将需要上百个session factory。根据文献所说的那样,多session factories是资源密集型应用(消耗大量的资源),这一点我们不会觉得舒服(这也是我们上面的想法的硬伤)。最后,我们查询了hibernate shards的文档,它对于如何与spring集成和配置的说明并不清楚,那么,springlocalSessionFactoryBean将无法工作。我不太喜欢深入spring的事务基础设施来创建一个ShardsSessionFactoryBean来合适地集成这种想法的事务管理。因此,我们决定采用routing-datasource的方法。

实现

我将带领你思考我们如何实现,已经它的优点和不足。首先是GLUD数据库。这个数据库包含了master_user表,他包含了在所有分区的所有用户的主键和邮箱地址。事实上,它包括了一个用户的所有唯一约束属性,也是数据库的唯一性约束可以应用的地方,但是在这里我们假设我们以email作为唯一约束。给定一个用户的email地址,master_user表可以用于定位用户表的主键。另外一个表示partition_map,它包括了一个用户主键的hash到一个分区id的映射。所以,如果你有一个用户的主键,那么就可以在partition_map中查找分区。我们所使用的hash函数是主键的最后三位数字,随后我们分配一千个虚拟分区。物理分区的数目可以是11000之间。例如,如果你只有两个物理分区,那么你可以映射分区000-499user database 1500-999user database 2(或者你可以采用奇偶数的方法)。现在的问题是,你有用户的主键和email地址,你能够知道用户数据的数据库的分区id

那么,谁来负责做分区定位的计算呢?我们编写了一个spring aop的拦截器来包装所有用于我们的分区数据库的services。拦截器可以使用GLUD数据库(通过中间的GludService)来确定路由到哪个分区。最后问题在于拦截器如何知道目前的操作关联到哪个用户。因此我们约定,每个方法的第一个参数可以识别用户:它应该是用户对象本身或者是用户主键或email。以上的这些将会帮助我们确定数据存在哪个分区。然而这是有漏洞的:在现有的分区系统中,使用分区数据库的service只能使用愚蠢的方法签名,它们不是类型安全的。下面是拦截器中方法的大致实现:

public Object selectExistingPartitionWithUser(ProceedingJoinPoint jp, LocatePreexistingUser annotation, User user) throws Throwable 
    {
        GludEntry gludEntry = getGludService().getGludEntryForExistingUser(user);
        int partitionNumber = gludEntry.getDatabasePartition();
        datasourceNumberCache.set(partitionNumber);
        Object returnValue = null;
        try
        {
            returnValue = jp.proceed();
        }
        finally
        {
            datasourceNumberCache.remove();
        }
        return returnValue;
    }

 

 

这里datasourceNumberCache是一个pubilc static final ThreadLocal<Integer>,它维护了本次操作所关联的用户所在的分区id。谁将读取这个ThreadLocal将在后文介绍。

 

 

我们使用了AspectJ切入点语言来描述我们的切入点(pointcut).这将使得我们可以为我们的拦截器使用类型安全的方法签名,正如你在上文看到的那样(没有Method对象或者Object对象这样的参数)。我们也发现许多不同类型的拦截也是必要的。上文我们看到了最简单的情况,寻找与用户关联的数据。但是当用户更新他的email(或者别的存在GLUD中的唯一性字段)?那么新建一个用户呢?某个操作需要广播到所有分区呢(计算上周某个用户创建的内容总数)?如果我们需要加载所有用户生成的内容来进行索引,批量加载呢?以上所有操作都要求在拦截器中编写不同的方法。如何使拦截器绑定不同的方法到不同的service呢?

对于这种情况我们使用了注解。你可以看到注解实例通过spring集成设施进入到方法签名之上。下面你就可以看到方法的切入点:

spring的官方文档和AspectJ的官网来完全理解这段代码的意思,当然它主要指绑定到所有以LocatePreexistingUser为注解的方法,和以User 对象作为第一参数的方法。argNames项是必要的,它能正确获得注解并传递User对象。我记得曾经比较糟糕的是当多于一个参数绑定到切入点上时,那很难运行正常,直到我偶然发现argNames参数。

@Around(value="@annotation(annotation) && args(user, ..)", argNames="annotation,user")

 你可以通过

 

使用注解很爽的地方是你可以将数据从被注解的方法传递到拦截器。例如,下面是如上注解的定义:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LocatePreexistingUser
{
    public UserIdentifier userIdentifier() default USER_OBJECT;
    public boolean userUpdate() default false;
}

 

 

 在这里, UserIdentifier是一个值为USER_OBJECTEMAILUSER_PK的枚举,如果你更新GLUD中具有唯一约束的字段,如email,你可以在你的ProfileService中使用如下注解:

 

@LocatePreexistingUser(userUpdate=true)
public void updateEmail(User user, String newEmail) 
{ ... }

 

 那么,拦截器将像下面那样:

if(annotation.userUpdate)
{
    // tell GLUD service to update its master_user record
}

 

这的确很nice。你可以传递信息到拦截器,它将告诉拦截器如何处理该方法的调用,并且注解指定了正确的方法定义。同样的,我认为这很nice

 

hibernate已经准备好发生sql到数据库了,它调用datasource获得一个连接。PartitionRoutingDataSourceThreadLocal中读取出分区id,然后返回指向该数据库的连接。它继承了SpringAbstractRoutingDataSource,其代码大致如下:

 

protected Object determineCurrentLookupKey()
    {
        Integer datasourceNumber = 
DatasourceSwitchingAspect.datasourceNumberCache.get();
        return datasourceNumber;
    }

 

Spring的配置中配置了两个普通的数据源(以两个分区为例子),user1DataSourceuser2DataSource。它们是标准的数据源指向物理的数据库(使用jboss连接池,通过jndi查找)。那么,我们供给Hibernate session factory的数据源配置应该是这样的:

<bean id="userDataSource" class="PartitionRoutingDataSource">
        <property name="targetDataSources">
            <map key-type="java.lang.Integer">
                <entry key="1" value-ref="user1DataSource"/>
                <entry key="2" value-ref="user2DataSource"/>
            </map>
        </property>
    </bean>

 

 

 Spring创建的session factory应该是这样的:

 

 

<bean id="userSessionFactory" 
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="userDataSource"/>
        etc 
    </bean>

 

 

这里没有什么特别的。为了能够使用多个数据库分区,简单地在应用服务器上配置连接池,把数据源加入到spring中,然后添加引用到PartitionRoutingDataSource中。完成了!最美好的事情是你可以增加任意的数据库分区而不需要创建数目众多的session factory

 

 

还有一些其他的事情需要我们去担忧。当hibernate获取到一个连接的时候,你想确认在你开启一个spring事务之前你已经设置好了分区id。换句话说,你需要确认     DatasourceSwitchingAspect 中的order属性值比事务拦截器中的低。这里,DatasourceSwitchingAspectorder设置为1,事务拦截器的order属性设置为2

<aop:config>
        <aop:pointcut id="profileServicePointcut" expression="execution(* *..ProfileService.*(..))"/>
        <aop:advisor advice-ref="userTxAdvice" pointcut-ref="profileServicePointcut" order="2"/>        
    </aop:config>

 

 

userTxAdvice是我们以前在spring使用的事务通知(advice)。事务管理器是一个普通的HibernateTransactionManager

 

 

Mark Fisher's的博客中其中一条评论中指出,配置的顺序可能会引起hibernate二级缓存的混乱,除非id分开存储。我们考虑了几种办法来解决它,如分派一个范围给每个数据库。但是DBA们比较倾向于一个high-low的表存在GLUD中,因此,我们考虑去实现它。Hibernate有一个high-low的主键生成策略,但是它会认为你插入数据的表在同一个数据库中,但是我们的主键都存在GLUD数据库中。为了不编写我们自己的high-low主键生成策略,我们需要编写一个hibernate主键生成器的包装类。该包装类简单的钩取(grabs)GLUDsession factory来发送主键生成器。GLUD session来自于ApplicationContextAware单例对象,它维持了spring应用上下文的引用,然后在必要的时候钩取GLUD session。由于hibernate在一个我们所不知道的地方创建了主键生成器,因此gludSessionFactory不能通过spring进行依赖注入。

public class UserDbIdGenerator implements IdentifierGenerator, Configurable
{
    private MultipleHiLoPerTableGenerator generator; 
    
    public ProfileIdGenerator()
    {
        generator = new MultipleHiLoPerTableGenerator();        
    }

    public Serializable generate(SessionImplementor profileSession, Object entity) throws HibernateException
    {
        SessionFactory gludSessionFactory = getGludSessionFactory();
        Session gludSession = gludSessionFactory.openSession();
        Transaction txn = gludSession.beginTransaction();
        
        // Pass through to the wrapped id generator
        Long key = (Long) generator.generate((SessionImplementor) gludSession, entity);
        
        txn.commit();
        gludSession.close();
        return key;
    }

    protected SessionFactory getGludSessionFactory()
    {
        SessionFactory sessionFactory =
 SpringContextSingleton.getInstance().getBean("gludSessionFactory");
        return sessionFactory;
    }

    public void configure(Type type, Properties props, Dialect dialect) throws MappingException
    {
        generator.configure(type, props, dialect);
    }
    
}

 

  在我们的hibernate映射文件中,对象应该使用这个主键生成器类:

 

<class name="Foo" table="foo">
        <id name="id" column="id">
            <generator class="UserDbIdGenerator">
                <param name="primary_key_value">foo</param>
                <param name="max_lo">5000</param>
            </generator>
        </id>
    </class>

 

 

问题

总体来说,这个分区模型运行得很不错。它运行在生产环境中并且性能表现良好。然而如果你要将分区应用到你的应用程序中,我对你有一些建议。

二级缓存

我们的hibernate二级缓存存在相当数量的小故障。简言之,一个hibernate session factory(我们绝对相信它是连到一个数据库),它与多个数据库一起工作就会充满了危险。对于对象缓存,一般没有问题,因为我们的id是唯一的,并且FOO#1只能在一个分区上被发现。然而,查询缓存是一个噩梦。

比方说,你发出查询:“给我从上周开始所有的blog实体”,首先,查询在分区1上面运行,结果他们被缓存在查询缓存。接下来拦截器尝试在分区2上面运行查询,但是由于之前有缓存,因此之前的缓存结果被返回。有些对象不在分区2中,但是它们在缓存中,因此,你会受到欺骗:你的所有返回的对象都来自于分区1,而没有任何对象来自于之后的分区。

一般说来,你能操作属于分区N   session,但是使用存储在分区M上的对象(因为你在二级缓存中找到了它们),如果你要去数据库中更新这些对象,那么你可能会出错,因为你找到了错误的数据库。

如果你采用shards的方式,使用单一session factory,每个数据库一个二级缓存,那么这些问题将不复存在。

JTA

分区用户数据库和GLUD数据库的系统是一个单元:你不希望事务在一个数据库中提交了,但在另一个数据库中没能提交。如果你想把它们包装在一个事务中,你可能需要使用JTA。我不确信JTA在这种情况下可以运行良好。想象这样的情景:你开启了一个JTA事务,然后使用hibernate session facory对数据库分区1进行操作。如果你进行了更新,hibernate维持该sql直到事务完成。现在,在同一个JTA事务,你连接到分区2.,我们可以看到2个不好的事情发生:1hibernate说:在这个会话中,我已经有一个分区1的连接了。2)当事务提交时,这个session维持了两个分区的sql,它知道发送到哪个分区吗?

       Hibernate的官方文档中有只言片语引导我们:可以配置hibernate会一个一个地发出sql并且释放连接,不过我没有查证这个。我曾经试图在我的应用程序中创建一个JTA事务管理器,但是不能让它运行(springJtaTransactionManager拒绝去查找Jboss中的事务管理器)。我花了大概一小时,然后我才知道大概问题所在(classpath中重复的jta.jar)。

       此外,在一个单session factory per database分区风格中,JTA应该会正常运行。

测试

当涉及到分区的时候,测试变得很痛苦(并且它不仅针对于我们的分区风格)。我们为分区数据库形成了两种测试方案:一种是ROIT(regular old integration tests),测试dao面向单一分区。随后我们有分区集成测试,使用GLUD和分区。你至少应该编写测试类来测试拦截器,路由datasource和所有的xml配置,来确定它们运行恰当。但是你需要创造力来为这些测试类创建应用程序上下文,来避免手动初始化对象或者编写重复的配置文件。使用DBUnits是一个小小的挑战,因为你通常需要插入或更新GLUD和分区中的数据(或者更多)。

共享对象

最后,更痛苦的一点是,对象在多个分区之间共享是一团糟。假设我们的Blog对象有一个Category属性,它是多对多关系,如果你希望Category对象和Blog在同一数据库中,它们需要存在每一个分区数据库中,因此,category(id=1,name=’java’)将出现在两个分区数据库中,当它们被加载到二级缓存中,它们将出现冲突,你可以关闭缓存来避免这种情况,关闭乐观锁,将它们放到另外的数据库中(GLUD?),不过,如果你有多个session factory(并且二级缓存是分开的)这样的情况不会如此糟糕。

概述

我希望你发现这篇有趣的描述。如果你即将尝试进行分区,你可以考虑使用上文的方法。但是一定要知道以上的问题:二级缓存(可能还有JTA)将不会很好的工作,每个数据库对应一个session factory的策略将消耗更多的资源(并且将降低应用程序的启动速度),但或许可以解决这两个问题。

我想如果我重新开始,我愿意回答多session factory 的方式,或者我再看看hibernate shards怎么样了,那或许要更好。我想当上面的情况改变时我可能会改变我之前的实现方法。不过,通过这些技术实现分区是一件多有趣和有挑战的事情啊。希望大家分享你自己的经验。

6
2
分享到:
评论

相关推荐

    248ssm-mysql-jsp 校园外卖管理系统.zip(可运行源码+数据库文件+文档)

    此次设计的外卖订单管理系统的登录角色一共分为四个,消费者、商户、管理员以及骑手。设计的系统为前端网页和后台管理系统。 消费者主要有以模块的需求:(1)购物车,(2)订单中心,(3)收藏夹,(4)收货地址,(5)个人信息管理,(6)站内咨询浏览,(7)在线留言。 商户的用例包括了一下几个模块设计:(1)商品管理,(2)库存管理,(3)订单管理,(4)销量统计,(5)收藏统计(6)销售额统计,(7)订单量统计 管理员系统结构中的功能设计比较多,分为三个大类分别是基础信息、业务功能和统计信息,基础信息主要是对消费者、商户以及骑手进行信息的维护工作,维护网站内的资讯信息等。业务功能是对网站内的商家进行分类管理,对于商品以及库存进行管理,对订单进行管理以及留言管理。统计信息包括对于商品销量的统计、订单走势图的分析等。 此次使用了java web技术线进行网页端的开发,开发工具采用idea.工具,数据库采用了MySQL进行设计开发,服务器采用了Tomcat服务器技术。该网站系统能够将学校周围商家的外卖产品在网站上向用户进行展示

    MyBatis 动态 SQL 示例

    MyBatis 是一个持久层框架,它允许用户在 XML 文件中编写动态 SQL 语句。MyBatis 的动态 SQL 功能非常强大,它允许开发者根据运行时的条件动态地生成 SQL 语句。这使得 MyBatis 能够灵活地处理各种复杂的查询需求。 MyBatis 动态 SQL 通过使用 <if>、<choose>、<when>、<otherwise>、<trim>、<set> 等标签来实现。附件中是一些常见的动态 SQL 标签及其用法,通过组合使用这些标签,可以编写出非常灵活和强大的 SQL 语句,以适应不同的查询和更新需求

    华为数据治理方法论,包括:数据治理框架、数据治理组织架构、数据治理度量评估体系以及华为数据治理案例分享

    华为数据治理方法论,包括:数据治理框架、数据治理组织架构、数据治理度量评估体系以及华为数据治理案例分享。 1目的 1 2面向的读者 2 3数据治理框架 3 3.1数据治理框架 3 3.2数据治理模块域 3 3.3数据治理各模块域之间的关系 4 4数据治理组织架构 7 4.1数据治理组织架构框架 7 4.2数据治理组织职责 7 5数据治理度量评估体系 10 5.1数据治理实施方法论 10 5.2数据治理度量维度 11 5.3数据治理度量评分规则 11 6华为数据治理案例 13 6.1华为数据治理思考 13 6.2华为数据治理实践 14 6.3华为数据治理效果 15 7新冠疫情数据治理思考 16 8DAYU 方法论产品落地 17

    毕业设计:基于SSM的mysql-羽毛球馆管理系统(源码 + 数据库 + 说明文档)

    毕业设计:基于SSM的mysql_羽毛球馆管理系统(源码 + 数据库 + 说明文档) 第二章 需求分析 3 2.1需求调研 3 2.2可行性分析 3 2.2.1技术的可行性 3 2.2.2经济的可行性 3 2.2.3操作可行性 3 2.2.4法律的可行性 4 2.3开发工具及技术 4 2.3.1网站开发环境 4 2.3.2 PHP语言简介 4 2.3.3 JavaScript技术 4 2.3.4 MySQL数据库 4 2.3.5 PHPstorm平台 5 2.3.6 工作环境 5 第三章 网站系统设计 5 3.1系统功能研究 5 3.1.1系统功能需求 5 3.2功能模块分析 6 3.3 设计的基本思想 7 3.4 性能要求 8 3.4.1 网站的安全性 8 3.4.2 数据的完整性 8 3.4.3界面要求 8 第四章 网站功能实现 8 4.1系统实现 8 4.1.1 管理员登录界面 9 4.1.2 后台用户管理 9 4.1.3 球场管理 10 4.1.4 物资管理 11 4.1.5 预定管理 12 4.2数据库的分析与设计 13 4.2.1数据库的概念结构设计 13 4.2.2数据库

    搜索链接相见欢友情链接系统ASPX版 v1.0-xjlinkaspxv1.0.rar

    相见欢友情链接系统ASPX版 v1.0_xjlinkaspxv1.0.rar,这是一个专为计算机专业人士设计的JSP源码资料包。这款系统是基于ASPX技术构建的,旨在为用户提供一个高效、便捷的友情链接管理平台。它能够帮助网站管理员轻松地管理和控制网站上的友情链接,提高网站的互动性和用户体验。这个资料包包含了完整的源代码和相关的文档,使得用户可以轻松地进行二次开发和定制。它提供了丰富的功能,包括友情链接的添加、删除、修改、审核等,以及链接分类、排序、搜索等功能。此外,它还支持多种链接样式和模板,可以根据用户的喜好和需求进行个性化设置。相见欢友情链接系统ASPX版 v1.0不仅功能强大,而且操作简便。它的界面设计简洁明了,用户可以快速上手,无需专业的计算机知识。同时,它还具有良好的兼容性和稳定性,可以在不同的环境和平台上运行,满足各种规模的网站建设需求。总的来说,相见欢友情链接系统ASPX版 v1.0是一个实用、高效、易用的友情链接管理工具。无论是对计算机专业人士,还是对普通用户,都是一个很好的选择。通过使用这个系统,可以大大提高网站管理的工作效率,提升网站的专业性和用户体验。重新回答||

    node-v8.12.0-linux-armv7l.tar.gz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    最新UI站长引流工具箱 带后台+安装说明.rar

    最新UI站长引流工具箱 带后台+安装说明.rar最新UI站长引流工具箱 带后台+安装说明.rar最新UI站长引流工具箱 带后台+安装说明.rar

    上市公司-企业要素密集度数据集(2000-2022年).txt

    数据存放网盘,txt文件内包含下载链接及提取码,永久有效。 样例数据及详细介绍参见文章:https://blog.csdn.net/li514006030/article/details/138294086

    node-v6.12.3.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    乐趣大型购物系统 v1.1(jsp+servlet+mysql)130223.rar

    这个文件名为"乐趣大型购物系统 v1.1(jsp+servlet+mysql)130223.rar",是一个计算机专业的JSP源码资料包。它包含了一个使用JSP、Servlet和MySQL技术实现的大型购物系统的源代码。该系统的设计理念是以用户为中心,提供便捷、高效的购物体验。系统采用了模块化的设计方法,将不同的功能划分为不同的模块,提高了代码的可读性和可维护性。同时,系统使用了MVC设计模式,将业务逻辑、数据访问和用户界面进行了分离,使得系统具有更好的扩展性和灵活性。在数据库设计方面,系统使用了MySQL数据库,对商品信息、用户信息、订单信息等进行了合理的设计和存储。通过使用数据库的事务管理机制,保证了数据的一致性和完整性。在用户界面设计上,系统提供了简洁明了的页面布局,使用户能够快速上手并完成购物操作。系统还提供了搜索功能,方便用户快速找到自己需要的商品。此外,系统还提供了购物车功能,用户可以将选购的商品加入购物车,并在合适的时间进行结算。总的来说,这个"乐趣大型购物系统 v1.1(jsp+servlet+mysql)130223.rar"是一个功能齐全、设计合理的购物系统,对于学

    node-v8.15.0-x64.msi

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    记账管理系统的设计与实现

    近年来由于生活节奏的加快,好像每个人都被很多难以启齿的问题困惑,然而关于随意消费是大多数人头疼的问题,没有任何计划和筹备的情况下随意消费,导致现实生活中我们所称为的“月光族”。 当你逐渐了解自己的财务状况,就可以学着做简单的收支规划。大部分月光族的根源其实是缺乏规划,想买什么的时候就买了。并不是说规划不能随意买东西,规划的价值在于让你使用资金的效率最高。无论你用金钱换取的必需品,满足感或者快乐,都可以通过规划获得比较高的效率。 本记账系统是一个基于国内外电子商务网站的发展现状,采用B2C(Business to Consumers)模式开发的电子商务平台,它的价值所在对于那些随意消费性的人群能起到一个很大的警示作用,而且系统扩张性很强,能根据客户的不同需求进行快速改进。该系统采用B/S三层结构,服务器是Tomcat同时运用JSp技术进行动态页面设计,后台数据库是Oracle。

    Linux 0.01版,经典中的经典

    大名鼎鼎的Linux源码,最经典的第一版

    node-v12.5.0-sunos-x64.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    node-v8.3.0-win-x64.zip

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    node-v0.10.48.tar.gz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    matlab实现遗传算法matlab源码.zip

    优秀源码设计,详情请查看资源内容

    基于matlab实现此函数使用标量衍射限制模型为宽场显微镜生成点扩展函数

    基于matlab实现此函数使用标量衍射限制模型为宽场显微镜生成点扩展函数(Stokseth 参考下面的 [1] 和 [3]).rar

    Qt 5.15.13静态库

    Qt 5.15.13静态库

Global site tag (gtag.js) - Google Analytics