First your core logic of checking if a record is success or not is incorrect. . As it will always create junk `Order_Integration__c` records for each `upsertOrderList`. 

As per your code lets say if first result in `lsr` is not successful, so the code will go ahead iterate on all `upsertOrderList` and create `Order_Integration__c` records for all `upsertOrderList` in else condition.

For checking if a upsert has passed successfully or not check [this][1]

As explained here order of the result and the upsert records will be always same.

	for (Integer index = 0; index < lsr.size(); index++) {
        error = '';
        errorRecCount = 0;
		if(lsr[index].isSuccess() && lsr[index].isCreated()){
			if (lsr[index].isCreated()) {
				successRecCount++;
			}
			orderIdSet.add(lsr[index].getId());
		} else {
			hasErrors = true;
			for (Database.Error err : lsr[index].getErrors()) {
				errorRecCount++;
				Order_Integration__c ordInt = new Order_Integration__c();
				ordInt.Id = upsertOrderList[index].Order_Integration_Ids__c;
				error += err.getStatusCode() + ': ' + err.getMessage();
				error += 'Fields: ' + err.getFields();
				error += '\n';
				ordInt.Status_Description__c = error;
				ordIntegrationList.add(ordInt);
				if (err.getStatusCode() == system.StatusCode.REQUIRED_FIELD_MISSING) {
					if (errorMap.containsKey(err.getMessage())) {
						count = errorMap.get(err.getMessage());
						errorMap.put(err.getMessage(), ++count);
					} else {
						errorMap.put(err.getMessage(), 1);
					}
				}
			}
		}
	}

  [1]: https://salesforce.stackexchange.com/questions/189138/database-upsert-how-to-find-which-records-have-inserted-updated-based-on-exter

For the exception , I believe you are initializing `error = ''` and `errorRecCount` in wrong place. It should be inside the for loop like shown above.

The issue is as you have initialized `String` at top and concatenating it always, so there might be scenario in your batch where that `String` is growing beyond 12MB of heap limit.